2005-04-17 02:20:36 +04:00
/*
* drivers / mtd / nand . c
*
* Overview :
* This is the generic MTD driver for NAND flash devices . It should be
* capable of working with almost all NAND chips currently available .
* Basic support for AG - AND chips is provided .
2005-11-07 14:15:49 +03:00
*
2005-04-17 02:20:36 +04:00
* Additional technical information is available on
* http : //www.linux-mtd.infradead.org/tech/nand.html
2005-11-07 14:15:49 +03:00
*
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 2000 Steven J . Hill ( sjhill @ realitydiluted . com )
2006-05-24 14:07:37 +04:00
* 2002 - 2006 Thomas Gleixner ( tglx @ linutronix . de )
2005-04-17 02:20:36 +04:00
*
2006-05-24 14:07:37 +04:00
* Credits :
2005-11-07 14:15:49 +03:00
* David Woodhouse for adding multichip support
*
2005-04-17 02:20:36 +04:00
* Aleph One Ltd . and Toby Churchill Ltd . for supporting the
* rework for 2 K page size chips
*
2006-05-24 14:07:37 +04:00
* TODO :
2005-04-17 02:20:36 +04:00
* Enable cached programming for 2 k page size chips
* Check , if mtd - > ecctype should be set to MTD_ECC_HW
* if we have HW ecc support .
* The AG - AND chips have nice features for speed improvement ,
* which are not supported yet . Read / program 4 pages in one go .
*
* 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 .
*
*/
2006-05-14 04:20:46 +04:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <linux/delay.h>
# include <linux/errno.h>
2006-05-23 13:54:38 +04:00
# include <linux/err.h>
2005-04-17 02:20:36 +04:00
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/types.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/nand.h>
# include <linux/mtd/nand_ecc.h>
# include <linux/mtd/compatmac.h>
# include <linux/interrupt.h>
# include <linux/bitops.h>
2006-03-31 14:31:14 +04:00
# include <linux/leds.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# ifdef CONFIG_MTD_PARTITIONS
# include <linux/mtd/partitions.h>
# endif
/* Define default oob placement schemes for large and small page devices */
2006-05-28 00:16:10 +04:00
static struct nand_ecclayout nand_oob_8 = {
2005-04-17 02:20:36 +04:00
. eccbytes = 3 ,
. eccpos = { 0 , 1 , 2 } ,
2006-05-28 00:16:10 +04:00
. oobfree = {
{ . offset = 3 ,
. length = 2 } ,
{ . offset = 6 ,
. length = 2 } }
2005-04-17 02:20:36 +04:00
} ;
2006-05-28 00:16:10 +04:00
static struct nand_ecclayout nand_oob_16 = {
2005-04-17 02:20:36 +04:00
. eccbytes = 6 ,
. eccpos = { 0 , 1 , 2 , 3 , 6 , 7 } ,
2006-05-28 00:16:10 +04:00
. oobfree = {
{ . offset = 8 ,
. length = 8 } }
2005-04-17 02:20:36 +04:00
} ;
2006-05-28 00:16:10 +04:00
static struct nand_ecclayout nand_oob_64 = {
2005-04-17 02:20:36 +04:00
. eccbytes = 24 ,
. eccpos = {
2006-05-13 21:07:53 +04:00
40 , 41 , 42 , 43 , 44 , 45 , 46 , 47 ,
48 , 49 , 50 , 51 , 52 , 53 , 54 , 55 ,
56 , 57 , 58 , 59 , 60 , 61 , 62 , 63 } ,
2006-05-28 00:16:10 +04:00
. oobfree = {
{ . offset = 2 ,
. length = 38 } }
2005-04-17 02:20:36 +04:00
} ;
2006-05-24 14:07:37 +04:00
static int nand_get_device ( struct nand_chip * chip , struct mtd_info * mtd ,
2006-05-23 13:50:56 +04:00
int new_state ) ;
2005-04-17 02:20:36 +04:00
2006-05-29 05:26:58 +04:00
static int nand_do_write_oob ( struct mtd_info * mtd , loff_t to ,
struct mtd_oob_ops * ops ) ;
2006-05-24 01:48:57 +04:00
/*
* For devices which display every fart in the system on a seperate LED . Is
* compiled away when LED support is disabled .
*/
DEFINE_LED_TRIGGER ( nand_led_trigger ) ;
2005-04-17 02:20:36 +04:00
/**
* nand_release_device - [ GENERIC ] release chip
* @ mtd : MTD device structure
2005-11-07 14:15:49 +03:00
*
* Deselect , release chip lock and wake up anyone waiting on the device
2005-04-17 02:20:36 +04:00
*/
2006-05-13 21:07:53 +04:00
static void nand_release_device ( struct mtd_info * mtd )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
/* De-select the NAND device */
2006-05-24 14:07:37 +04:00
chip - > select_chip ( mtd , - 1 ) ;
2005-05-31 23:39:20 +04:00
2006-05-23 13:37:03 +04:00
/* Release the controller and the chip */
2006-05-24 14:07:37 +04:00
spin_lock ( & chip - > controller - > lock ) ;
chip - > controller - > active = NULL ;
chip - > state = FL_READY ;
wake_up ( & chip - > controller - > wq ) ;
spin_unlock ( & chip - > controller - > lock ) ;
2005-04-17 02:20:36 +04:00
}
/**
* nand_read_byte - [ DEFAULT ] read one byte from the chip
* @ mtd : MTD device structure
*
* Default read function for 8 bit buswith
*/
2006-05-23 13:52:35 +04:00
static uint8_t nand_read_byte ( struct mtd_info * mtd )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
return readb ( chip - > IO_ADDR_R ) ;
2005-04-17 02:20:36 +04:00
}
/**
* nand_read_byte16 - [ DEFAULT ] read one byte endianess aware from the chip
* @ mtd : MTD device structure
*
2005-11-07 14:15:49 +03:00
* Default read function for 16 bit buswith with
2005-04-17 02:20:36 +04:00
* endianess conversion
*/
2006-05-23 13:52:35 +04:00
static uint8_t nand_read_byte16 ( struct mtd_info * mtd )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
return ( uint8_t ) cpu_to_le16 ( readw ( chip - > IO_ADDR_R ) ) ;
2005-04-17 02:20:36 +04:00
}
/**
* nand_read_word - [ DEFAULT ] read one word from the chip
* @ mtd : MTD device structure
*
2005-11-07 14:15:49 +03:00
* Default read function for 16 bit buswith without
2005-04-17 02:20:36 +04:00
* endianess conversion
*/
static u16 nand_read_word ( struct mtd_info * mtd )
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
return readw ( chip - > IO_ADDR_R ) ;
2005-04-17 02:20:36 +04:00
}
/**
* nand_select_chip - [ DEFAULT ] control CE line
* @ mtd : MTD device structure
2006-06-29 08:48:27 +04:00
* @ chipnr : chipnumber to select , - 1 for deselect
2005-04-17 02:20:36 +04:00
*
* Default select function for 1 chip devices .
*/
2006-05-24 14:07:37 +04:00
static void nand_select_chip ( struct mtd_info * mtd , int chipnr )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
switch ( chipnr ) {
2005-04-17 02:20:36 +04:00
case - 1 :
2006-05-24 14:07:37 +04:00
chip - > cmd_ctrl ( mtd , NAND_CMD_NONE , 0 | NAND_CTRL_CHANGE ) ;
2005-04-17 02:20:36 +04:00
break ;
case 0 :
break ;
default :
BUG ( ) ;
}
}
/**
* nand_write_buf - [ DEFAULT ] write buffer to chip
* @ mtd : MTD device structure
* @ buf : data buffer
* @ len : number of bytes to write
*
* Default write function for 8 bit buswith
*/
2006-05-23 13:52:35 +04:00
static void nand_write_buf ( struct mtd_info * mtd , const uint8_t * buf , int len )
2005-04-17 02:20:36 +04:00
{
int i ;
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < len ; i + + )
2006-05-24 14:07:37 +04:00
writeb ( buf [ i ] , chip - > IO_ADDR_W ) ;
2005-04-17 02:20:36 +04:00
}
/**
2005-11-07 14:15:49 +03:00
* nand_read_buf - [ DEFAULT ] read chip data into buffer
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
* @ buf : buffer to store date
* @ len : number of bytes to read
*
* Default read function for 8 bit buswith
*/
2006-05-23 13:52:35 +04:00
static void nand_read_buf ( struct mtd_info * mtd , uint8_t * buf , int len )
2005-04-17 02:20:36 +04:00
{
int i ;
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < len ; i + + )
2006-05-24 14:07:37 +04:00
buf [ i ] = readb ( chip - > IO_ADDR_R ) ;
2005-04-17 02:20:36 +04:00
}
/**
2005-11-07 14:15:49 +03:00
* nand_verify_buf - [ DEFAULT ] Verify chip data against buffer
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
* @ buf : buffer containing the data to compare
* @ len : number of bytes to compare
*
* Default verify function for 8 bit buswith
*/
2006-05-23 13:52:35 +04:00
static int nand_verify_buf ( struct mtd_info * mtd , const uint8_t * buf , int len )
2005-04-17 02:20:36 +04:00
{
int i ;
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < len ; i + + )
2006-05-24 14:07:37 +04:00
if ( buf [ i ] ! = readb ( chip - > IO_ADDR_R ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
return 0 ;
}
/**
* nand_write_buf16 - [ DEFAULT ] write buffer to chip
* @ mtd : MTD device structure
* @ buf : data buffer
* @ len : number of bytes to write
*
* Default write function for 16 bit buswith
*/
2006-05-23 13:52:35 +04:00
static void nand_write_buf16 ( struct mtd_info * mtd , const uint8_t * buf , int len )
2005-04-17 02:20:36 +04:00
{
int i ;
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
u16 * p = ( u16 * ) buf ;
len > > = 1 ;
2005-11-07 14:15:49 +03:00
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < len ; i + + )
2006-05-24 14:07:37 +04:00
writew ( p [ i ] , chip - > IO_ADDR_W ) ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
}
/**
2005-11-07 14:15:49 +03:00
* nand_read_buf16 - [ DEFAULT ] read chip data into buffer
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
* @ buf : buffer to store date
* @ len : number of bytes to read
*
* Default read function for 16 bit buswith
*/
2006-05-23 13:52:35 +04:00
static void nand_read_buf16 ( struct mtd_info * mtd , uint8_t * buf , int len )
2005-04-17 02:20:36 +04:00
{
int i ;
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
u16 * p = ( u16 * ) buf ;
len > > = 1 ;
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < len ; i + + )
2006-05-24 14:07:37 +04:00
p [ i ] = readw ( chip - > IO_ADDR_R ) ;
2005-04-17 02:20:36 +04:00
}
/**
2005-11-07 14:15:49 +03:00
* nand_verify_buf16 - [ DEFAULT ] Verify chip data against buffer
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
* @ buf : buffer containing the data to compare
* @ len : number of bytes to compare
*
* Default verify function for 16 bit buswith
*/
2006-05-23 13:52:35 +04:00
static int nand_verify_buf16 ( struct mtd_info * mtd , const uint8_t * buf , int len )
2005-04-17 02:20:36 +04:00
{
int i ;
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
u16 * p = ( u16 * ) buf ;
len > > = 1 ;
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < len ; i + + )
2006-05-24 14:07:37 +04:00
if ( p [ i ] ! = readw ( chip - > IO_ADDR_R ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
return 0 ;
}
/**
* nand_block_bad - [ DEFAULT ] Read bad block marker from the chip
* @ mtd : MTD device structure
* @ ofs : offset from device start
* @ getchip : 0 , if the chip is already selected
*
2005-11-07 14:15:49 +03:00
* Check , if the block is bad .
2005-04-17 02:20:36 +04:00
*/
static int nand_block_bad ( struct mtd_info * mtd , loff_t ofs , int getchip )
{
int page , chipnr , res = 0 ;
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
u16 bad ;
if ( getchip ) {
2006-05-24 14:07:37 +04:00
page = ( int ) ( ofs > > chip - > page_shift ) ;
chipnr = ( int ) ( ofs > > chip - > chip_shift ) ;
2005-04-17 02:20:36 +04:00
2006-05-24 14:07:37 +04:00
nand_get_device ( chip , mtd , FL_READING ) ;
2005-04-17 02:20:36 +04:00
/* Select the NAND device */
2006-05-24 14:07:37 +04:00
chip - > select_chip ( mtd , chipnr ) ;
2005-11-07 14:15:49 +03:00
} else
2006-05-13 21:07:53 +04:00
page = ( int ) ofs ;
2005-04-17 02:20:36 +04:00
2006-05-24 14:07:37 +04:00
if ( chip - > options & NAND_BUSWIDTH_16 ) {
chip - > cmdfunc ( mtd , NAND_CMD_READOOB , chip - > badblockpos & 0xFE ,
page & chip - > pagemask ) ;
bad = cpu_to_le16 ( chip - > read_word ( mtd ) ) ;
if ( chip - > badblockpos & 0x1 )
2005-11-02 19:54:46 +03:00
bad > > = 8 ;
2005-04-17 02:20:36 +04:00
if ( ( bad & 0xFF ) ! = 0xff )
res = 1 ;
} else {
2006-05-24 14:07:37 +04:00
chip - > cmdfunc ( mtd , NAND_CMD_READOOB , chip - > badblockpos ,
page & chip - > pagemask ) ;
if ( chip - > read_byte ( mtd ) ! = 0xff )
2005-04-17 02:20:36 +04:00
res = 1 ;
}
2005-11-07 14:15:49 +03:00
2006-05-24 14:07:37 +04:00
if ( getchip )
2005-04-17 02:20:36 +04:00
nand_release_device ( mtd ) ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
return res ;
}
/**
* nand_default_block_markbad - [ DEFAULT ] mark a block bad
* @ mtd : MTD device structure
* @ ofs : offset from device start
*
* This is the default implementation , which can be overridden by
* a hardware specific driver .
*/
static int nand_default_block_markbad ( struct mtd_info * mtd , loff_t ofs )
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2006-05-23 13:52:35 +04:00
uint8_t buf [ 2 ] = { 0 , 0 } ;
2006-05-30 02:37:34 +04:00
int block , ret ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Get block number */
2006-05-24 14:07:37 +04:00
block = ( ( int ) ofs ) > > chip - > bbt_erase_shift ;
if ( chip - > bbt )
chip - > bbt [ block > > 2 ] | = 0x01 < < ( ( block & 0x03 ) < < 1 ) ;
2005-04-17 02:20:36 +04:00
/* Do we have a flash based bad block table ? */
2006-05-24 14:07:37 +04:00
if ( chip - > options & NAND_USE_FLASH_BBT )
2006-05-30 02:37:34 +04:00
ret = nand_update_bbt ( mtd , ofs ) ;
else {
/* We write two bytes, so we dont have to mess with 16 bit
* access
*/
ofs + = mtd - > oobsize ;
chip - > ops . len = 2 ;
chip - > ops . datbuf = NULL ;
chip - > ops . oobbuf = buf ;
chip - > ops . ooboffs = chip - > badblockpos & ~ 0x01 ;
ret = nand_do_write_oob ( mtd , ofs , & chip - > ops ) ;
}
if ( ! ret )
mtd - > ecc_stats . badblocks + + ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:49 +03:00
/**
2005-04-17 02:20:36 +04:00
* nand_check_wp - [ GENERIC ] check if the chip is write protected
* @ mtd : MTD device structure
2005-11-07 14:15:49 +03:00
* Check , if the device is write protected
2005-04-17 02:20:36 +04:00
*
2005-11-07 14:15:49 +03:00
* The function expects , that the device is already selected
2005-04-17 02:20:36 +04:00
*/
2006-05-13 21:07:53 +04:00
static int nand_check_wp ( struct mtd_info * mtd )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
/* Check the WP bit */
2006-05-24 14:07:37 +04:00
chip - > cmdfunc ( mtd , NAND_CMD_STATUS , - 1 , - 1 ) ;
return ( chip - > read_byte ( mtd ) & NAND_STATUS_WP ) ? 0 : 1 ;
2005-04-17 02:20:36 +04:00
}
/**
* nand_block_checkbad - [ GENERIC ] Check if a block is marked bad
* @ mtd : MTD device structure
* @ ofs : offset from device start
* @ getchip : 0 , if the chip is already selected
* @ allowbbt : 1 , if its allowed to access the bbt area
*
* Check , if the block is bad . Either by reading the bad block table or
* calling of the scan function .
*/
2006-05-23 13:50:56 +04:00
static int nand_block_checkbad ( struct mtd_info * mtd , loff_t ofs , int getchip ,
int allowbbt )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-11-07 14:15:49 +03:00
2006-05-24 14:07:37 +04:00
if ( ! chip - > bbt )
return chip - > block_bad ( mtd , ofs , getchip ) ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Return info from the table */
2006-05-13 21:07:53 +04:00
return nand_isbad_bbt ( mtd , ofs , allowbbt ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:49 +03:00
/*
2005-02-23 00:56:49 +03:00
* Wait for the ready pin , after a command
* The timeout is catched later .
*/
2006-09-25 20:05:24 +04:00
void nand_wait_ready ( struct mtd_info * mtd )
2005-02-23 00:56:49 +03:00
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2006-05-13 21:07:53 +04:00
unsigned long timeo = jiffies + 2 ;
2005-02-23 00:56:49 +03:00
2006-03-31 14:31:14 +04:00
led_trigger_event ( nand_led_trigger , LED_FULL ) ;
2005-02-23 00:56:49 +03:00
/* wait until command is processed or timeout occures */
do {
2006-05-24 14:07:37 +04:00
if ( chip - > dev_ready ( mtd ) )
2006-03-31 14:31:14 +04:00
break ;
2005-09-07 02:16:27 +04:00
touch_softlockup_watchdog ( ) ;
2005-11-07 14:15:49 +03:00
} while ( time_before ( jiffies , timeo ) ) ;
2006-03-31 14:31:14 +04:00
led_trigger_event ( nand_led_trigger , LED_OFF ) ;
2005-02-23 00:56:49 +03:00
}
2006-09-25 20:05:24 +04:00
EXPORT_SYMBOL_GPL ( nand_wait_ready ) ;
2005-02-23 00:56:49 +03:00
2005-04-17 02:20:36 +04:00
/**
* nand_command - [ DEFAULT ] Send command to NAND device
* @ mtd : MTD device structure
* @ command : the command to be sent
* @ column : the column address for this command , - 1 if none
* @ page_addr : the page address for this command , - 1 if none
*
* Send command to NAND device . This function is used for small page
* devices ( 256 / 512 Bytes per page )
*/
2006-05-24 01:25:53 +04:00
static void nand_command ( struct mtd_info * mtd , unsigned int command ,
int column , int page_addr )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
register struct nand_chip * chip = mtd - > priv ;
2006-05-24 01:25:53 +04:00
int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE ;
2005-04-17 02:20:36 +04:00
/*
* Write out the command to the device .
*/
if ( command = = NAND_CMD_SEQIN ) {
int readcmd ;
2006-05-23 01:18:05 +04:00
if ( column > = mtd - > writesize ) {
2005-04-17 02:20:36 +04:00
/* OOB area */
2006-05-23 01:18:05 +04:00
column - = mtd - > writesize ;
2005-04-17 02:20:36 +04:00
readcmd = NAND_CMD_READOOB ;
} else if ( column < 256 ) {
/* First 256 bytes --> READ0 */
readcmd = NAND_CMD_READ0 ;
} else {
column - = 256 ;
readcmd = NAND_CMD_READ1 ;
}
2006-05-24 14:07:37 +04:00
chip - > cmd_ctrl ( mtd , readcmd , ctrl ) ;
2006-05-24 01:25:53 +04:00
ctrl & = ~ NAND_CTRL_CHANGE ;
2005-04-17 02:20:36 +04:00
}
2006-05-24 14:07:37 +04:00
chip - > cmd_ctrl ( mtd , command , ctrl ) ;
2005-04-17 02:20:36 +04:00
2006-05-24 01:25:53 +04:00
/*
* Address cycle , when necessary
*/
ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE ;
/* Serially input address */
if ( column ! = - 1 ) {
/* Adjust columns for 16 bit buswidth */
2006-05-24 14:07:37 +04:00
if ( chip - > options & NAND_BUSWIDTH_16 )
2006-05-24 01:25:53 +04:00
column > > = 1 ;
2006-05-24 14:07:37 +04:00
chip - > cmd_ctrl ( mtd , column , ctrl ) ;
2006-05-24 01:25:53 +04:00
ctrl & = ~ NAND_CTRL_CHANGE ;
}
if ( page_addr ! = - 1 ) {
2006-05-24 14:07:37 +04:00
chip - > cmd_ctrl ( mtd , page_addr , ctrl ) ;
2006-05-24 01:25:53 +04:00
ctrl & = ~ NAND_CTRL_CHANGE ;
2006-05-24 14:07:37 +04:00
chip - > cmd_ctrl ( mtd , page_addr > > 8 , ctrl ) ;
2006-05-24 01:25:53 +04:00
/* One more address cycle for devices > 32MiB */
2006-05-24 14:07:37 +04:00
if ( chip - > chipsize > ( 32 < < 20 ) )
chip - > cmd_ctrl ( mtd , page_addr > > 16 , ctrl ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-24 14:07:37 +04:00
chip - > cmd_ctrl ( mtd , NAND_CMD_NONE , NAND_NCE | NAND_CTRL_CHANGE ) ;
2005-11-07 14:15:49 +03:00
/*
* program and erase have their own busy handlers
2005-04-17 02:20:36 +04:00
* status and sequential in needs no delay
2006-05-13 21:07:53 +04:00
*/
2005-04-17 02:20:36 +04:00
switch ( command ) {
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
case NAND_CMD_PAGEPROG :
case NAND_CMD_ERASE1 :
case NAND_CMD_ERASE2 :
case NAND_CMD_SEQIN :
case NAND_CMD_STATUS :
return ;
case NAND_CMD_RESET :
2006-05-24 14:07:37 +04:00
if ( chip - > dev_ready )
2005-04-17 02:20:36 +04:00
break ;
2006-05-24 14:07:37 +04:00
udelay ( chip - > chip_delay ) ;
chip - > cmd_ctrl ( mtd , NAND_CMD_STATUS ,
2006-05-24 01:25:53 +04:00
NAND_CTRL_CLE | NAND_CTRL_CHANGE ) ;
2006-05-25 00:57:09 +04:00
chip - > cmd_ctrl ( mtd ,
NAND_CMD_NONE , NAND_NCE | NAND_CTRL_CHANGE ) ;
2006-05-24 14:07:37 +04:00
while ( ! ( chip - > read_byte ( mtd ) & NAND_STATUS_READY ) ) ;
2005-04-17 02:20:36 +04:00
return ;
2006-05-13 21:07:53 +04:00
/* This applies to read commands */
2005-04-17 02:20:36 +04:00
default :
2005-11-07 14:15:49 +03:00
/*
2005-04-17 02:20:36 +04:00
* If we don ' t have access to the busy pin , we apply the given
* command delay
2006-05-13 21:07:53 +04:00
*/
2006-05-24 14:07:37 +04:00
if ( ! chip - > dev_ready ) {
udelay ( chip - > chip_delay ) ;
2005-04-17 02:20:36 +04:00
return ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
}
/* Apply this short delay always to ensure that we do wait tWB in
* any case on any machine . */
2006-05-13 21:07:53 +04:00
ndelay ( 100 ) ;
2005-02-23 00:56:49 +03:00
nand_wait_ready ( mtd ) ;
2005-04-17 02:20:36 +04:00
}
/**
* nand_command_lp - [ DEFAULT ] Send command to NAND large page device
* @ mtd : MTD device structure
* @ command : the command to be sent
* @ column : the column address for this command , - 1 if none
* @ page_addr : the page address for this command , - 1 if none
*
2006-05-24 01:25:53 +04:00
* Send command to NAND device . This is the version for the new large page
* devices We dont have the separate regions as we have in the small page
* devices . We must emulate NAND_CMD_READOOB to keep the code compatible .
2005-04-17 02:20:36 +04:00
*/
2006-05-24 01:25:53 +04:00
static void nand_command_lp ( struct mtd_info * mtd , unsigned int command ,
int column , int page_addr )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
register struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
/* Emulate NAND_CMD_READOOB */
if ( command = = NAND_CMD_READOOB ) {
2006-05-23 01:18:05 +04:00
column + = mtd - > writesize ;
2005-04-17 02:20:36 +04:00
command = NAND_CMD_READ0 ;
}
2005-11-07 14:15:49 +03:00
2006-05-24 01:25:53 +04:00
/* Command latch cycle */
2006-05-24 14:07:37 +04:00
chip - > cmd_ctrl ( mtd , command & 0xff ,
2006-05-24 01:25:53 +04:00
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE ) ;
2005-04-17 02:20:36 +04:00
if ( column ! = - 1 | | page_addr ! = - 1 ) {
2006-05-24 01:25:53 +04:00
int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE ;
2005-04-17 02:20:36 +04:00
/* Serially input address */
if ( column ! = - 1 ) {
/* Adjust columns for 16 bit buswidth */
2006-05-24 14:07:37 +04:00
if ( chip - > options & NAND_BUSWIDTH_16 )
2005-04-17 02:20:36 +04:00
column > > = 1 ;
2006-05-24 14:07:37 +04:00
chip - > cmd_ctrl ( mtd , column , ctrl ) ;
2006-05-24 01:25:53 +04:00
ctrl & = ~ NAND_CTRL_CHANGE ;
2006-05-24 14:07:37 +04:00
chip - > cmd_ctrl ( mtd , column > > 8 , ctrl ) ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
if ( page_addr ! = - 1 ) {
2006-05-24 14:07:37 +04:00
chip - > cmd_ctrl ( mtd , page_addr , ctrl ) ;
chip - > cmd_ctrl ( mtd , page_addr > > 8 ,
2006-05-24 01:25:53 +04:00
NAND_NCE | NAND_ALE ) ;
2005-04-17 02:20:36 +04:00
/* One more address cycle for devices > 128MiB */
2006-05-24 14:07:37 +04:00
if ( chip - > chipsize > ( 128 < < 20 ) )
chip - > cmd_ctrl ( mtd , page_addr > > 16 ,
2006-05-24 01:25:53 +04:00
NAND_NCE | NAND_ALE ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-05-24 14:07:37 +04:00
chip - > cmd_ctrl ( mtd , NAND_CMD_NONE , NAND_NCE | NAND_CTRL_CHANGE ) ;
2005-11-07 14:15:49 +03:00
/*
* program and erase have their own busy handlers
2005-01-17 21:35:25 +03:00
* status , sequential in , and deplete1 need no delay
*/
2005-04-17 02:20:36 +04:00
switch ( command ) {
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
case NAND_CMD_CACHEDPROG :
case NAND_CMD_PAGEPROG :
case NAND_CMD_ERASE1 :
case NAND_CMD_ERASE2 :
case NAND_CMD_SEQIN :
2006-06-20 22:05:05 +04:00
case NAND_CMD_RNDIN :
2005-04-17 02:20:36 +04:00
case NAND_CMD_STATUS :
2005-01-17 21:35:25 +03:00
case NAND_CMD_DEPLETE1 :
2005-04-17 02:20:36 +04:00
return ;
2006-05-13 21:07:53 +04:00
/*
* read error status commands require only a short delay
*/
2005-01-17 21:35:25 +03:00
case NAND_CMD_STATUS_ERROR :
case NAND_CMD_STATUS_ERROR0 :
case NAND_CMD_STATUS_ERROR1 :
case NAND_CMD_STATUS_ERROR2 :
case NAND_CMD_STATUS_ERROR3 :
2006-05-24 14:07:37 +04:00
udelay ( chip - > chip_delay ) ;
2005-01-17 21:35:25 +03:00
return ;
2005-04-17 02:20:36 +04:00
case NAND_CMD_RESET :
2006-05-24 14:07:37 +04:00
if ( chip - > dev_ready )
2005-04-17 02:20:36 +04:00
break ;
2006-05-24 14:07:37 +04:00
udelay ( chip - > chip_delay ) ;
2006-05-25 00:57:09 +04:00
chip - > cmd_ctrl ( mtd , NAND_CMD_STATUS ,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE ) ;
chip - > cmd_ctrl ( mtd , NAND_CMD_NONE ,
NAND_NCE | NAND_CTRL_CHANGE ) ;
2006-05-24 14:07:37 +04:00
while ( ! ( chip - > read_byte ( mtd ) & NAND_STATUS_READY ) ) ;
2005-04-17 02:20:36 +04:00
return ;
2006-06-20 22:05:05 +04:00
case NAND_CMD_RNDOUT :
/* No ready / busy check necessary */
chip - > cmd_ctrl ( mtd , NAND_CMD_RNDOUTSTART ,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE ) ;
chip - > cmd_ctrl ( mtd , NAND_CMD_NONE ,
NAND_NCE | NAND_CTRL_CHANGE ) ;
return ;
2005-04-17 02:20:36 +04:00
case NAND_CMD_READ0 :
2006-05-25 00:57:09 +04:00
chip - > cmd_ctrl ( mtd , NAND_CMD_READSTART ,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE ) ;
chip - > cmd_ctrl ( mtd , NAND_CMD_NONE ,
NAND_NCE | NAND_CTRL_CHANGE ) ;
2005-11-07 14:15:49 +03:00
2006-05-13 21:07:53 +04:00
/* This applies to read commands */
2005-04-17 02:20:36 +04:00
default :
2005-11-07 14:15:49 +03:00
/*
2005-04-17 02:20:36 +04:00
* If we don ' t have access to the busy pin , we apply the given
* command delay
2006-05-13 21:07:53 +04:00
*/
2006-05-24 14:07:37 +04:00
if ( ! chip - > dev_ready ) {
udelay ( chip - > chip_delay ) ;
2005-04-17 02:20:36 +04:00
return ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
}
2005-02-23 00:56:49 +03:00
2005-04-17 02:20:36 +04:00
/* Apply this short delay always to ensure that we do wait tWB in
* any case on any machine . */
2006-05-13 21:07:53 +04:00
ndelay ( 100 ) ;
2005-02-23 00:56:49 +03:00
nand_wait_ready ( mtd ) ;
2005-04-17 02:20:36 +04:00
}
/**
* nand_get_device - [ GENERIC ] Get chip for selected access
2006-06-29 08:48:27 +04:00
* @ chip : the nand chip descriptor
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
2005-11-07 14:15:49 +03:00
* @ new_state : the state which is requested
2005-04-17 02:20:36 +04:00
*
* Get the device and lock it for exclusive access
*/
2006-05-23 13:50:56 +04:00
static int
2006-05-24 14:07:37 +04:00
nand_get_device ( struct nand_chip * chip , struct mtd_info * mtd , int new_state )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
spinlock_t * lock = & chip - > controller - > lock ;
wait_queue_head_t * wq = & chip - > controller - > wq ;
2006-05-13 21:07:53 +04:00
DECLARE_WAITQUEUE ( wait , current ) ;
retry :
2005-05-31 23:39:20 +04:00
spin_lock ( lock ) ;
2005-04-17 02:20:36 +04:00
/* Hardware controller shared among independend devices */
2006-05-23 13:37:03 +04:00
/* Hardware controller shared among independend devices */
2006-05-24 14:07:37 +04:00
if ( ! chip - > controller - > active )
chip - > controller - > active = chip ;
2006-05-23 13:37:03 +04:00
2006-05-24 14:07:37 +04:00
if ( chip - > controller - > active = = chip & & chip - > state = = FL_READY ) {
chip - > state = new_state ;
2005-05-31 23:39:20 +04:00
spin_unlock ( lock ) ;
2005-09-15 17:58:53 +04:00
return 0 ;
}
if ( new_state = = FL_PM_SUSPENDED ) {
spin_unlock ( lock ) ;
2006-05-24 14:07:37 +04:00
return ( chip - > state = = FL_PM_SUSPENDED ) ? 0 : - EAGAIN ;
2005-05-31 23:39:20 +04:00
}
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
add_wait_queue ( wq , & wait ) ;
spin_unlock ( lock ) ;
schedule ( ) ;
remove_wait_queue ( wq , & wait ) ;
2005-04-17 02:20:36 +04:00
goto retry ;
}
/**
* nand_wait - [ DEFAULT ] wait until the command is done
* @ mtd : MTD device structure
2006-06-29 08:48:27 +04:00
* @ chip : NAND chip structure
2005-04-17 02:20:36 +04:00
*
* Wait for command done . This applies to erase and program only
2005-11-07 14:15:49 +03:00
* Erase can take up to 400 ms and program up to 20 ms according to
2005-04-17 02:20:36 +04:00
* general NAND and SmartMedia specs
2006-06-29 08:48:27 +04:00
*/
2006-06-20 22:05:05 +04:00
static int nand_wait ( struct mtd_info * mtd , struct nand_chip * chip )
2005-04-17 02:20:36 +04:00
{
2006-05-13 21:07:53 +04:00
unsigned long timeo = jiffies ;
2006-06-20 22:05:05 +04:00
int status , state = chip - > state ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
if ( state = = FL_ERASING )
2006-05-13 21:07:53 +04:00
timeo + = ( HZ * 400 ) / 1000 ;
2005-04-17 02:20:36 +04:00
else
2006-05-13 21:07:53 +04:00
timeo + = ( HZ * 20 ) / 1000 ;
2005-04-17 02:20:36 +04:00
2006-03-31 14:31:14 +04:00
led_trigger_event ( nand_led_trigger , LED_FULL ) ;
2005-04-17 02:20:36 +04:00
/* Apply this short delay always to ensure that we do wait tWB in
* any case on any machine . */
2006-05-13 21:07:53 +04:00
ndelay ( 100 ) ;
2005-04-17 02:20:36 +04:00
2006-05-24 14:07:37 +04:00
if ( ( state = = FL_ERASING ) & & ( chip - > options & NAND_IS_AND ) )
chip - > cmdfunc ( mtd , NAND_CMD_STATUS_MULTI , - 1 , - 1 ) ;
2005-11-07 14:15:49 +03:00
else
2006-05-24 14:07:37 +04:00
chip - > cmdfunc ( mtd , NAND_CMD_STATUS , - 1 , - 1 ) ;
2005-04-17 02:20:36 +04:00
2005-11-07 14:15:49 +03:00
while ( time_before ( jiffies , timeo ) ) {
2006-05-24 14:07:37 +04:00
if ( chip - > dev_ready ) {
if ( chip - > dev_ready ( mtd ) )
2005-11-07 14:15:49 +03:00
break ;
2005-04-17 02:20:36 +04:00
} else {
2006-05-24 14:07:37 +04:00
if ( chip - > read_byte ( mtd ) & NAND_STATUS_READY )
2005-04-17 02:20:36 +04:00
break ;
}
2005-03-01 12:32:48 +03:00
cond_resched ( ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-31 14:31:14 +04:00
led_trigger_event ( nand_led_trigger , LED_OFF ) ;
2006-05-24 14:07:37 +04:00
status = ( int ) chip - > read_byte ( mtd ) ;
2005-04-17 02:20:36 +04:00
return status ;
}
2006-05-29 05:26:58 +04:00
/**
* nand_read_page_raw - [ Intern ] read raw page data without ecc
* @ mtd : mtd info structure
* @ chip : nand chip info structure
* @ buf : buffer to store read data
*/
static int nand_read_page_raw ( struct mtd_info * mtd , struct nand_chip * chip ,
uint8_t * buf )
{
chip - > read_buf ( mtd , buf , mtd - > writesize ) ;
chip - > read_buf ( mtd , chip - > oob_poi , mtd - > oobsize ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/**
2006-10-19 17:04:02 +04:00
* nand_read_page_swecc - [ REPLACABLE ] software ecc based page read function
2006-05-25 12:07:16 +04:00
* @ mtd : mtd info structure
* @ chip : nand chip info structure
* @ buf : buffer to store read data
2005-01-24 06:07:46 +03:00
*/
2006-05-25 12:07:16 +04:00
static int nand_read_page_swecc ( struct mtd_info * mtd , struct nand_chip * chip ,
uint8_t * buf )
2005-04-17 02:20:36 +04:00
{
2006-05-25 12:07:16 +04:00
int i , eccsize = chip - > ecc . size ;
int eccbytes = chip - > ecc . bytes ;
int eccsteps = chip - > ecc . steps ;
uint8_t * p = buf ;
2006-09-25 20:08:04 +04:00
uint8_t * ecc_calc = chip - > buffers - > ecccalc ;
uint8_t * ecc_code = chip - > buffers - > ecccode ;
2006-05-28 00:16:10 +04:00
int * eccpos = chip - > ecc . layout - > eccpos ;
2006-05-25 12:07:16 +04:00
2006-05-29 05:26:58 +04:00
nand_read_page_raw ( mtd , chip , buf ) ;
2006-05-25 12:07:16 +04:00
for ( i = 0 ; eccsteps ; eccsteps - - , i + = eccbytes , p + = eccsize )
chip - > ecc . calculate ( mtd , p , & ecc_calc [ i ] ) ;
for ( i = 0 ; i < chip - > ecc . total ; i + + )
2006-05-26 20:52:08 +04:00
ecc_code [ i ] = chip - > oob_poi [ eccpos [ i ] ] ;
2006-05-25 12:07:16 +04:00
eccsteps = chip - > ecc . steps ;
p = buf ;
for ( i = 0 ; eccsteps ; eccsteps - - , i + = eccbytes , p + = eccsize ) {
int stat ;
stat = chip - > ecc . correct ( mtd , p , & ecc_code [ i ] , & ecc_calc [ i ] ) ;
if ( stat = = - 1 )
mtd - > ecc_stats . failed + + ;
else
mtd - > ecc_stats . corrected + = stat ;
}
return 0 ;
2005-04-04 22:56:32 +04:00
}
2005-04-17 02:20:36 +04:00
2005-01-24 06:07:46 +03:00
/**
2006-10-19 17:04:02 +04:00
* nand_read_page_hwecc - [ REPLACABLE ] hardware ecc based page read function
2006-05-25 12:07:16 +04:00
* @ mtd : mtd info structure
* @ chip : nand chip info structure
* @ buf : buffer to store read data
2005-01-24 06:07:46 +03:00
*
2006-05-25 12:07:16 +04:00
* Not for syndrome calculating ecc controllers which need a special oob layout
2005-01-24 06:07:46 +03:00
*/
2006-05-25 12:07:16 +04:00
static int nand_read_page_hwecc ( struct mtd_info * mtd , struct nand_chip * chip ,
uint8_t * buf )
2005-04-17 02:20:36 +04:00
{
2006-05-25 12:07:16 +04:00
int i , eccsize = chip - > ecc . size ;
int eccbytes = chip - > ecc . bytes ;
int eccsteps = chip - > ecc . steps ;
uint8_t * p = buf ;
2006-09-25 20:08:04 +04:00
uint8_t * ecc_calc = chip - > buffers - > ecccalc ;
uint8_t * ecc_code = chip - > buffers - > ecccode ;
2006-05-28 00:16:10 +04:00
int * eccpos = chip - > ecc . layout - > eccpos ;
2006-05-25 12:07:16 +04:00
for ( i = 0 ; eccsteps ; eccsteps - - , i + = eccbytes , p + = eccsize ) {
chip - > ecc . hwctl ( mtd , NAND_ECC_READ ) ;
chip - > read_buf ( mtd , p , eccsize ) ;
chip - > ecc . calculate ( mtd , p , & ecc_calc [ i ] ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-26 20:52:08 +04:00
chip - > read_buf ( mtd , chip - > oob_poi , mtd - > oobsize ) ;
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
for ( i = 0 ; i < chip - > ecc . total ; i + + )
2006-05-26 20:52:08 +04:00
ecc_code [ i ] = chip - > oob_poi [ eccpos [ i ] ] ;
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
eccsteps = chip - > ecc . steps ;
p = buf ;
2005-11-07 14:15:49 +03:00
2006-05-25 12:07:16 +04:00
for ( i = 0 ; eccsteps ; eccsteps - - , i + = eccbytes , p + = eccsize ) {
int stat ;
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
stat = chip - > ecc . correct ( mtd , p , & ecc_code [ i ] , & ecc_calc [ i ] ) ;
if ( stat = = - 1 )
mtd - > ecc_stats . failed + + ;
else
mtd - > ecc_stats . corrected + = stat ;
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
/**
2006-10-19 17:04:02 +04:00
* nand_read_page_syndrome - [ REPLACABLE ] hardware ecc syndrom based page read
2006-05-25 12:07:16 +04:00
* @ mtd : mtd info structure
* @ chip : nand chip info structure
* @ buf : buffer to store read data
*
* The hw generator calculates the error syndrome automatically . Therefor
2006-05-26 20:52:08 +04:00
* we need a special oob layout and handling .
2006-05-25 12:07:16 +04:00
*/
static int nand_read_page_syndrome ( struct mtd_info * mtd , struct nand_chip * chip ,
uint8_t * buf )
{
int i , eccsize = chip - > ecc . size ;
int eccbytes = chip - > ecc . bytes ;
int eccsteps = chip - > ecc . steps ;
uint8_t * p = buf ;
2006-05-26 20:52:08 +04:00
uint8_t * oob = chip - > oob_poi ;
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
for ( i = 0 ; eccsteps ; eccsteps - - , i + = eccbytes , p + = eccsize ) {
int stat ;
2005-11-07 14:15:49 +03:00
2006-05-25 12:07:16 +04:00
chip - > ecc . hwctl ( mtd , NAND_ECC_READ ) ;
chip - > read_buf ( mtd , p , eccsize ) ;
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
if ( chip - > ecc . prepad ) {
chip - > read_buf ( mtd , oob , chip - > ecc . prepad ) ;
oob + = chip - > ecc . prepad ;
}
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
chip - > ecc . hwctl ( mtd , NAND_ECC_READSYN ) ;
chip - > read_buf ( mtd , oob , eccbytes ) ;
stat = chip - > ecc . correct ( mtd , p , oob , NULL ) ;
2005-11-07 14:15:49 +03:00
2006-05-25 12:07:16 +04:00
if ( stat = = - 1 )
mtd - > ecc_stats . failed + + ;
2005-11-07 14:15:49 +03:00
else
2006-05-25 12:07:16 +04:00
mtd - > ecc_stats . corrected + = stat ;
2005-11-07 14:15:49 +03:00
2006-05-25 12:07:16 +04:00
oob + = eccbytes ;
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
if ( chip - > ecc . postpad ) {
chip - > read_buf ( mtd , oob , chip - > ecc . postpad ) ;
oob + = chip - > ecc . postpad ;
2005-11-07 14:15:49 +03:00
}
2006-05-25 12:07:16 +04:00
}
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
/* Calculate remaining oob bytes */
2006-06-07 09:34:37 +04:00
i = mtd - > oobsize - ( oob - chip - > oob_poi ) ;
2006-05-25 12:07:16 +04:00
if ( i )
chip - > read_buf ( mtd , oob , i ) ;
2005-11-07 14:15:49 +03:00
2006-05-25 12:07:16 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
/**
2006-05-29 05:26:58 +04:00
* nand_transfer_oob - [ Internal ] Transfer oob to client buffer
* @ chip : nand chip structure
2006-06-29 08:48:27 +04:00
* @ oob : oob destination address
2006-05-29 05:26:58 +04:00
* @ ops : oob ops structure
*/
static uint8_t * nand_transfer_oob ( struct nand_chip * chip , uint8_t * oob ,
struct mtd_oob_ops * ops )
{
size_t len = ops - > ooblen ;
switch ( ops - > mode ) {
case MTD_OOB_PLACE :
case MTD_OOB_RAW :
memcpy ( oob , chip - > oob_poi + ops - > ooboffs , len ) ;
return oob + len ;
case MTD_OOB_AUTO : {
struct nand_oobfree * free = chip - > ecc . layout - > oobfree ;
2006-06-20 22:05:05 +04:00
uint32_t boffs = 0 , roffs = ops - > ooboffs ;
size_t bytes = 0 ;
2006-05-29 05:26:58 +04:00
for ( ; free - > length & & len ; free + + , len - = bytes ) {
2006-06-20 22:05:05 +04:00
/* Read request not from offset 0 ? */
if ( unlikely ( roffs ) ) {
if ( roffs > = free - > length ) {
roffs - = free - > length ;
continue ;
}
boffs = free - > offset + roffs ;
bytes = min_t ( size_t , len ,
( free - > length - roffs ) ) ;
roffs = 0 ;
} else {
bytes = min_t ( size_t , len , free - > length ) ;
boffs = free - > offset ;
}
memcpy ( oob , chip - > oob_poi + boffs , bytes ) ;
2006-05-29 05:26:58 +04:00
oob + = bytes ;
}
return oob ;
}
default :
BUG ( ) ;
}
return NULL ;
}
/**
* nand_do_read_ops - [ Internal ] Read data with ECC
2006-05-25 12:07:16 +04:00
*
* @ mtd : MTD device structure
* @ from : offset to read from
2006-06-29 08:48:27 +04:00
* @ ops : oob ops structure
2006-05-25 12:07:16 +04:00
*
* Internal function . Called with chip held .
*/
2006-05-29 05:26:58 +04:00
static int nand_do_read_ops ( struct mtd_info * mtd , loff_t from ,
struct mtd_oob_ops * ops )
2006-05-25 12:07:16 +04:00
{
int chipnr , page , realpage , col , bytes , aligned ;
struct nand_chip * chip = mtd - > priv ;
struct mtd_ecc_stats stats ;
int blkcheck = ( 1 < < ( chip - > phys_erase_shift - chip - > page_shift ) ) - 1 ;
int sndcmd = 1 ;
int ret = 0 ;
2006-05-29 05:26:58 +04:00
uint32_t readlen = ops - > len ;
uint8_t * bufpoi , * oob , * buf ;
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
stats = mtd - > ecc_stats ;
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
chipnr = ( int ) ( from > > chip - > chip_shift ) ;
chip - > select_chip ( mtd , chipnr ) ;
2005-11-07 14:15:49 +03:00
2006-05-25 12:07:16 +04:00
realpage = ( int ) ( from > > chip - > page_shift ) ;
page = realpage & chip - > pagemask ;
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
col = ( int ) ( from & ( mtd - > writesize - 1 ) ) ;
2006-09-25 20:08:04 +04:00
chip - > oob_poi = chip - > buffers - > oobrbuf ;
2005-11-07 14:15:49 +03:00
2006-05-29 05:26:58 +04:00
buf = ops - > datbuf ;
oob = ops - > oobbuf ;
2006-05-25 12:07:16 +04:00
while ( 1 ) {
bytes = min ( mtd - > writesize - col , readlen ) ;
aligned = ( bytes = = mtd - > writesize ) ;
2005-11-07 14:15:49 +03:00
2006-05-25 12:07:16 +04:00
/* Is the current page in the buffer ? */
2006-05-29 05:26:58 +04:00
if ( realpage ! = chip - > pagebuf | | oob ) {
2006-09-25 20:08:04 +04:00
bufpoi = aligned ? buf : chip - > buffers - > databuf ;
2005-11-07 14:15:49 +03:00
2006-05-25 12:07:16 +04:00
if ( likely ( sndcmd ) ) {
chip - > cmdfunc ( mtd , NAND_CMD_READ0 , 0x00 , page ) ;
sndcmd = 0 ;
2005-04-17 02:20:36 +04:00
}
2006-05-25 12:07:16 +04:00
/* Now read the page into the buffer */
2006-09-25 20:12:39 +04:00
if ( unlikely ( ops - > mode = = MTD_OOB_RAW ) )
ret = chip - > ecc . read_page_raw ( mtd , chip , bufpoi ) ;
else
ret = chip - > ecc . read_page ( mtd , chip , bufpoi ) ;
2006-05-25 12:07:16 +04:00
if ( ret < 0 )
2005-04-17 02:20:36 +04:00
break ;
2006-05-25 12:07:16 +04:00
/* Transfer not aligned data */
if ( ! aligned ) {
chip - > pagebuf = realpage ;
2006-09-25 20:08:04 +04:00
memcpy ( buf , chip - > buffers - > databuf + col , bytes ) ;
2006-05-25 12:07:16 +04:00
}
2006-05-29 05:26:58 +04:00
buf + = bytes ;
if ( unlikely ( oob ) ) {
/* Raw mode does data:oob:data:oob */
if ( ops - > mode ! = MTD_OOB_RAW )
oob = nand_transfer_oob ( chip , oob , ops ) ;
else
buf = nand_transfer_oob ( chip , buf , ops ) ;
}
2006-05-25 12:07:16 +04:00
if ( ! ( chip - > options & NAND_NO_READRDY ) ) {
/*
* Apply delay or wait for ready / busy pin . Do
* this before the AUTOINCR check , so no
* problems arise if a chip which does auto
* increment is marked as NOAUTOINCR by the
* board driver .
*/
if ( ! chip - > dev_ready )
udelay ( chip - > chip_delay ) ;
else
nand_wait_ready ( mtd ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-29 05:26:58 +04:00
} else {
2006-09-25 20:08:04 +04:00
memcpy ( buf , chip - > buffers - > databuf + col , bytes ) ;
2006-05-29 05:26:58 +04:00
buf + = bytes ;
}
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
readlen - = bytes ;
2005-11-07 14:15:49 +03:00
2006-05-25 12:07:16 +04:00
if ( ! readlen )
2005-11-07 14:15:49 +03:00
break ;
2005-04-17 02:20:36 +04:00
/* For subsequent reads align to page boundary. */
col = 0 ;
/* Increment page address */
realpage + + ;
2006-05-24 14:07:37 +04:00
page = realpage & chip - > pagemask ;
2005-04-17 02:20:36 +04:00
/* Check, if we cross a chip boundary */
if ( ! page ) {
chipnr + + ;
2006-05-24 14:07:37 +04:00
chip - > select_chip ( mtd , - 1 ) ;
chip - > select_chip ( mtd , chipnr ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-25 12:07:16 +04:00
2005-11-07 14:15:49 +03:00
/* Check, if the chip supports auto page increment
* or if we have hit a block boundary .
2006-05-13 21:07:53 +04:00
*/
2006-05-25 12:07:16 +04:00
if ( ! NAND_CANAUTOINCR ( chip ) | | ! ( page & blkcheck ) )
2005-11-07 14:15:49 +03:00
sndcmd = 1 ;
2005-04-17 02:20:36 +04:00
}
2006-05-29 05:26:58 +04:00
ops - > retlen = ops - > len - ( size_t ) readlen ;
2005-04-17 02:20:36 +04:00
2006-05-25 12:07:16 +04:00
if ( ret )
return ret ;
2006-05-29 16:56:39 +04:00
if ( mtd - > ecc_stats . failed - stats . failed )
return - EBADMSG ;
return mtd - > ecc_stats . corrected - stats . corrected ? - EUCLEAN : 0 ;
2006-05-25 12:07:16 +04:00
}
/**
* nand_read - [ MTD Interface ] MTD compability function for nand_do_read_ecc
* @ mtd : MTD device structure
* @ from : offset to read from
* @ len : number of bytes to read
* @ retlen : pointer to variable to store the number of read bytes
* @ buf : the databuffer to put data
*
* Get hold of the chip and call nand_do_read
*/
static int nand_read ( struct mtd_info * mtd , loff_t from , size_t len ,
size_t * retlen , uint8_t * buf )
{
2006-05-29 05:26:58 +04:00
struct nand_chip * chip = mtd - > priv ;
2006-05-25 12:07:16 +04:00
int ret ;
/* Do not allow reads past end of device */
if ( ( from + len ) > mtd - > size )
return - EINVAL ;
if ( ! len )
return 0 ;
2006-05-29 05:26:58 +04:00
nand_get_device ( chip , mtd , FL_READING ) ;
2006-05-25 12:07:16 +04:00
2006-05-29 05:26:58 +04:00
chip - > ops . len = len ;
chip - > ops . datbuf = buf ;
chip - > ops . oobbuf = NULL ;
ret = nand_do_read_ops ( mtd , from , & chip - > ops ) ;
2006-05-25 12:07:16 +04:00
2006-08-27 12:23:33 +04:00
* retlen = chip - > ops . retlen ;
2006-05-25 12:07:16 +04:00
nand_release_device ( mtd ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2006-06-20 22:05:05 +04:00
/**
* nand_read_oob_std - [ REPLACABLE ] the most common OOB data read function
* @ mtd : mtd info structure
* @ chip : nand chip info structure
* @ page : page number to read
* @ sndcmd : flag whether to issue read command or not
*/
static int nand_read_oob_std ( struct mtd_info * mtd , struct nand_chip * chip ,
int page , int sndcmd )
{
if ( sndcmd ) {
chip - > cmdfunc ( mtd , NAND_CMD_READOOB , 0 , page ) ;
sndcmd = 0 ;
}
chip - > read_buf ( mtd , chip - > oob_poi , mtd - > oobsize ) ;
return sndcmd ;
}
/**
* nand_read_oob_syndrome - [ REPLACABLE ] OOB data read function for HW ECC
* with syndromes
* @ mtd : mtd info structure
* @ chip : nand chip info structure
* @ page : page number to read
* @ sndcmd : flag whether to issue read command or not
*/
static int nand_read_oob_syndrome ( struct mtd_info * mtd , struct nand_chip * chip ,
int page , int sndcmd )
{
uint8_t * buf = chip - > oob_poi ;
int length = mtd - > oobsize ;
int chunk = chip - > ecc . bytes + chip - > ecc . prepad + chip - > ecc . postpad ;
int eccsize = chip - > ecc . size ;
uint8_t * bufpoi = buf ;
int i , toread , sndrnd = 0 , pos ;
chip - > cmdfunc ( mtd , NAND_CMD_READ0 , chip - > ecc . size , page ) ;
for ( i = 0 ; i < chip - > ecc . steps ; i + + ) {
if ( sndrnd ) {
pos = eccsize + i * ( eccsize + chunk ) ;
if ( mtd - > writesize > 512 )
chip - > cmdfunc ( mtd , NAND_CMD_RNDOUT , pos , - 1 ) ;
else
chip - > cmdfunc ( mtd , NAND_CMD_READ0 , pos , page ) ;
} else
sndrnd = 1 ;
toread = min_t ( int , length , chunk ) ;
chip - > read_buf ( mtd , bufpoi , toread ) ;
bufpoi + = toread ;
length - = toread ;
}
if ( length > 0 )
chip - > read_buf ( mtd , bufpoi , length ) ;
return 1 ;
}
/**
* nand_write_oob_std - [ REPLACABLE ] the most common OOB data write function
* @ mtd : mtd info structure
* @ chip : nand chip info structure
* @ page : page number to write
*/
static int nand_write_oob_std ( struct mtd_info * mtd , struct nand_chip * chip ,
int page )
{
int status = 0 ;
const uint8_t * buf = chip - > oob_poi ;
int length = mtd - > oobsize ;
chip - > cmdfunc ( mtd , NAND_CMD_SEQIN , mtd - > writesize , page ) ;
chip - > write_buf ( mtd , buf , length ) ;
/* Send command to program the OOB data */
chip - > cmdfunc ( mtd , NAND_CMD_PAGEPROG , - 1 , - 1 ) ;
status = chip - > waitfunc ( mtd , chip ) ;
2006-06-21 13:51:20 +04:00
return status & NAND_STATUS_FAIL ? - EIO : 0 ;
2006-06-20 22:05:05 +04:00
}
/**
* nand_write_oob_syndrome - [ REPLACABLE ] OOB data write function for HW ECC
* with syndrome - only for large page flash !
* @ mtd : mtd info structure
* @ chip : nand chip info structure
* @ page : page number to write
*/
static int nand_write_oob_syndrome ( struct mtd_info * mtd ,
struct nand_chip * chip , int page )
{
int chunk = chip - > ecc . bytes + chip - > ecc . prepad + chip - > ecc . postpad ;
int eccsize = chip - > ecc . size , length = mtd - > oobsize ;
int i , len , pos , status = 0 , sndcmd = 0 , steps = chip - > ecc . steps ;
const uint8_t * bufpoi = chip - > oob_poi ;
/*
* data - ecc - data - ecc . . . ecc - oob
* or
* data - pad - ecc - pad - data - pad . . . . ecc - pad - oob
*/
if ( ! chip - > ecc . prepad & & ! chip - > ecc . postpad ) {
pos = steps * ( eccsize + chunk ) ;
steps = 0 ;
} else
2006-07-11 11:11:25 +04:00
pos = eccsize ;
2006-06-20 22:05:05 +04:00
chip - > cmdfunc ( mtd , NAND_CMD_SEQIN , pos , page ) ;
for ( i = 0 ; i < steps ; i + + ) {
if ( sndcmd ) {
if ( mtd - > writesize < = 512 ) {
uint32_t fill = 0xFFFFFFFF ;
len = eccsize ;
while ( len > 0 ) {
int num = min_t ( int , len , 4 ) ;
chip - > write_buf ( mtd , ( uint8_t * ) & fill ,
num ) ;
len - = num ;
}
} else {
pos = eccsize + i * ( eccsize + chunk ) ;
chip - > cmdfunc ( mtd , NAND_CMD_RNDIN , pos , - 1 ) ;
}
} else
sndcmd = 1 ;
len = min_t ( int , length , chunk ) ;
chip - > write_buf ( mtd , bufpoi , len ) ;
bufpoi + = len ;
length - = len ;
}
if ( length > 0 )
chip - > write_buf ( mtd , bufpoi , length ) ;
chip - > cmdfunc ( mtd , NAND_CMD_PAGEPROG , - 1 , - 1 ) ;
status = chip - > waitfunc ( mtd , chip ) ;
return status & NAND_STATUS_FAIL ? - EIO : 0 ;
}
2005-04-17 02:20:36 +04:00
/**
2006-05-29 05:26:58 +04:00
* nand_do_read_oob - [ Intern ] NAND read out - of - band
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
* @ from : offset to read from
2006-05-29 05:26:58 +04:00
* @ ops : oob operations description structure
2005-04-17 02:20:36 +04:00
*
* NAND read out - of - band data from the spare area
*/
2006-05-29 05:26:58 +04:00
static int nand_do_read_oob ( struct mtd_info * mtd , loff_t from ,
struct mtd_oob_ops * ops )
2005-04-17 02:20:36 +04:00
{
2006-06-20 22:05:05 +04:00
int page , realpage , chipnr , sndcmd = 1 ;
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2006-05-25 11:51:54 +04:00
int blkcheck = ( 1 < < ( chip - > phys_erase_shift - chip - > page_shift ) ) - 1 ;
2006-06-20 22:05:05 +04:00
int readlen = ops - > len ;
uint8_t * buf = ops - > oobbuf ;
2005-11-07 14:15:49 +03:00
2006-05-30 12:06:41 +04:00
DEBUG ( MTD_DEBUG_LEVEL3 , " nand_read_oob: from = 0x%08Lx, len = %i \n " ,
( unsigned long long ) from , readlen ) ;
2005-04-17 02:20:36 +04:00
2006-05-25 11:51:54 +04:00
chipnr = ( int ) ( from > > chip - > chip_shift ) ;
2006-05-24 14:07:37 +04:00
chip - > select_chip ( mtd , chipnr ) ;
2005-04-17 02:20:36 +04:00
2006-05-25 11:51:54 +04:00
/* Shift to get page */
realpage = ( int ) ( from > > chip - > page_shift ) ;
page = realpage & chip - > pagemask ;
2005-04-17 02:20:36 +04:00
2006-09-25 20:08:04 +04:00
chip - > oob_poi = chip - > buffers - > oobrbuf ;
2006-05-25 11:51:54 +04:00
while ( 1 ) {
2006-06-20 22:05:05 +04:00
sndcmd = chip - > ecc . read_oob ( mtd , chip , page , sndcmd ) ;
buf = nand_transfer_oob ( chip , buf , ops ) ;
2006-05-29 05:26:58 +04:00
2006-05-25 11:51:54 +04:00
if ( ! ( chip - > options & NAND_NO_READRDY ) ) {
/*
* Apply delay or wait for ready / busy pin . Do this
* before the AUTOINCR check , so no problems arise if a
* chip which does auto increment is marked as
* NOAUTOINCR by the board driver .
2005-07-15 17:53:51 +04:00
*/
2006-05-24 14:07:37 +04:00
if ( ! chip - > dev_ready )
udelay ( chip - > chip_delay ) ;
2005-07-15 17:53:51 +04:00
else
nand_wait_ready ( mtd ) ;
2006-05-25 11:51:54 +04:00
}
2005-07-15 17:53:51 +04:00
2006-06-21 13:51:20 +04:00
readlen - = ops - > ooblen ;
if ( ! readlen )
break ;
2006-05-25 11:51:54 +04:00
/* Increment page address */
realpage + + ;
page = realpage & chip - > pagemask ;
/* Check, if we cross a chip boundary */
if ( ! page ) {
chipnr + + ;
chip - > select_chip ( mtd , - 1 ) ;
chip - > select_chip ( mtd , chipnr ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-25 11:51:54 +04:00
/* Check, if the chip supports auto page increment
* or if we have hit a block boundary .
*/
if ( ! NAND_CANAUTOINCR ( chip ) | | ! ( page & blkcheck ) )
sndcmd = 1 ;
2005-04-17 02:20:36 +04:00
}
2006-05-29 05:26:58 +04:00
ops - > retlen = ops - > len ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/**
2006-05-29 05:26:58 +04:00
* nand_read_oob - [ MTD Interface ] NAND read data and / or out - of - band
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
* @ from : offset to read from
2006-05-29 05:26:58 +04:00
* @ ops : oob operation description structure
2005-04-17 02:20:36 +04:00
*
2006-05-29 05:26:58 +04:00
* NAND read data and / or out - of - band data
2005-04-17 02:20:36 +04:00
*/
2006-05-29 05:26:58 +04:00
static int nand_read_oob ( struct mtd_info * mtd , loff_t from ,
struct mtd_oob_ops * ops )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2006-05-29 05:26:58 +04:00
int ret = - ENOTSUPP ;
ops - > retlen = 0 ;
2005-04-17 02:20:36 +04:00
/* Do not allow reads past end of device */
2006-05-29 05:26:58 +04:00
if ( ( from + ops - > len ) > mtd - > size ) {
DEBUG ( MTD_DEBUG_LEVEL0 , " nand_read_oob: "
2006-05-24 14:07:37 +04:00
" Attempt read beyond end of device \n " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2006-05-24 14:07:37 +04:00
nand_get_device ( chip , mtd , FL_READING ) ;
2005-04-17 02:20:36 +04:00
2006-05-29 05:26:58 +04:00
switch ( ops - > mode ) {
case MTD_OOB_PLACE :
case MTD_OOB_AUTO :
case MTD_OOB_RAW :
break ;
2005-04-17 02:20:36 +04:00
2006-05-29 05:26:58 +04:00
default :
goto out ;
}
2005-04-17 02:20:36 +04:00
2006-05-29 05:26:58 +04:00
if ( ! ops - > datbuf )
ret = nand_do_read_oob ( mtd , from , ops ) ;
else
ret = nand_do_read_ops ( mtd , from , ops ) ;
2005-11-07 14:15:49 +03:00
2006-05-29 05:26:58 +04:00
out :
nand_release_device ( mtd ) ;
return ret ;
}
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
2006-05-29 05:26:58 +04:00
/**
* nand_write_page_raw - [ Intern ] raw page write function
* @ mtd : mtd info structure
* @ chip : nand chip info structure
* @ buf : data buffer
*/
static void nand_write_page_raw ( struct mtd_info * mtd , struct nand_chip * chip ,
const uint8_t * buf )
{
chip - > write_buf ( mtd , buf , mtd - > writesize ) ;
chip - > write_buf ( mtd , chip - > oob_poi , mtd - > oobsize ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-23 19:21:03 +04:00
/**
2006-10-19 17:04:02 +04:00
* nand_write_page_swecc - [ REPLACABLE ] software ecc based page write function
2006-05-26 20:52:08 +04:00
* @ mtd : mtd info structure
* @ chip : nand chip info structure
* @ buf : data buffer
2006-05-23 19:21:03 +04:00
*/
2006-05-26 20:52:08 +04:00
static void nand_write_page_swecc ( struct mtd_info * mtd , struct nand_chip * chip ,
const uint8_t * buf )
2006-05-23 19:21:03 +04:00
{
2006-05-26 20:52:08 +04:00
int i , eccsize = chip - > ecc . size ;
int eccbytes = chip - > ecc . bytes ;
int eccsteps = chip - > ecc . steps ;
2006-09-25 20:08:04 +04:00
uint8_t * ecc_calc = chip - > buffers - > ecccalc ;
2006-05-26 20:52:08 +04:00
const uint8_t * p = buf ;
2006-05-28 00:16:10 +04:00
int * eccpos = chip - > ecc . layout - > eccpos ;
2006-05-23 19:21:03 +04:00
2006-05-29 05:26:58 +04:00
/* Software ecc calculation */
for ( i = 0 ; eccsteps ; eccsteps - - , i + = eccbytes , p + = eccsize )
chip - > ecc . calculate ( mtd , p , & ecc_calc [ i ] ) ;
2006-05-23 19:21:03 +04:00
2006-05-29 05:26:58 +04:00
for ( i = 0 ; i < chip - > ecc . total ; i + + )
chip - > oob_poi [ eccpos [ i ] ] = ecc_calc [ i ] ;
2006-05-23 19:21:03 +04:00
2006-05-29 05:26:58 +04:00
nand_write_page_raw ( mtd , chip , buf ) ;
2006-05-26 20:52:08 +04:00
}
2006-05-23 19:21:03 +04:00
2006-05-26 20:52:08 +04:00
/**
2006-10-19 17:04:02 +04:00
* nand_write_page_hwecc - [ REPLACABLE ] hardware ecc based page write function
2006-05-26 20:52:08 +04:00
* @ mtd : mtd info structure
* @ chip : nand chip info structure
* @ buf : data buffer
*/
static void nand_write_page_hwecc ( struct mtd_info * mtd , struct nand_chip * chip ,
const uint8_t * buf )
{
int i , eccsize = chip - > ecc . size ;
int eccbytes = chip - > ecc . bytes ;
int eccsteps = chip - > ecc . steps ;
2006-09-25 20:08:04 +04:00
uint8_t * ecc_calc = chip - > buffers - > ecccalc ;
2006-05-26 20:52:08 +04:00
const uint8_t * p = buf ;
2006-05-28 00:16:10 +04:00
int * eccpos = chip - > ecc . layout - > eccpos ;
2006-05-23 19:21:03 +04:00
2006-05-26 20:52:08 +04:00
for ( i = 0 ; eccsteps ; eccsteps - - , i + = eccbytes , p + = eccsize ) {
chip - > ecc . hwctl ( mtd , NAND_ECC_WRITE ) ;
2006-05-27 02:05:44 +04:00
chip - > write_buf ( mtd , p , eccsize ) ;
2006-05-26 20:52:08 +04:00
chip - > ecc . calculate ( mtd , p , & ecc_calc [ i ] ) ;
2006-05-23 19:21:03 +04:00
}
2006-05-26 20:52:08 +04:00
for ( i = 0 ; i < chip - > ecc . total ; i + + )
chip - > oob_poi [ eccpos [ i ] ] = ecc_calc [ i ] ;
chip - > write_buf ( mtd , chip - > oob_poi , mtd - > oobsize ) ;
2006-05-23 19:21:03 +04:00
}
2005-11-07 14:15:49 +03:00
/**
2006-10-19 17:04:02 +04:00
* nand_write_page_syndrome - [ REPLACABLE ] hardware ecc syndrom based page write
2006-05-26 20:52:08 +04:00
* @ mtd : mtd info structure
* @ chip : nand chip info structure
* @ buf : data buffer
2005-04-17 02:20:36 +04:00
*
2006-05-26 20:52:08 +04:00
* The hw generator calculates the error syndrome automatically . Therefor
* we need a special oob layout and handling .
*/
static void nand_write_page_syndrome ( struct mtd_info * mtd ,
struct nand_chip * chip , const uint8_t * buf )
2005-04-17 02:20:36 +04:00
{
2006-05-26 20:52:08 +04:00
int i , eccsize = chip - > ecc . size ;
int eccbytes = chip - > ecc . bytes ;
int eccsteps = chip - > ecc . steps ;
const uint8_t * p = buf ;
uint8_t * oob = chip - > oob_poi ;
2005-04-17 02:20:36 +04:00
2006-05-26 20:52:08 +04:00
for ( i = 0 ; eccsteps ; eccsteps - - , i + = eccbytes , p + = eccsize ) {
2005-04-17 02:20:36 +04:00
2006-05-26 20:52:08 +04:00
chip - > ecc . hwctl ( mtd , NAND_ECC_WRITE ) ;
chip - > write_buf ( mtd , p , eccsize ) ;
2005-11-07 14:15:49 +03:00
2006-05-26 20:52:08 +04:00
if ( chip - > ecc . prepad ) {
chip - > write_buf ( mtd , oob , chip - > ecc . prepad ) ;
oob + = chip - > ecc . prepad ;
}
chip - > ecc . calculate ( mtd , p , oob ) ;
chip - > write_buf ( mtd , oob , eccbytes ) ;
oob + = eccbytes ;
if ( chip - > ecc . postpad ) {
chip - > write_buf ( mtd , oob , chip - > ecc . postpad ) ;
oob + = chip - > ecc . postpad ;
2005-04-17 02:20:36 +04:00
}
}
2006-05-26 20:52:08 +04:00
/* Calculate remaining oob bytes */
2006-06-07 09:34:37 +04:00
i = mtd - > oobsize - ( oob - chip - > oob_poi ) ;
2006-05-26 20:52:08 +04:00
if ( i )
chip - > write_buf ( mtd , oob , i ) ;
}
/**
2006-09-25 20:12:39 +04:00
* nand_write_page - [ REPLACEABLE ] write one page
2006-05-26 20:52:08 +04:00
* @ mtd : MTD device structure
* @ chip : NAND chip descriptor
* @ buf : the data to write
* @ page : page number to write
* @ cached : cached programming
*/
static int nand_write_page ( struct mtd_info * mtd , struct nand_chip * chip ,
2006-09-25 20:12:39 +04:00
const uint8_t * buf , int page , int cached , int raw )
2006-05-26 20:52:08 +04:00
{
int status ;
chip - > cmdfunc ( mtd , NAND_CMD_SEQIN , 0x00 , page ) ;
2006-09-25 20:12:39 +04:00
if ( unlikely ( raw ) )
chip - > ecc . write_page_raw ( mtd , chip , buf ) ;
else
chip - > ecc . write_page ( mtd , chip , buf ) ;
2006-05-26 20:52:08 +04:00
/*
* Cached progamming disabled for now , Not sure if its worth the
* trouble . The speed gain is not very impressive . ( 2.3 - > 2.6 Mib / s )
*/
cached = 0 ;
if ( ! cached | | ! ( chip - > options & NAND_CACHEPRG ) ) {
chip - > cmdfunc ( mtd , NAND_CMD_PAGEPROG , - 1 , - 1 ) ;
2006-06-20 22:05:05 +04:00
status = chip - > waitfunc ( mtd , chip ) ;
2006-05-26 20:52:08 +04:00
/*
* See if operation failed and additional status checks are
* available
*/
if ( ( status & NAND_STATUS_FAIL ) & & ( chip - > errstat ) )
status = chip - > errstat ( mtd , chip , FL_WRITING , status ,
page ) ;
if ( status & NAND_STATUS_FAIL )
return - EIO ;
} else {
chip - > cmdfunc ( mtd , NAND_CMD_CACHEDPROG , - 1 , - 1 ) ;
2006-06-20 22:05:05 +04:00
status = chip - > waitfunc ( mtd , chip ) ;
2006-05-26 20:52:08 +04:00
}
# ifdef CONFIG_MTD_NAND_VERIFY_WRITE
/* Send command to read back the data */
chip - > cmdfunc ( mtd , NAND_CMD_READ0 , 0 , page ) ;
if ( chip - > verify_buf ( mtd , buf , mtd - > writesize ) )
return - EIO ;
# endif
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-05-29 05:26:58 +04:00
/**
* nand_fill_oob - [ Internal ] Transfer client buffer to oob
* @ chip : nand chip structure
* @ oob : oob data buffer
* @ ops : oob ops structure
*/
static uint8_t * nand_fill_oob ( struct nand_chip * chip , uint8_t * oob ,
struct mtd_oob_ops * ops )
{
size_t len = ops - > ooblen ;
switch ( ops - > mode ) {
case MTD_OOB_PLACE :
case MTD_OOB_RAW :
memcpy ( chip - > oob_poi + ops - > ooboffs , oob , len ) ;
return oob + len ;
case MTD_OOB_AUTO : {
struct nand_oobfree * free = chip - > ecc . layout - > oobfree ;
2006-06-20 22:05:05 +04:00
uint32_t boffs = 0 , woffs = ops - > ooboffs ;
size_t bytes = 0 ;
2006-05-29 05:26:58 +04:00
for ( ; free - > length & & len ; free + + , len - = bytes ) {
2006-06-20 22:05:05 +04:00
/* Write request not from offset 0 ? */
if ( unlikely ( woffs ) ) {
if ( woffs > = free - > length ) {
woffs - = free - > length ;
continue ;
}
boffs = free - > offset + woffs ;
bytes = min_t ( size_t , len ,
( free - > length - woffs ) ) ;
woffs = 0 ;
} else {
bytes = min_t ( size_t , len , free - > length ) ;
boffs = free - > offset ;
}
2006-07-11 11:11:25 +04:00
memcpy ( chip - > oob_poi + boffs , oob , bytes ) ;
2006-05-29 05:26:58 +04:00
oob + = bytes ;
}
return oob ;
}
default :
BUG ( ) ;
}
return NULL ;
}
2006-05-23 01:18:05 +04:00
# define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0
2005-04-17 02:20:36 +04:00
/**
2006-05-29 05:26:58 +04:00
* nand_do_write_ops - [ Internal ] NAND write with ECC
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
* @ to : offset to write to
2006-05-29 05:26:58 +04:00
* @ ops : oob operations description structure
2005-04-17 02:20:36 +04:00
*
* NAND write with ECC
*/
2006-05-29 05:26:58 +04:00
static int nand_do_write_ops ( struct mtd_info * mtd , loff_t to ,
struct mtd_oob_ops * ops )
2005-04-17 02:20:36 +04:00
{
2006-05-26 20:52:08 +04:00
int chipnr , realpage , page , blockmask ;
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2006-05-29 05:26:58 +04:00
uint32_t writelen = ops - > len ;
uint8_t * oob = ops - > oobbuf ;
uint8_t * buf = ops - > datbuf ;
2006-05-26 20:52:08 +04:00
int bytes = mtd - > writesize ;
2006-05-29 05:26:58 +04:00
int ret ;
2005-04-17 02:20:36 +04:00
2006-05-29 05:26:58 +04:00
ops - > retlen = 0 ;
2005-04-17 02:20:36 +04:00
2005-11-07 14:15:49 +03:00
/* reject writes, which are not page aligned */
2006-05-29 05:26:58 +04:00
if ( NOTALIGNED ( to ) | | NOTALIGNED ( ops - > len ) ) {
2006-05-26 20:52:08 +04:00
printk ( KERN_NOTICE " nand_write: "
" Attempt to write not page aligned data \n " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2006-05-29 05:26:58 +04:00
if ( ! writelen )
2006-05-26 20:52:08 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2006-06-28 02:11:45 +04:00
chipnr = ( int ) ( to > > chip - > chip_shift ) ;
chip - > select_chip ( mtd , chipnr ) ;
2005-04-17 02:20:36 +04:00
/* Check, if it is write protected */
if ( nand_check_wp ( mtd ) )
2006-05-29 05:26:58 +04:00
return - EIO ;
2005-04-17 02:20:36 +04:00
2006-05-26 20:52:08 +04:00
realpage = ( int ) ( to > > chip - > page_shift ) ;
page = realpage & chip - > pagemask ;
blockmask = ( 1 < < ( chip - > phys_erase_shift - chip - > page_shift ) ) - 1 ;
/* Invalidate the page cache, when we write to the cached page */
if ( to < = ( chip - > pagebuf < < chip - > page_shift ) & &
2006-05-29 05:26:58 +04:00
( chip - > pagebuf < < chip - > page_shift ) < ( to + ops - > len ) )
2006-05-24 14:07:37 +04:00
chip - > pagebuf = - 1 ;
2005-11-07 14:15:49 +03:00
2006-09-25 20:08:04 +04:00
chip - > oob_poi = chip - > buffers - > oobwbuf ;
2005-11-07 14:15:49 +03:00
2006-05-26 20:52:08 +04:00
while ( 1 ) {
int cached = writelen > bytes & & page ! = blockmask ;
2005-04-17 02:20:36 +04:00
2006-05-29 05:26:58 +04:00
if ( unlikely ( oob ) )
oob = nand_fill_oob ( chip , oob , ops ) ;
2006-09-25 20:12:39 +04:00
ret = chip - > write_page ( mtd , chip , buf , page , cached ,
( ops - > mode = = MTD_OOB_RAW ) ) ;
2006-05-26 20:52:08 +04:00
if ( ret )
break ;
writelen - = bytes ;
if ( ! writelen )
break ;
buf + = bytes ;
realpage + + ;
page = realpage & chip - > pagemask ;
/* Check, if we cross a chip boundary */
if ( ! page ) {
chipnr + + ;
chip - > select_chip ( mtd , - 1 ) ;
chip - > select_chip ( mtd , chipnr ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-05-29 05:26:58 +04:00
if ( unlikely ( oob ) )
memset ( chip - > oob_poi , 0xff , mtd - > oobsize ) ;
ops - > retlen = ops - > len - writelen ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2006-05-26 20:52:08 +04:00
/**
2006-05-29 05:26:58 +04:00
* nand_write - [ MTD Interface ] NAND write with ECC
2006-05-26 20:52:08 +04:00
* @ mtd : MTD device structure
* @ to : offset to write to
* @ len : number of bytes to write
2006-05-29 05:26:58 +04:00
* @ retlen : pointer to variable to store the number of written bytes
* @ buf : the data to write
2006-05-26 20:52:08 +04:00
*
2006-05-29 05:26:58 +04:00
* NAND write with ECC
2006-05-26 20:52:08 +04:00
*/
2006-05-29 05:26:58 +04:00
static int nand_write ( struct mtd_info * mtd , loff_t to , size_t len ,
size_t * retlen , const uint8_t * buf )
2006-05-26 20:52:08 +04:00
{
struct nand_chip * chip = mtd - > priv ;
int ret ;
2006-05-29 05:26:58 +04:00
/* Do not allow reads past end of device */
if ( ( to + len ) > mtd - > size )
2006-05-26 20:52:08 +04:00
return - EINVAL ;
2006-05-29 05:26:58 +04:00
if ( ! len )
return 0 ;
2006-05-26 20:52:08 +04:00
2006-06-20 22:05:05 +04:00
nand_get_device ( chip , mtd , FL_WRITING ) ;
2006-05-26 20:52:08 +04:00
2006-05-29 05:26:58 +04:00
chip - > ops . len = len ;
chip - > ops . datbuf = ( uint8_t * ) buf ;
chip - > ops . oobbuf = NULL ;
2006-05-26 20:52:08 +04:00
2006-05-29 05:26:58 +04:00
ret = nand_do_write_ops ( mtd , to , & chip - > ops ) ;
2006-05-26 20:52:08 +04:00
2006-08-27 12:23:33 +04:00
* retlen = chip - > ops . retlen ;
2006-05-26 20:52:08 +04:00
nand_release_device ( mtd ) ;
2006-05-29 05:26:58 +04:00
return ret ;
2006-05-26 20:52:08 +04:00
}
2006-05-25 11:51:54 +04:00
2005-04-17 02:20:36 +04:00
/**
2006-05-29 05:26:58 +04:00
* nand_do_write_oob - [ MTD Interface ] NAND write out - of - band
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
* @ to : offset to write to
2006-05-29 05:26:58 +04:00
* @ ops : oob operation description structure
2005-04-17 02:20:36 +04:00
*
* NAND write out - of - band
*/
2006-05-29 05:26:58 +04:00
static int nand_do_write_oob ( struct mtd_info * mtd , loff_t to ,
struct mtd_oob_ops * ops )
2005-04-17 02:20:36 +04:00
{
2006-05-29 05:26:58 +04:00
int chipnr , page , status ;
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
2006-05-25 11:51:54 +04:00
DEBUG ( MTD_DEBUG_LEVEL3 , " nand_write_oob: to = 0x%08x, len = %i \n " ,
2006-05-29 05:26:58 +04:00
( unsigned int ) to , ( int ) ops - > len ) ;
2005-04-17 02:20:36 +04:00
/* Do not allow write past end of page */
2006-05-29 05:26:58 +04:00
if ( ( ops - > ooboffs + ops - > len ) > mtd - > oobsize ) {
2006-05-25 11:51:54 +04:00
DEBUG ( MTD_DEBUG_LEVEL0 , " nand_write_oob: "
" Attempt to write past end of page \n " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2006-05-25 11:51:54 +04:00
chipnr = ( int ) ( to > > chip - > chip_shift ) ;
2006-05-24 14:07:37 +04:00
chip - > select_chip ( mtd , chipnr ) ;
2005-04-17 02:20:36 +04:00
2006-05-25 11:51:54 +04:00
/* Shift to get page */
page = ( int ) ( to > > chip - > page_shift ) ;
/*
* Reset the chip . Some chips ( like the Toshiba TC5832DC found in one
* of my DiskOnChip 2000 test units ) will clear the whole data page too
* if we don ' t do this . I have no clue why , but I seem to have ' fixed '
* it in the doc2000 driver in August 1999. dwmw2 .
*/
2006-05-24 14:07:37 +04:00
chip - > cmdfunc ( mtd , NAND_CMD_RESET , - 1 , - 1 ) ;
2005-04-17 02:20:36 +04:00
/* Check, if it is write protected */
if ( nand_check_wp ( mtd ) )
2006-05-29 05:26:58 +04:00
return - EROFS ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Invalidate the page cache, if we write to the cached page */
2006-05-24 14:07:37 +04:00
if ( page = = chip - > pagebuf )
chip - > pagebuf = - 1 ;
2005-04-17 02:20:36 +04:00
2006-09-25 20:08:04 +04:00
chip - > oob_poi = chip - > buffers - > oobwbuf ;
2006-06-20 22:05:05 +04:00
memset ( chip - > oob_poi , 0xff , mtd - > oobsize ) ;
nand_fill_oob ( chip , ops - > oobbuf , ops ) ;
status = chip - > ecc . write_oob ( mtd , chip , page & chip - > pagemask ) ;
memset ( chip - > oob_poi , 0xff , mtd - > oobsize ) ;
2005-04-17 02:20:36 +04:00
2006-06-20 22:05:05 +04:00
if ( status )
return status ;
2005-04-17 02:20:36 +04:00
2006-05-29 05:26:58 +04:00
ops - > retlen = ops - > len ;
2005-04-17 02:20:36 +04:00
2006-06-20 22:05:05 +04:00
return 0 ;
2006-05-29 05:26:58 +04:00
}
/**
* nand_write_oob - [ MTD Interface ] NAND write data and / or out - of - band
* @ mtd : MTD device structure
2006-06-29 08:48:27 +04:00
* @ to : offset to write to
2006-05-29 05:26:58 +04:00
* @ ops : oob operation description structure
*/
static int nand_write_oob ( struct mtd_info * mtd , loff_t to ,
struct mtd_oob_ops * ops )
{
struct nand_chip * chip = mtd - > priv ;
int ret = - ENOTSUPP ;
ops - > retlen = 0 ;
/* Do not allow writes past end of device */
if ( ( to + ops - > len ) > mtd - > size ) {
DEBUG ( MTD_DEBUG_LEVEL0 , " nand_read_oob: "
" Attempt read beyond end of device \n " ) ;
return - EINVAL ;
}
2006-06-20 22:05:05 +04:00
nand_get_device ( chip , mtd , FL_WRITING ) ;
2006-05-29 05:26:58 +04:00
switch ( ops - > mode ) {
case MTD_OOB_PLACE :
case MTD_OOB_AUTO :
case MTD_OOB_RAW :
break ;
default :
goto out ;
}
if ( ! ops - > datbuf )
ret = nand_do_write_oob ( mtd , to , ops ) ;
else
ret = nand_do_write_ops ( mtd , to , ops ) ;
2006-05-13 21:07:53 +04:00
out :
2005-04-17 02:20:36 +04:00
nand_release_device ( mtd ) ;
return ret ;
}
/**
* single_erease_cmd - [ GENERIC ] NAND standard block erase command function
* @ mtd : MTD device structure
* @ page : the page address of the block which will be erased
*
* Standard erase command for NAND chips
*/
2006-05-13 21:07:53 +04:00
static void single_erase_cmd ( struct mtd_info * mtd , int page )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
/* Send commands to erase a block */
2006-05-24 14:07:37 +04:00
chip - > cmdfunc ( mtd , NAND_CMD_ERASE1 , - 1 , page ) ;
chip - > cmdfunc ( mtd , NAND_CMD_ERASE2 , - 1 , - 1 ) ;
2005-04-17 02:20:36 +04:00
}
/**
* multi_erease_cmd - [ GENERIC ] AND specific block erase command function
* @ mtd : MTD device structure
* @ page : the page address of the block which will be erased
*
* AND multi block erase command function
* Erase 4 consecutive blocks
*/
2006-05-13 21:07:53 +04:00
static void multi_erase_cmd ( struct mtd_info * mtd , int page )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
/* Send commands to erase a block */
2006-05-24 14:07:37 +04:00
chip - > cmdfunc ( mtd , NAND_CMD_ERASE1 , - 1 , page + + ) ;
chip - > cmdfunc ( mtd , NAND_CMD_ERASE1 , - 1 , page + + ) ;
chip - > cmdfunc ( mtd , NAND_CMD_ERASE1 , - 1 , page + + ) ;
chip - > cmdfunc ( mtd , NAND_CMD_ERASE1 , - 1 , page ) ;
chip - > cmdfunc ( mtd , NAND_CMD_ERASE2 , - 1 , - 1 ) ;
2005-04-17 02:20:36 +04:00
}
/**
* nand_erase - [ MTD Interface ] erase block ( s )
* @ mtd : MTD device structure
* @ instr : erase instruction
*
* Erase one ore more blocks
*/
2006-05-13 21:07:53 +04:00
static int nand_erase ( struct mtd_info * mtd , struct erase_info * instr )
2005-04-17 02:20:36 +04:00
{
2006-05-13 21:07:53 +04:00
return nand_erase_nand ( mtd , instr , 0 ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:49 +03:00
2005-01-17 21:35:25 +03:00
# define BBT_PAGE_MASK 0xffffff3f
2005-04-17 02:20:36 +04:00
/**
2006-05-24 14:07:37 +04:00
* nand_erase_nand - [ Internal ] erase block ( s )
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
* @ instr : erase instruction
* @ allowbbt : allow erasing the bbt area
*
* Erase one ore more blocks
*/
2006-05-24 14:07:37 +04:00
int nand_erase_nand ( struct mtd_info * mtd , struct erase_info * instr ,
int allowbbt )
2005-04-17 02:20:36 +04:00
{
int page , len , status , pages_per_block , ret , chipnr ;
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
int rewrite_bbt [ NAND_MAX_CHIPS ] = { 0 } ;
unsigned int bbt_masked_page = 0xffffffff ;
2005-04-17 02:20:36 +04:00
2006-05-24 14:07:37 +04:00
DEBUG ( MTD_DEBUG_LEVEL3 , " nand_erase: start = 0x%08x, len = %i \n " ,
( unsigned int ) instr - > addr , ( unsigned int ) instr - > len ) ;
2005-04-17 02:20:36 +04:00
/* Start address must align on block boundary */
2006-05-24 14:07:37 +04:00
if ( instr - > addr & ( ( 1 < < chip - > phys_erase_shift ) - 1 ) ) {
2006-05-13 21:07:53 +04:00
DEBUG ( MTD_DEBUG_LEVEL0 , " nand_erase: Unaligned address \n " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
/* Length must align on block boundary */
2006-05-24 14:07:37 +04:00
if ( instr - > len & ( ( 1 < < chip - > phys_erase_shift ) - 1 ) ) {
DEBUG ( MTD_DEBUG_LEVEL0 , " nand_erase: "
" Length not block aligned \n " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
/* Do not allow erase past end of device */
if ( ( instr - > len + instr - > addr ) > mtd - > size ) {
2006-05-24 14:07:37 +04:00
DEBUG ( MTD_DEBUG_LEVEL0 , " nand_erase: "
" Erase past end of device \n " ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
instr - > fail_addr = 0xffffffff ;
/* Grab the lock and see if the device is available */
2006-05-24 14:07:37 +04:00
nand_get_device ( chip , mtd , FL_ERASING ) ;
2005-04-17 02:20:36 +04:00
/* Shift to get first page */
2006-05-24 14:07:37 +04:00
page = ( int ) ( instr - > addr > > chip - > page_shift ) ;
chipnr = ( int ) ( instr - > addr > > chip - > chip_shift ) ;
2005-04-17 02:20:36 +04:00
/* Calculate pages in each block */
2006-05-24 14:07:37 +04:00
pages_per_block = 1 < < ( chip - > phys_erase_shift - chip - > page_shift ) ;
2005-04-17 02:20:36 +04:00
/* Select the NAND device */
2006-05-24 14:07:37 +04:00
chip - > select_chip ( mtd , chipnr ) ;
2005-04-17 02:20:36 +04:00
/* Check, if it is write protected */
if ( nand_check_wp ( mtd ) ) {
2006-05-24 14:07:37 +04:00
DEBUG ( MTD_DEBUG_LEVEL0 , " nand_erase: "
" Device is write protected!!! \n " ) ;
2005-04-17 02:20:36 +04:00
instr - > state = MTD_ERASE_FAILED ;
goto erase_exit ;
}
2006-05-24 14:07:37 +04:00
/*
* If BBT requires refresh , set the BBT page mask to see if the BBT
* should be rewritten . Otherwise the mask is set to 0xffffffff which
* can not be matched . This is also done when the bbt is actually
* erased to avoid recusrsive updates
*/
if ( chip - > options & BBT_AUTO_REFRESH & & ! allowbbt )
bbt_masked_page = chip - > bbt_td - > pages [ chipnr ] & BBT_PAGE_MASK ;
2005-01-17 21:35:25 +03:00
2005-04-17 02:20:36 +04:00
/* Loop through the pages */
len = instr - > len ;
instr - > state = MTD_ERASING ;
while ( len ) {
2006-05-24 14:07:37 +04:00
/*
* heck if we have a bad block , we do not erase bad blocks !
*/
if ( nand_block_checkbad ( mtd , ( ( loff_t ) page ) < <
chip - > page_shift , 0 , allowbbt ) ) {
printk ( KERN_WARNING " nand_erase: attempt to erase a "
" bad block at page 0x%08x \n " , page ) ;
2005-04-17 02:20:36 +04:00
instr - > state = MTD_ERASE_FAILED ;
goto erase_exit ;
}
2005-11-07 14:15:49 +03:00
2006-05-24 14:07:37 +04:00
/*
* Invalidate the page cache , if we erase the block which
* contains the current cached page
*/
if ( page < = chip - > pagebuf & & chip - > pagebuf <
( page + pages_per_block ) )
chip - > pagebuf = - 1 ;
2005-04-17 02:20:36 +04:00
2006-05-24 14:07:37 +04:00
chip - > erase_cmd ( mtd , page & chip - > pagemask ) ;
2005-11-07 14:15:49 +03:00
2006-06-20 22:05:05 +04:00
status = chip - > waitfunc ( mtd , chip ) ;
2005-04-17 02:20:36 +04:00
2006-05-24 14:07:37 +04:00
/*
* See if operation failed and additional status checks are
* available
*/
if ( ( status & NAND_STATUS_FAIL ) & & ( chip - > errstat ) )
status = chip - > errstat ( mtd , chip , FL_ERASING ,
status , page ) ;
2005-01-24 06:07:46 +03:00
2005-04-17 02:20:36 +04:00
/* See if block erase succeeded */
2005-01-23 21:30:53 +03:00
if ( status & NAND_STATUS_FAIL ) {
2006-05-24 14:07:37 +04:00
DEBUG ( MTD_DEBUG_LEVEL0 , " nand_erase: "
" Failed erase, page 0x%08x \n " , page ) ;
2005-04-17 02:20:36 +04:00
instr - > state = MTD_ERASE_FAILED ;
2006-05-24 14:07:37 +04:00
instr - > fail_addr = ( page < < chip - > page_shift ) ;
2005-04-17 02:20:36 +04:00
goto erase_exit ;
}
2005-01-17 21:35:25 +03:00
2006-05-24 14:07:37 +04:00
/*
* If BBT requires refresh , set the BBT rewrite flag to the
* page being erased
*/
if ( bbt_masked_page ! = 0xffffffff & &
( page & BBT_PAGE_MASK ) = = bbt_masked_page )
rewrite_bbt [ chipnr ] = ( page < < chip - > page_shift ) ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Increment page address and decrement length */
2006-05-24 14:07:37 +04:00
len - = ( 1 < < chip - > phys_erase_shift ) ;
2005-04-17 02:20:36 +04:00
page + = pages_per_block ;
/* Check, if we cross a chip boundary */
2006-05-24 14:07:37 +04:00
if ( len & & ! ( page & chip - > pagemask ) ) {
2005-04-17 02:20:36 +04:00
chipnr + + ;
2006-05-24 14:07:37 +04:00
chip - > select_chip ( mtd , - 1 ) ;
chip - > select_chip ( mtd , chipnr ) ;
2005-01-17 21:35:25 +03:00
2006-05-24 14:07:37 +04:00
/*
* If BBT requires refresh and BBT - PERCHIP , set the BBT
* page mask to see if this BBT should be rewritten
*/
if ( bbt_masked_page ! = 0xffffffff & &
( chip - > bbt_td - > options & NAND_BBT_PERCHIP ) )
bbt_masked_page = chip - > bbt_td - > pages [ chipnr ] &
BBT_PAGE_MASK ;
2005-04-17 02:20:36 +04:00
}
}
instr - > state = MTD_ERASE_DONE ;
2006-05-13 21:07:53 +04:00
erase_exit :
2005-04-17 02:20:36 +04:00
ret = instr - > state = = MTD_ERASE_DONE ? 0 : - EIO ;
/* Do call back function */
if ( ! ret )
mtd_erase_callback ( instr ) ;
/* Deselect and wake up anyone waiting on the device */
nand_release_device ( mtd ) ;
2006-05-24 14:07:37 +04:00
/*
* If BBT requires refresh and erase was successful , rewrite any
* selected bad block tables
*/
if ( bbt_masked_page = = 0xffffffff | | ret )
return ret ;
for ( chipnr = 0 ; chipnr < chip - > numchips ; chipnr + + ) {
if ( ! rewrite_bbt [ chipnr ] )
continue ;
/* update the BBT for chip */
DEBUG ( MTD_DEBUG_LEVEL0 , " nand_erase_nand: nand_update_bbt "
" (%d:0x%0x 0x%0x) \n " , chipnr , rewrite_bbt [ chipnr ] ,
chip - > bbt_td - > pages [ chipnr ] ) ;
nand_update_bbt ( mtd , rewrite_bbt [ chipnr ] ) ;
2005-01-17 21:35:25 +03:00
}
2005-04-17 02:20:36 +04:00
/* Return more or less happy */
return ret ;
}
/**
* nand_sync - [ MTD Interface ] sync
* @ mtd : MTD device structure
*
* Sync is actually a wait for chip ready function
*/
2006-05-13 21:07:53 +04:00
static void nand_sync ( struct mtd_info * mtd )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
2006-05-13 21:07:53 +04:00
DEBUG ( MTD_DEBUG_LEVEL3 , " nand_sync: called \n " ) ;
2005-04-17 02:20:36 +04:00
/* Grab the lock and see if the device is available */
2006-05-24 14:07:37 +04:00
nand_get_device ( chip , mtd , FL_SYNCING ) ;
2005-04-17 02:20:36 +04:00
/* Release it and go back */
2006-05-13 21:07:53 +04:00
nand_release_device ( mtd ) ;
2005-04-17 02:20:36 +04:00
}
/**
2006-05-24 14:07:37 +04:00
* nand_block_isbad - [ MTD Interface ] Check if block at offset is bad
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
2006-06-29 08:48:27 +04:00
* @ offs : offset relative to mtd start
2005-04-17 02:20:36 +04:00
*/
2006-05-24 14:07:37 +04:00
static int nand_block_isbad ( struct mtd_info * mtd , loff_t offs )
2005-04-17 02:20:36 +04:00
{
/* Check for invalid offset */
2006-05-24 14:07:37 +04:00
if ( offs > mtd - > size )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2005-11-07 14:15:49 +03:00
2006-05-24 14:07:37 +04:00
return nand_block_checkbad ( mtd , offs , 1 , 0 ) ;
2005-04-17 02:20:36 +04:00
}
/**
2006-05-24 14:07:37 +04:00
* nand_block_markbad - [ MTD Interface ] Mark block at the given offset as bad
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
* @ ofs : offset relative to mtd start
*/
2006-05-13 21:07:53 +04:00
static int nand_block_markbad ( struct mtd_info * mtd , loff_t ofs )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
int ret ;
2006-05-13 21:07:53 +04:00
if ( ( ret = nand_block_isbad ( mtd , ofs ) ) ) {
/* If it was bad already, return success and do nothing. */
2005-04-17 02:20:36 +04:00
if ( ret > 0 )
return 0 ;
2006-05-13 21:07:53 +04:00
return ret ;
}
2005-04-17 02:20:36 +04:00
2006-05-24 14:07:37 +04:00
return chip - > block_markbad ( mtd , ofs ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-15 17:58:53 +04:00
/**
* nand_suspend - [ MTD Interface ] Suspend the NAND flash
* @ mtd : MTD device structure
*/
static int nand_suspend ( struct mtd_info * mtd )
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-09-15 17:58:53 +04:00
2006-05-24 14:07:37 +04:00
return nand_get_device ( chip , mtd , FL_PM_SUSPENDED ) ;
2005-09-15 17:58:53 +04:00
}
/**
* nand_resume - [ MTD Interface ] Resume the NAND flash
* @ mtd : MTD device structure
*/
static void nand_resume ( struct mtd_info * mtd )
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-09-15 17:58:53 +04:00
2006-05-24 14:07:37 +04:00
if ( chip - > state = = FL_PM_SUSPENDED )
2005-09-15 17:58:53 +04:00
nand_release_device ( mtd ) ;
else
2006-05-23 13:50:56 +04:00
printk ( KERN_ERR " nand_resume() called for a chip which is not "
" in suspended state \n " ) ;
2005-09-15 17:58:53 +04:00
}
2006-05-23 13:54:38 +04:00
/*
* Set default functions
*/
2006-05-24 14:07:37 +04:00
static void nand_set_defaults ( struct nand_chip * chip , int busw )
2006-05-23 13:54:38 +04:00
{
2005-04-17 02:20:36 +04:00
/* check for proper chip_delay setup, set 20us if not */
2006-05-24 14:07:37 +04:00
if ( ! chip - > chip_delay )
chip - > chip_delay = 20 ;
2005-04-17 02:20:36 +04:00
/* check, if a user supplied command function given */
2006-05-24 14:07:37 +04:00
if ( chip - > cmdfunc = = NULL )
chip - > cmdfunc = nand_command ;
2005-04-17 02:20:36 +04:00
/* check, if a user supplied wait function given */
2006-05-24 14:07:37 +04:00
if ( chip - > waitfunc = = NULL )
chip - > waitfunc = nand_wait ;
if ( ! chip - > select_chip )
chip - > select_chip = nand_select_chip ;
if ( ! chip - > read_byte )
chip - > read_byte = busw ? nand_read_byte16 : nand_read_byte ;
if ( ! chip - > read_word )
chip - > read_word = nand_read_word ;
if ( ! chip - > block_bad )
chip - > block_bad = nand_block_bad ;
if ( ! chip - > block_markbad )
chip - > block_markbad = nand_default_block_markbad ;
if ( ! chip - > write_buf )
chip - > write_buf = busw ? nand_write_buf16 : nand_write_buf ;
if ( ! chip - > read_buf )
chip - > read_buf = busw ? nand_read_buf16 : nand_read_buf ;
if ( ! chip - > verify_buf )
chip - > verify_buf = busw ? nand_verify_buf16 : nand_verify_buf ;
if ( ! chip - > scan_bbt )
chip - > scan_bbt = nand_default_bbt ;
2006-05-26 20:52:08 +04:00
if ( ! chip - > controller ) {
chip - > controller = & chip - > hwcontrol ;
spin_lock_init ( & chip - > controller - > lock ) ;
init_waitqueue_head ( & chip - > controller - > wq ) ;
}
2006-05-23 13:54:38 +04:00
}
/*
2006-05-24 14:07:37 +04:00
* Get the flash and manufacturer id and lookup if the type is supported
2006-05-23 13:54:38 +04:00
*/
static struct nand_flash_dev * nand_get_flash_type ( struct mtd_info * mtd ,
2006-05-24 14:07:37 +04:00
struct nand_chip * chip ,
2006-05-23 13:54:38 +04:00
int busw , int * maf_id )
{
struct nand_flash_dev * type = NULL ;
int i , dev_id , maf_idx ;
2005-04-17 02:20:36 +04:00
/* Select the device */
2006-05-24 14:07:37 +04:00
chip - > select_chip ( mtd , 0 ) ;
2005-04-17 02:20:36 +04:00
/* Send the command for reading device ID */
2006-05-24 14:07:37 +04:00
chip - > cmdfunc ( mtd , NAND_CMD_READID , 0x00 , - 1 ) ;
2005-04-17 02:20:36 +04:00
/* Read manufacturer and device IDs */
2006-05-24 14:07:37 +04:00
* maf_id = chip - > read_byte ( mtd ) ;
dev_id = chip - > read_byte ( mtd ) ;
2005-04-17 02:20:36 +04:00
2006-05-23 13:54:38 +04:00
/* Lookup the flash id */
2005-04-17 02:20:36 +04:00
for ( i = 0 ; nand_flash_ids [ i ] . name ! = NULL ; i + + ) {
2006-05-23 13:54:38 +04:00
if ( dev_id = = nand_flash_ids [ i ] . id ) {
type = & nand_flash_ids [ i ] ;
break ;
}
}
2005-11-07 14:15:49 +03:00
2006-05-23 13:54:38 +04:00
if ( ! type )
return ERR_PTR ( - ENODEV ) ;
2006-05-27 03:02:13 +04:00
if ( ! mtd - > name )
mtd - > name = type - > name ;
chip - > chipsize = type - > chipsize < < 20 ;
2006-05-23 13:54:38 +04:00
/* Newer devices have all the information in additional id bytes */
2006-05-27 03:02:13 +04:00
if ( ! type - > pagesize ) {
2006-05-23 13:54:38 +04:00
int extid ;
/* The 3rd id byte contains non relevant data ATM */
2006-05-24 14:07:37 +04:00
extid = chip - > read_byte ( mtd ) ;
2006-05-23 13:54:38 +04:00
/* The 4th id byte is the important one */
2006-05-24 14:07:37 +04:00
extid = chip - > read_byte ( mtd ) ;
2006-05-23 13:54:38 +04:00
/* Calc pagesize */
2006-05-23 14:37:31 +04:00
mtd - > writesize = 1024 < < ( extid & 0x3 ) ;
2006-05-23 13:54:38 +04:00
extid > > = 2 ;
/* Calc oobsize */
2006-05-23 14:37:31 +04:00
mtd - > oobsize = ( 8 < < ( extid & 0x01 ) ) * ( mtd - > writesize > > 9 ) ;
2006-05-23 13:54:38 +04:00
extid > > = 2 ;
/* Calc blocksize. Blocksize is multiples of 64KiB */
mtd - > erasesize = ( 64 * 1024 ) < < ( extid & 0x03 ) ;
extid > > = 2 ;
/* Get buswidth information */
busw = ( extid & 0x01 ) ? NAND_BUSWIDTH_16 : 0 ;
2005-11-07 14:15:49 +03:00
2006-05-23 13:54:38 +04:00
} else {
/*
2006-05-24 14:07:37 +04:00
* Old devices have chip data hardcoded in the device id table
2006-05-23 13:54:38 +04:00
*/
2006-05-27 03:02:13 +04:00
mtd - > erasesize = type - > erasesize ;
mtd - > writesize = type - > pagesize ;
2006-05-23 14:37:31 +04:00
mtd - > oobsize = mtd - > writesize / 32 ;
2006-05-27 03:02:13 +04:00
busw = type - > options & NAND_BUSWIDTH_16 ;
2006-05-23 13:54:38 +04:00
}
2005-04-17 02:20:36 +04:00
2006-05-23 13:54:38 +04:00
/* Try to identify manufacturer */
2006-07-15 16:26:18 +04:00
for ( maf_idx = 0 ; nand_manuf_ids [ maf_idx ] . id ! = 0x0 ; maf_idx + + ) {
2006-05-23 13:54:38 +04:00
if ( nand_manuf_ids [ maf_idx ] . id = = * maf_id )
break ;
}
2005-02-16 12:39:39 +03:00
2006-05-23 13:54:38 +04:00
/*
* Check , if buswidth is correct . Hardware drivers should set
2006-05-24 14:07:37 +04:00
* chip correct !
2006-05-23 13:54:38 +04:00
*/
2006-05-24 14:07:37 +04:00
if ( busw ! = ( chip - > options & NAND_BUSWIDTH_16 ) ) {
2006-05-23 13:54:38 +04:00
printk ( KERN_INFO " NAND device: Manufacturer ID: "
" 0x%02x, Chip ID: 0x%02x (%s %s) \n " , * maf_id ,
dev_id , nand_manuf_ids [ maf_idx ] . name , mtd - > name ) ;
printk ( KERN_WARNING " NAND bus width %d instead %d bit \n " ,
2006-05-24 14:07:37 +04:00
( chip - > options & NAND_BUSWIDTH_16 ) ? 16 : 8 ,
2006-05-23 13:54:38 +04:00
busw ? 16 : 8 ) ;
return ERR_PTR ( - EINVAL ) ;
}
2005-11-07 14:15:49 +03:00
2006-05-23 13:54:38 +04:00
/* Calculate the address shift from the page size */
2006-05-24 14:07:37 +04:00
chip - > page_shift = ffs ( mtd - > writesize ) - 1 ;
2006-05-23 13:54:38 +04:00
/* Convert chipsize to number of pages per chip -1. */
2006-05-24 14:07:37 +04:00
chip - > pagemask = ( chip - > chipsize > > chip - > page_shift ) - 1 ;
2005-11-07 14:15:49 +03:00
2006-05-24 14:07:37 +04:00
chip - > bbt_erase_shift = chip - > phys_erase_shift =
2006-05-23 13:54:38 +04:00
ffs ( mtd - > erasesize ) - 1 ;
2006-05-24 14:07:37 +04:00
chip - > chip_shift = ffs ( chip - > chipsize ) - 1 ;
2005-04-17 02:20:36 +04:00
2006-05-23 13:54:38 +04:00
/* Set the bad block position */
2006-05-24 14:07:37 +04:00
chip - > badblockpos = mtd - > writesize > 512 ?
2006-05-23 13:54:38 +04:00
NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS ;
2005-11-07 14:15:49 +03:00
2006-05-23 13:54:38 +04:00
/* Get chip options, preserve non chip based options */
2006-05-24 14:07:37 +04:00
chip - > options & = ~ NAND_CHIPOPTIONS_MSK ;
2006-05-27 03:02:13 +04:00
chip - > options | = type - > options & NAND_CHIPOPTIONS_MSK ;
2006-05-23 13:54:38 +04:00
/*
2006-05-24 14:07:37 +04:00
* Set chip as a default . Board drivers can override it , if necessary
2006-05-23 13:54:38 +04:00
*/
2006-05-24 14:07:37 +04:00
chip - > options | = NAND_NO_AUTOINCR ;
2006-05-23 13:54:38 +04:00
2006-05-24 14:07:37 +04:00
/* Check if chip is a not a samsung device. Do not clear the
2006-05-23 13:54:38 +04:00
* options for chips which are not having an extended id .
*/
2006-05-27 03:02:13 +04:00
if ( * maf_id ! = NAND_MFR_SAMSUNG & & ! type - > pagesize )
2006-05-24 14:07:37 +04:00
chip - > options & = ~ NAND_SAMSUNG_LP_OPTIONS ;
2006-05-23 13:54:38 +04:00
/* Check for AND chips with 4 page planes */
2006-05-24 14:07:37 +04:00
if ( chip - > options & NAND_4PAGE_ARRAY )
chip - > erase_cmd = multi_erase_cmd ;
2006-05-23 13:54:38 +04:00
else
2006-05-24 14:07:37 +04:00
chip - > erase_cmd = single_erase_cmd ;
2006-05-23 13:54:38 +04:00
/* Do not replace user supplied command function ! */
2006-05-24 14:07:37 +04:00
if ( mtd - > writesize > 512 & & chip - > cmdfunc = = nand_command )
chip - > cmdfunc = nand_command_lp ;
2006-05-23 13:54:38 +04:00
printk ( KERN_INFO " NAND device: Manufacturer ID: "
" 0x%02x, Chip ID: 0x%02x (%s %s) \n " , * maf_id , dev_id ,
nand_manuf_ids [ maf_idx ] . name , type - > name ) ;
return type ;
}
/**
2006-09-25 20:06:53 +04:00
* nand_scan_ident - [ NAND Interface ] Scan for the NAND device
* @ mtd : MTD device structure
* @ maxchips : Number of chips to scan for
2006-05-23 13:54:38 +04:00
*
2006-09-25 20:06:53 +04:00
* This is the first phase of the normal nand_scan ( ) function . It
* reads the flash ID and sets up MTD fields accordingly .
2006-05-23 13:54:38 +04:00
*
2006-09-25 20:06:53 +04:00
* The mtd - > owner field must be set to the module of the caller .
2006-05-23 13:54:38 +04:00
*/
2006-09-25 20:06:53 +04:00
int nand_scan_ident ( struct mtd_info * mtd , int maxchips )
2006-05-23 13:54:38 +04:00
{
int i , busw , nand_maf_id ;
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2006-05-23 13:54:38 +04:00
struct nand_flash_dev * type ;
/* Get buswidth to select the correct functions */
2006-05-24 14:07:37 +04:00
busw = chip - > options & NAND_BUSWIDTH_16 ;
2006-05-23 13:54:38 +04:00
/* Set the default functions */
2006-05-24 14:07:37 +04:00
nand_set_defaults ( chip , busw ) ;
2006-05-23 13:54:38 +04:00
/* Read the flash type */
2006-05-24 14:07:37 +04:00
type = nand_get_flash_type ( mtd , chip , busw , & nand_maf_id ) ;
2006-05-23 13:54:38 +04:00
if ( IS_ERR ( type ) ) {
2006-05-13 21:07:53 +04:00
printk ( KERN_WARNING " No NAND device found!!! \n " ) ;
2006-05-24 14:07:37 +04:00
chip - > select_chip ( mtd , - 1 ) ;
2006-05-23 13:54:38 +04:00
return PTR_ERR ( type ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-23 13:54:38 +04:00
/* Check for a chip array */
2006-05-13 21:07:53 +04:00
for ( i = 1 ; i < maxchips ; i + + ) {
2006-05-24 14:07:37 +04:00
chip - > select_chip ( mtd , i ) ;
2005-04-17 02:20:36 +04:00
/* Send the command for reading device ID */
2006-05-24 14:07:37 +04:00
chip - > cmdfunc ( mtd , NAND_CMD_READID , 0x00 , - 1 ) ;
2005-04-17 02:20:36 +04:00
/* Read manufacturer and device IDs */
2006-05-24 14:07:37 +04:00
if ( nand_maf_id ! = chip - > read_byte ( mtd ) | |
type - > id ! = chip - > read_byte ( mtd ) )
2005-04-17 02:20:36 +04:00
break ;
}
if ( i > 1 )
printk ( KERN_INFO " %d NAND chips detected \n " , i ) ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Store the number of chips and calc total size for mtd */
2006-05-24 14:07:37 +04:00
chip - > numchips = i ;
mtd - > size = i * chip - > chipsize ;
2006-05-23 13:54:38 +04:00
2006-09-25 20:06:53 +04:00
return 0 ;
}
/**
* nand_scan_tail - [ NAND Interface ] Scan for the NAND device
* @ mtd : MTD device structure
* @ maxchips : Number of chips to scan for
*
* This is the second phase of the normal nand_scan ( ) function . It
* fills out all the uninitialized function pointers with the defaults
* and scans for a bad block table if appropriate .
*/
int nand_scan_tail ( struct mtd_info * mtd )
{
int i ;
struct nand_chip * chip = mtd - > priv ;
2006-09-25 20:08:04 +04:00
if ( ! ( chip - > options & NAND_OWN_BUFFERS ) )
chip - > buffers = kmalloc ( sizeof ( * chip - > buffers ) , GFP_KERNEL ) ;
if ( ! chip - > buffers )
return - ENOMEM ;
2006-05-26 20:52:08 +04:00
/* Preset the internal oob write buffer */
2006-09-25 20:08:04 +04:00
memset ( chip - > buffers - > oobwbuf , 0xff , mtd - > oobsize ) ;
2005-04-17 02:20:36 +04:00
2006-05-23 13:54:38 +04:00
/*
* If no default placement scheme is given , select an appropriate one
*/
2006-05-28 00:16:10 +04:00
if ( ! chip - > ecc . layout ) {
2005-11-07 14:15:49 +03:00
switch ( mtd - > oobsize ) {
2005-04-17 02:20:36 +04:00
case 8 :
2006-05-28 00:16:10 +04:00
chip - > ecc . layout = & nand_oob_8 ;
2005-04-17 02:20:36 +04:00
break ;
case 16 :
2006-05-28 00:16:10 +04:00
chip - > ecc . layout = & nand_oob_16 ;
2005-04-17 02:20:36 +04:00
break ;
case 64 :
2006-05-28 00:16:10 +04:00
chip - > ecc . layout = & nand_oob_64 ;
2005-04-17 02:20:36 +04:00
break ;
default :
2006-05-23 13:54:38 +04:00
printk ( KERN_WARNING " No oob scheme defined for "
" oobsize %d \n " , mtd - > oobsize ) ;
2005-04-17 02:20:36 +04:00
BUG ( ) ;
}
}
2005-11-07 14:15:49 +03:00
2006-09-25 20:12:39 +04:00
if ( ! chip - > write_page )
chip - > write_page = nand_write_page ;
2005-11-07 14:15:49 +03:00
/*
2006-05-23 13:54:38 +04:00
* check ECC mode , default to software if 3 byte / 512 byte hardware ECC is
* selected and we have 256 byte pagesize fallback to software ECC
2006-05-13 21:07:53 +04:00
*/
2006-09-25 20:12:39 +04:00
if ( ! chip - > ecc . read_page_raw )
chip - > ecc . read_page_raw = nand_read_page_raw ;
if ( ! chip - > ecc . write_page_raw )
chip - > ecc . write_page_raw = nand_write_page_raw ;
2006-05-24 14:07:37 +04:00
switch ( chip - > ecc . mode ) {
2006-05-23 14:00:46 +04:00
case NAND_ECC_HW :
2006-05-25 12:07:16 +04:00
/* Use standard hwecc read page function ? */
if ( ! chip - > ecc . read_page )
chip - > ecc . read_page = nand_read_page_hwecc ;
2006-05-26 20:52:08 +04:00
if ( ! chip - > ecc . write_page )
chip - > ecc . write_page = nand_write_page_hwecc ;
2006-06-20 22:05:05 +04:00
if ( ! chip - > ecc . read_oob )
chip - > ecc . read_oob = nand_read_oob_std ;
if ( ! chip - > ecc . write_oob )
chip - > ecc . write_oob = nand_write_oob_std ;
2006-05-25 12:07:16 +04:00
2006-05-23 14:00:46 +04:00
case NAND_ECC_HW_SYNDROME :
2006-05-24 14:07:37 +04:00
if ( ! chip - > ecc . calculate | | ! chip - > ecc . correct | |
! chip - > ecc . hwctl ) {
2006-05-23 14:00:46 +04:00
printk ( KERN_WARNING " No ECC functions supplied, "
" Hardware ECC not possible \n " ) ;
BUG ( ) ;
}
2006-05-26 20:52:08 +04:00
/* Use standard syndrome read/write page function ? */
2006-05-25 12:07:16 +04:00
if ( ! chip - > ecc . read_page )
chip - > ecc . read_page = nand_read_page_syndrome ;
2006-05-26 20:52:08 +04:00
if ( ! chip - > ecc . write_page )
chip - > ecc . write_page = nand_write_page_syndrome ;
2006-06-20 22:05:05 +04:00
if ( ! chip - > ecc . read_oob )
chip - > ecc . read_oob = nand_read_oob_syndrome ;
if ( ! chip - > ecc . write_oob )
chip - > ecc . write_oob = nand_write_oob_syndrome ;
2006-05-25 12:07:16 +04:00
2006-05-24 14:07:37 +04:00
if ( mtd - > writesize > = chip - > ecc . size )
2006-05-23 14:00:46 +04:00
break ;
printk ( KERN_WARNING " %d byte HW ECC not possible on "
" %d byte page size, fallback to SW ECC \n " ,
2006-05-24 14:07:37 +04:00
chip - > ecc . size , mtd - > writesize ) ;
chip - > ecc . mode = NAND_ECC_SOFT ;
2005-11-07 14:15:49 +03:00
2006-05-23 14:00:46 +04:00
case NAND_ECC_SOFT :
2006-05-24 14:07:37 +04:00
chip - > ecc . calculate = nand_calculate_ecc ;
chip - > ecc . correct = nand_correct_data ;
2006-05-25 12:07:16 +04:00
chip - > ecc . read_page = nand_read_page_swecc ;
2006-05-26 20:52:08 +04:00
chip - > ecc . write_page = nand_write_page_swecc ;
2006-06-20 22:05:05 +04:00
chip - > ecc . read_oob = nand_read_oob_std ;
chip - > ecc . write_oob = nand_write_oob_std ;
2006-05-24 14:07:37 +04:00
chip - > ecc . size = 256 ;
chip - > ecc . bytes = 3 ;
2005-04-17 02:20:36 +04:00
break ;
2005-11-07 14:15:49 +03:00
case NAND_ECC_NONE :
2006-05-23 13:54:38 +04:00
printk ( KERN_WARNING " NAND_ECC_NONE selected by board driver. "
" This is not recommended !! \n " ) ;
2006-05-29 05:26:58 +04:00
chip - > ecc . read_page = nand_read_page_raw ;
chip - > ecc . write_page = nand_write_page_raw ;
2006-06-20 22:05:05 +04:00
chip - > ecc . read_oob = nand_read_oob_std ;
chip - > ecc . write_oob = nand_write_oob_std ;
2006-05-24 14:07:37 +04:00
chip - > ecc . size = mtd - > writesize ;
chip - > ecc . bytes = 0 ;
2005-04-17 02:20:36 +04:00
break ;
2006-09-25 20:12:39 +04:00
2005-04-17 02:20:36 +04:00
default :
2006-05-23 13:54:38 +04:00
printk ( KERN_WARNING " Invalid NAND_ECC_MODE %d \n " ,
2006-05-24 14:07:37 +04:00
chip - > ecc . mode ) ;
2005-11-07 14:15:49 +03:00
BUG ( ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:49 +03:00
2006-05-28 00:16:10 +04:00
/*
* The number of bytes available for a client to place data into
* the out of band area
*/
chip - > ecc . layout - > oobavail = 0 ;
for ( i = 0 ; chip - > ecc . layout - > oobfree [ i ] . length ; i + + )
chip - > ecc . layout - > oobavail + =
chip - > ecc . layout - > oobfree [ i ] . length ;
2006-05-23 13:54:38 +04:00
/*
* Set the number of read / write steps for one page depending on ECC
* mode
*/
2006-05-24 14:07:37 +04:00
chip - > ecc . steps = mtd - > writesize / chip - > ecc . size ;
if ( chip - > ecc . steps * chip - > ecc . size ! = mtd - > writesize ) {
2006-05-23 14:00:46 +04:00
printk ( KERN_WARNING " Invalid ecc parameters \n " ) ;
BUG ( ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-25 12:07:16 +04:00
chip - > ecc . total = chip - > ecc . steps * chip - > ecc . bytes ;
2005-11-07 14:15:49 +03:00
2006-05-25 11:45:29 +04:00
/* Initialize state */
2006-05-24 14:07:37 +04:00
chip - > state = FL_READY ;
2005-04-17 02:20:36 +04:00
/* De-select the device */
2006-05-24 14:07:37 +04:00
chip - > select_chip ( mtd , - 1 ) ;
2005-04-17 02:20:36 +04:00
/* Invalidate the pagebuffer reference */
2006-05-24 14:07:37 +04:00
chip - > pagebuf = - 1 ;
2005-04-17 02:20:36 +04:00
/* Fill in remaining MTD driver data */
mtd - > type = MTD_NANDFLASH ;
2006-05-23 01:18:29 +04:00
mtd - > flags = MTD_CAP_NANDFLASH ;
2005-04-17 02:20:36 +04:00
mtd - > ecctype = MTD_ECC_SW ;
mtd - > erase = nand_erase ;
mtd - > point = NULL ;
mtd - > unpoint = NULL ;
mtd - > read = nand_read ;
mtd - > write = nand_write ;
mtd - > read_oob = nand_read_oob ;
mtd - > write_oob = nand_write_oob ;
mtd - > sync = nand_sync ;
mtd - > lock = NULL ;
mtd - > unlock = NULL ;
2005-09-15 17:58:53 +04:00
mtd - > suspend = nand_suspend ;
mtd - > resume = nand_resume ;
2005-04-17 02:20:36 +04:00
mtd - > block_isbad = nand_block_isbad ;
mtd - > block_markbad = nand_block_markbad ;
2006-05-28 00:16:10 +04:00
/* propagate ecc.layout to mtd_info */
mtd - > ecclayout = chip - > ecc . layout ;
2005-04-17 02:20:36 +04:00
2005-02-09 15:20:00 +03:00
/* Check, if we should skip the bad block table scan */
2006-05-24 14:07:37 +04:00
if ( chip - > options & NAND_SKIP_BBTSCAN )
2005-02-09 15:20:00 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
/* Build bad block table */
2006-05-24 14:07:37 +04:00
return chip - > scan_bbt ( mtd ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-25 20:06:53 +04:00
/* module_text_address() isn't exported, and it's mostly a pointless
test if this is a module _anyway_ - - they ' d have to try _really_ hard
to call us from in - kernel code if the core NAND support is modular . */
# ifdef MODULE
# define caller_is_module() (1)
# else
# define caller_is_module() \
module_text_address ( ( unsigned long ) __builtin_return_address ( 0 ) )
# endif
/**
* nand_scan - [ NAND Interface ] Scan for the NAND device
* @ mtd : MTD device structure
* @ maxchips : Number of chips to scan for
*
* This fills out all the uninitialized function pointers
* with the defaults .
* The flash ID is read and the mtd / chip structures are
* filled with the appropriate values .
* The mtd - > owner field must be set to the module of the caller
*
*/
int nand_scan ( struct mtd_info * mtd , int maxchips )
{
int ret ;
/* Many callers got this wrong, so check for it for a while... */
if ( ! mtd - > owner & & caller_is_module ( ) ) {
printk ( KERN_CRIT " nand_scan() called with NULL mtd->owner! \n " ) ;
BUG ( ) ;
}
ret = nand_scan_ident ( mtd , maxchips ) ;
if ( ! ret )
ret = nand_scan_tail ( mtd ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
/**
2005-11-07 14:15:49 +03:00
* nand_release - [ NAND Interface ] Free resources held by the NAND device
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
*/
2006-05-13 21:07:53 +04:00
void nand_release ( struct mtd_info * mtd )
2005-04-17 02:20:36 +04:00
{
2006-05-24 14:07:37 +04:00
struct nand_chip * chip = mtd - > priv ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_MTD_PARTITIONS
/* Deregister partitions */
2006-05-13 21:07:53 +04:00
del_mtd_partitions ( mtd ) ;
2005-04-17 02:20:36 +04:00
# endif
/* Deregister the device */
2006-05-13 21:07:53 +04:00
del_mtd_device ( mtd ) ;
2005-04-17 02:20:36 +04:00
2005-11-07 12:01:27 +03:00
/* Free bad block table memory */
2006-05-24 14:07:37 +04:00
kfree ( chip - > bbt ) ;
2006-09-25 20:08:04 +04:00
if ( ! ( chip - > options & NAND_OWN_BUFFERS ) )
kfree ( chip - > buffers ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-13 21:07:53 +04:00
EXPORT_SYMBOL_GPL ( nand_scan ) ;
2006-09-25 20:06:53 +04:00
EXPORT_SYMBOL_GPL ( nand_scan_ident ) ;
EXPORT_SYMBOL_GPL ( nand_scan_tail ) ;
2006-05-13 21:07:53 +04:00
EXPORT_SYMBOL_GPL ( nand_release ) ;
2006-03-31 14:31:14 +04:00
static int __init nand_base_init ( void )
{
led_trigger_register_simple ( " nand-disk " , & nand_led_trigger ) ;
return 0 ;
}
static void __exit nand_base_exit ( void )
{
led_trigger_unregister_simple ( nand_led_trigger ) ;
}
module_init ( nand_base_init ) ;
module_exit ( nand_base_exit ) ;
2006-05-13 21:07:53 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de> " ) ;
MODULE_DESCRIPTION ( " Generic NAND flash driver code " ) ;