2005-04-16 15:20:36 -07:00
/*
* drivers / mtd / nand / sharpsl . c
*
* Copyright ( C ) 2004 Richard Purdie
*
* Based on Sharp ' s NAND driver sharp_sl . c
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
*/
# include <linux/genhd.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/nand.h>
# include <linux/mtd/nand_ecc.h>
# include <linux/mtd/partitions.h>
# include <linux/interrupt.h>
2008-10-16 12:08:14 +04:00
# include <linux/platform_device.h>
2005-04-16 15:20:36 -07:00
# include <asm/io.h>
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2005-04-16 15:20:36 -07:00
# include <asm/mach-types.h>
static void __iomem * sharpsl_io_base ;
/* register offset */
# define ECCLPLB sharpsl_io_base+0x00 /* line parity 7 - 0 bit */
# define ECCLPUB sharpsl_io_base+0x04 /* line parity 15 - 8 bit */
# define ECCCP sharpsl_io_base+0x08 /* column parity 5 - 0 bit */
# define ECCCNTR sharpsl_io_base+0x0C /* ECC byte counter */
# define ECCCLRR sharpsl_io_base+0x10 /* cleare ECC */
# define FLASHIO sharpsl_io_base+0x14 /* Flash I/O */
# define FLASHCTL sharpsl_io_base+0x18 /* Flash Control */
/* Flash control bit */
# define FLRYBY (1 << 5)
# define FLCE1 (1 << 4)
# define FLWP (1 << 3)
# define FLALE (1 << 2)
# define FLCLE (1 << 1)
# define FLCE0 (1 << 0)
/*
* MTD structure for SharpSL
*/
static struct mtd_info * sharpsl_mtd = NULL ;
/*
* Define partitions for flash device
*/
# define DEFAULT_NUM_PARTITIONS 3
static int nr_partitions ;
static struct mtd_partition sharpsl_nand_default_partition_info [ ] = {
{
2006-05-13 18:07:53 +01:00
. name = " System Area " ,
. offset = 0 ,
. size = 7 * 1024 * 1024 ,
} ,
2005-04-16 15:20:36 -07:00
{
2006-05-13 18:07:53 +01:00
. name = " Root Filesystem " ,
. offset = 7 * 1024 * 1024 ,
. size = 30 * 1024 * 1024 ,
} ,
2005-04-16 15:20:36 -07:00
{
2006-05-13 18:07:53 +01:00
. name = " Home Filesystem " ,
. offset = MTDPART_OFS_APPEND ,
. size = MTDPART_SIZ_FULL ,
} ,
2005-04-16 15:20:36 -07:00
} ;
2005-11-07 11:15:49 +00:00
/*
2005-04-16 15:20:36 -07:00
* hardware specific access to control - lines
2006-05-23 23:25:53 +02:00
* ctrl :
2006-07-15 13:05:24 +01:00
* NAND_CNE : bit 0 - > ! bit 0 & 4
2006-05-23 23:25:53 +02:00
* NAND_CLE : bit 1 - > bit 1
* NAND_ALE : bit 2 - > bit 2
*
2005-04-16 15:20:36 -07:00
*/
2006-05-23 23:25:53 +02:00
static void sharpsl_nand_hwcontrol ( struct mtd_info * mtd , int cmd ,
unsigned int ctrl )
2005-04-16 15:20:36 -07:00
{
2006-05-23 23:25:53 +02:00
struct nand_chip * chip = mtd - > priv ;
if ( ctrl & NAND_CTRL_CHANGE ) {
unsigned char bits = ctrl & 0x07 ;
bits | = ( ctrl & 0x01 ) < < 4 ;
2006-07-15 13:05:24 +01:00
bits ^ = 0x11 ;
writeb ( ( readb ( FLASHCTL ) & ~ 0x17 ) | bits , FLASHCTL ) ;
2005-04-16 15:20:36 -07:00
}
2006-05-23 23:25:53 +02:00
if ( cmd ! = NAND_CMD_NONE )
writeb ( cmd , chip - > IO_ADDR_W ) ;
2005-04-16 15:20:36 -07:00
}
static uint8_t scan_ff_pattern [ ] = { 0xff , 0xff } ;
static struct nand_bbt_descr sharpsl_bbt = {
. options = 0 ,
. offs = 4 ,
. len = 2 ,
. pattern = scan_ff_pattern
} ;
2005-11-03 11:36:45 +00:00
static struct nand_bbt_descr sharpsl_akita_bbt = {
. options = 0 ,
. offs = 4 ,
. len = 1 ,
. pattern = scan_ff_pattern
} ;
2006-05-27 22:16:10 +02:00
static struct nand_ecclayout akita_oobinfo = {
2005-11-03 11:36:45 +00:00
. eccbytes = 24 ,
. eccpos = {
2006-05-13 18:07:53 +01:00
0x5 , 0x1 , 0x2 , 0x3 , 0x6 , 0x7 , 0x15 , 0x11 ,
0x12 , 0x13 , 0x16 , 0x17 , 0x25 , 0x21 , 0x22 , 0x23 ,
0x26 , 0x27 , 0x35 , 0x31 , 0x32 , 0x33 , 0x36 , 0x37 } ,
. oobfree = { { 0x08 , 0x09 } }
2005-11-03 11:36:45 +00:00
} ;
2006-05-13 18:07:53 +01:00
static int sharpsl_nand_dev_ready ( struct mtd_info * mtd )
2005-04-16 15:20:36 -07:00
{
return ! ( ( readb ( FLASHCTL ) & FLRYBY ) = = 0 ) ;
}
2006-05-13 18:07:53 +01:00
static void sharpsl_nand_enable_hwecc ( struct mtd_info * mtd , int mode )
2005-04-16 15:20:36 -07:00
{
2006-05-13 18:07:53 +01:00
writeb ( 0 , ECCCLRR ) ;
2005-04-16 15:20:36 -07:00
}
2006-05-13 18:07:53 +01:00
static int sharpsl_nand_calculate_ecc ( struct mtd_info * mtd , const u_char * dat , u_char * ecc_code )
2005-04-16 15:20:36 -07:00
{
ecc_code [ 0 ] = ~ readb ( ECCLPUB ) ;
ecc_code [ 1 ] = ~ readb ( ECCLPLB ) ;
ecc_code [ 2 ] = ( ~ readb ( ECCCP ) < < 2 ) | 0x03 ;
return readb ( ECCCNTR ) ! = 0 ;
}
# ifdef CONFIG_MTD_PARTITIONS
const char * part_probes [ ] = { " cmdlinepart " , NULL } ;
# endif
/*
* Main initialization routine
*/
2008-10-16 12:08:14 +04:00
static int __devinit sharpsl_nand_probe ( struct platform_device * pdev )
2005-04-16 15:20:36 -07:00
{
struct nand_chip * this ;
2006-05-13 18:07:53 +01:00
struct mtd_partition * sharpsl_partition_info ;
2008-10-16 12:08:14 +04:00
struct resource * r ;
2005-04-16 15:20:36 -07:00
int err = 0 ;
/* Allocate memory for MTD device structure and private data */
2006-05-13 18:07:53 +01:00
sharpsl_mtd = kmalloc ( sizeof ( struct mtd_info ) + sizeof ( struct nand_chip ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! sharpsl_mtd ) {
2006-05-13 18:07:53 +01:00
printk ( " Unable to allocate SharpSL NAND MTD device structure. \n " ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
2005-11-07 11:15:49 +00:00
2008-10-16 12:08:14 +04:00
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! r ) {
dev_err ( & pdev - > dev , " no io memory resource defined! \n " ) ;
err = - ENODEV ;
goto err_get_res ;
}
2008-02-03 17:22:34 +02:00
/* map physical address */
2008-10-16 12:08:14 +04:00
sharpsl_io_base = ioremap ( r - > start , resource_size ( r ) ) ;
2006-05-13 18:07:53 +01:00
if ( ! sharpsl_io_base ) {
2005-04-16 15:20:36 -07:00
printk ( " ioremap to access Sharp SL NAND chip failed \n " ) ;
kfree ( sharpsl_mtd ) ;
return - EIO ;
}
2005-11-07 11:15:49 +00:00
2005-04-16 15:20:36 -07:00
/* Get pointer to private data */
2006-05-13 18:07:53 +01:00
this = ( struct nand_chip * ) ( & sharpsl_mtd [ 1 ] ) ;
2005-04-16 15:20:36 -07:00
/* Initialize structures */
2006-05-13 18:07:53 +01:00
memset ( sharpsl_mtd , 0 , sizeof ( struct mtd_info ) ) ;
memset ( this , 0 , sizeof ( struct nand_chip ) ) ;
2005-04-16 15:20:36 -07:00
/* Link the private data with the MTD structure */
sharpsl_mtd - > priv = this ;
2006-05-14 01:20:46 +01:00
sharpsl_mtd - > owner = THIS_MODULE ;
2005-04-16 15:20:36 -07:00
/*
* PXA initialize
*/
writeb ( readb ( FLASHCTL ) | FLWP , FLASHCTL ) ;
/* Set address of NAND IO lines */
this - > IO_ADDR_R = FLASHIO ;
this - > IO_ADDR_W = FLASHIO ;
/* Set address of hardware control function */
2006-05-23 23:25:53 +02:00
this - > cmd_ctrl = sharpsl_nand_hwcontrol ;
2005-04-16 15:20:36 -07:00
this - > dev_ready = sharpsl_nand_dev_ready ;
/* 15 us command delay time */
this - > chip_delay = 15 ;
/* set eccmode using hardware ECC */
2006-05-23 12:00:46 +02:00
this - > ecc . mode = NAND_ECC_HW ;
this - > ecc . size = 256 ;
this - > ecc . bytes = 3 ;
2005-11-07 11:15:49 +00:00
this - > badblock_pattern = & sharpsl_bbt ;
2005-11-03 11:36:45 +00:00
if ( machine_is_akita ( ) | | machine_is_borzoi ( ) ) {
this - > badblock_pattern = & sharpsl_akita_bbt ;
2006-05-27 22:16:10 +02:00
this - > ecc . layout = & akita_oobinfo ;
2005-11-03 11:36:45 +00:00
}
2006-05-23 12:00:46 +02:00
this - > ecc . hwctl = sharpsl_nand_enable_hwecc ;
this - > ecc . calculate = sharpsl_nand_calculate_ecc ;
this - > ecc . correct = nand_correct_data ;
2005-04-16 15:20:36 -07:00
/* Scan to find existence of the device */
2006-05-13 18:07:53 +01:00
err = nand_scan ( sharpsl_mtd , 1 ) ;
2005-04-16 15:20:36 -07:00
if ( err ) {
iounmap ( sharpsl_io_base ) ;
kfree ( sharpsl_mtd ) ;
return err ;
}
/* Register the partitions */
sharpsl_mtd - > name = " sharpsl-nand " ;
2006-05-13 18:07:53 +01:00
nr_partitions = parse_mtd_partitions ( sharpsl_mtd , part_probes , & sharpsl_partition_info , 0 ) ;
2005-11-07 11:15:49 +00:00
2005-04-16 15:20:36 -07:00
if ( nr_partitions < = 0 ) {
nr_partitions = DEFAULT_NUM_PARTITIONS ;
sharpsl_partition_info = sharpsl_nand_default_partition_info ;
if ( machine_is_poodle ( ) ) {
2006-05-13 18:07:53 +01:00
sharpsl_partition_info [ 1 ] . size = 22 * 1024 * 1024 ;
2005-04-16 15:20:36 -07:00
} else if ( machine_is_corgi ( ) | | machine_is_shepherd ( ) ) {
2006-05-13 18:07:53 +01:00
sharpsl_partition_info [ 1 ] . size = 25 * 1024 * 1024 ;
2005-04-16 15:20:36 -07:00
} else if ( machine_is_husky ( ) ) {
2006-05-13 18:07:53 +01:00
sharpsl_partition_info [ 1 ] . size = 53 * 1024 * 1024 ;
2005-09-16 19:27:31 -07:00
} else if ( machine_is_spitz ( ) ) {
2006-05-13 18:07:53 +01:00
sharpsl_partition_info [ 1 ] . size = 5 * 1024 * 1024 ;
2005-09-16 19:27:31 -07:00
} else if ( machine_is_akita ( ) ) {
2006-05-13 18:07:53 +01:00
sharpsl_partition_info [ 1 ] . size = 58 * 1024 * 1024 ;
2005-09-16 19:27:31 -07:00
} else if ( machine_is_borzoi ( ) ) {
2006-05-13 18:07:53 +01:00
sharpsl_partition_info [ 1 ] . size = 32 * 1024 * 1024 ;
2005-09-16 19:27:31 -07:00
}
2005-04-16 15:20:36 -07:00
}
add_mtd_partitions ( sharpsl_mtd , sharpsl_partition_info , nr_partitions ) ;
/* Return happy */
return 0 ;
2006-05-13 18:07:53 +01:00
2008-10-16 12:08:14 +04:00
err_get_res :
kfree ( sharpsl_mtd ) ;
return err ;
}
2005-04-16 15:20:36 -07:00
/*
* Clean up routine
*/
2008-10-16 12:08:14 +04:00
static int __devexit sharpsl_nand_remove ( struct platform_device * pdev )
2005-04-16 15:20:36 -07:00
{
/* Release resources, unregister device */
nand_release ( sharpsl_mtd ) ;
iounmap ( sharpsl_io_base ) ;
/* Free the MTD device structure */
kfree ( sharpsl_mtd ) ;
2008-10-16 12:08:14 +04:00
return 0 ;
}
static struct platform_driver sharpsl_nand_driver = {
. driver = {
. name = " sharpsl-nand " ,
. owner = THIS_MODULE ,
} ,
. probe = sharpsl_nand_probe ,
. remove = __devexit_p ( sharpsl_nand_remove ) ,
} ;
static struct resource sharpsl_nand_resources [ ] = {
{
. start = 0x0C000000 ,
. end = 0x0C000FFF ,
. flags = IORESOURCE_MEM ,
} ,
} ;
static struct platform_device sharpsl_nand_device = {
. name = " sharpsl-nand " ,
. id = - 1 ,
. resource = sharpsl_nand_resources ,
. num_resources = ARRAY_SIZE ( sharpsl_nand_resources ) ,
} ;
static int __init sharpsl_nand_init ( void )
{
platform_device_register ( & sharpsl_nand_device ) ;
return platform_driver_register ( & sharpsl_nand_driver ) ;
2005-04-16 15:20:36 -07:00
}
2008-10-16 12:08:14 +04:00
module_init ( sharpsl_nand_init ) ;
2006-05-13 18:07:53 +01:00
2008-10-16 12:08:14 +04:00
static void __exit sharpsl_nand_exit ( void )
{
platform_driver_unregister ( & sharpsl_nand_driver ) ;
platform_device_unregister ( & sharpsl_nand_device ) ;
}
module_exit ( sharpsl_nand_exit ) ;
2005-04-16 15:20:36 -07:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Richard Purdie <rpurdie@rpsys.net> " ) ;
MODULE_DESCRIPTION ( " Device specific logic for NAND flash on Sharp SL-C7xx Series " ) ;