2005-04-17 02:20:36 +04:00
/*
* drivers / mtd / nand_bbt . c
*
* Overview :
* Bad block table support for the NAND driver
2005-11-07 14:15:49 +03:00
*
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 2004 Thomas Gleixner ( tglx @ linutronix . de )
*
* 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 .
*
* Description :
*
2005-11-07 14:15:49 +03:00
* When nand_scan_bbt is called , then it tries to find the bad block table
* depending on the options in the bbt descriptor ( s ) . If a bbt is found
* then the contents are read and the memory based bbt is created . If a
2005-04-17 02:20:36 +04:00
* mirrored bbt is selected then the mirror is searched too and the
2005-11-07 14:15:49 +03:00
* versions are compared . If the mirror has a greater version number
2005-04-17 02:20:36 +04:00
* than the mirror bbt is used to build the memory based bbt .
* If the tables are not versioned , then we " or " the bad block information .
2005-11-07 14:15:49 +03:00
* If one of the bbt ' s is out of date or does not exist it is ( re ) created .
* If no bbt exists at all then the device is scanned for factory marked
* good / bad blocks and the bad block tables are created .
2005-04-17 02:20:36 +04:00
*
2005-11-07 14:15:49 +03:00
* For manufacturer created bbts like the one found on M - SYS DOC devices
2005-04-17 02:20:36 +04:00
* the bbt is searched and read but never created
*
2005-11-07 14:15:49 +03:00
* The autogenerated bad block table is located in the last good blocks
* of the device . The table is mirrored , so it can be updated eventually .
* The table is marked in the oob area with an ident pattern and a version
2005-04-17 02:20:36 +04:00
* number which indicates which of both tables is more up to date .
*
* The table uses 2 bits per block
* 11 b : block is good
* 00 b : block is factory marked bad
* 01 b , 10 b : block is marked bad due to wear
*
* The memory bad block table uses the following scheme :
* 00 b : block is good
* 01 b : block is marked bad due to wear
* 10 b : block is reserved ( to protect the bbt area )
* 11 b : block is factory marked bad
2005-11-07 14:15:49 +03:00
*
2005-04-17 02:20:36 +04:00
* Multichip devices like DOC store the bad block info per floor .
*
* Following assumptions are made :
* - bbts start at a page boundary , if autolocated on a block boundary
2006-05-13 21:07:53 +04:00
* - the space necessary for a bbt in FLASH does not exceed a block boundary
2005-11-07 14:15:49 +03:00
*
2005-04-17 02:20:36 +04:00
*/
# 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/bitops.h>
# include <linux/delay.h>
2006-05-13 07:03:42 +04:00
# include <linux/vmalloc.h>
2005-04-17 02:20:36 +04:00
2005-11-07 14:15:49 +03:00
/**
2005-04-17 02:20:36 +04:00
* check_pattern - [ GENERIC ] check if a pattern is in the buffer
* @ buf : the buffer to search
* @ len : the length of buffer to search
* @ paglen : the pagelength
* @ td : search pattern descriptor
*
* Check for a pattern at the given place . Used to search bad block
* tables and good / bad block identifiers .
* If the SCAN_EMPTY option is set then check , if all bytes except the
* pattern area contain 0xff
*
*/
2006-05-13 21:07:53 +04:00
static int check_pattern ( uint8_t * buf , int len , int paglen , struct nand_bbt_descr * td )
2005-04-17 02:20:36 +04:00
{
2005-02-16 20:09:39 +03:00
int i , end = 0 ;
2005-04-17 02:20:36 +04:00
uint8_t * p = buf ;
2005-06-14 19:39:57 +04:00
end = paglen + td - > offs ;
2005-04-17 02:20:36 +04:00
if ( td - > options & NAND_BBT_SCANEMPTY ) {
for ( i = 0 ; i < end ; i + + ) {
if ( p [ i ] ! = 0xff )
return - 1 ;
}
2005-11-07 14:15:49 +03:00
}
2005-06-14 19:39:57 +04:00
p + = end ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Compare the pattern */
for ( i = 0 ; i < td - > len ; i + + ) {
if ( p [ i ] ! = td - > pattern [ i ] )
return - 1 ;
}
if ( td - > options & NAND_BBT_SCANEMPTY ) {
2005-02-16 20:09:39 +03:00
p + = td - > len ;
end + = td - > len ;
2005-04-17 02:20:36 +04:00
for ( i = end ; i < len ; i + + ) {
if ( * p + + ! = 0xff )
return - 1 ;
}
}
return 0 ;
}
2005-11-07 14:15:49 +03:00
/**
2005-06-14 19:39:57 +04:00
* check_short_pattern - [ GENERIC ] check if a pattern is in the buffer
* @ buf : the buffer to search
* @ td : search pattern descriptor
*
* Check for a pattern at the given place . Used to search bad block
2005-11-07 14:15:49 +03:00
* tables and good / bad block identifiers . Same as check_pattern , but
2005-07-15 17:53:51 +04:00
* no optional empty check
2005-06-14 19:39:57 +04:00
*
*/
2006-05-13 21:07:53 +04:00
static int check_short_pattern ( uint8_t * buf , struct nand_bbt_descr * td )
2005-06-14 19:39:57 +04:00
{
int i ;
uint8_t * p = buf ;
/* Compare the pattern */
for ( i = 0 ; i < td - > len ; i + + ) {
2005-07-15 17:53:51 +04:00
if ( p [ td - > offs + i ] ! = td - > pattern [ i ] )
2005-06-14 19:39:57 +04:00
return - 1 ;
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
/**
* read_bbt - [ GENERIC ] Read the bad block table starting from page
* @ mtd : MTD device structure
* @ buf : temporary buffer
* @ page : the starting page
* @ num : the number of bbt descriptors to read
* @ bits : number of bits per block
* @ offs : offset in the memory table
* @ reserved_block_code : Pattern to identify reserved blocks
*
* Read the bad block table starting from page .
*
*/
2006-05-13 21:07:53 +04:00
static int read_bbt ( struct mtd_info * mtd , uint8_t * buf , int page , int num ,
int bits , int offs , int reserved_block_code )
2005-04-17 02:20:36 +04:00
{
int res , i , j , act = 0 ;
struct nand_chip * this = mtd - > priv ;
size_t retlen , len , totlen ;
loff_t from ;
uint8_t msk = ( uint8_t ) ( ( 1 < < bits ) - 1 ) ;
totlen = ( num * bits ) > > 3 ;
2006-05-13 21:07:53 +04:00
from = ( ( loff_t ) page ) < < this - > page_shift ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
while ( totlen ) {
2006-05-13 21:07:53 +04:00
len = min ( totlen , ( size_t ) ( 1 < < this - > bbt_erase_shift ) ) ;
2006-05-23 19:21:03 +04:00
res = mtd - > read ( mtd , from , len , & retlen , buf ) ;
2005-04-17 02:20:36 +04:00
if ( res < 0 ) {
if ( retlen ! = len ) {
2006-05-13 21:07:53 +04:00
printk ( KERN_INFO " nand_bbt: Error reading bad block table \n " ) ;
2005-04-17 02:20:36 +04:00
return res ;
}
2006-05-13 21:07:53 +04:00
printk ( KERN_WARNING " nand_bbt: ECC error while reading bad block table \n " ) ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
/* Analyse data */
for ( i = 0 ; i < len ; i + + ) {
uint8_t dat = buf [ i ] ;
for ( j = 0 ; j < 8 ; j + = bits , act + = 2 ) {
uint8_t tmp = ( dat > > j ) & msk ;
if ( tmp = = msk )
continue ;
2006-05-13 21:07:53 +04:00
if ( reserved_block_code & & ( tmp = = reserved_block_code ) ) {
2008-12-10 16:37:21 +03:00
printk ( KERN_DEBUG " nand_read_bbt: Reserved block at 0x%012llx \n " ,
( loff_t ) ( ( offs < < 2 ) + ( act > > 1 ) ) < < this - > bbt_erase_shift ) ;
2005-04-17 02:20:36 +04:00
this - > bbt [ offs + ( act > > 3 ) ] | = 0x2 < < ( act & 0x06 ) ;
2006-05-30 02:37:34 +04:00
mtd - > ecc_stats . bbtblocks + + ;
2005-04-17 02:20:36 +04:00
continue ;
}
/* Leave it for now, if its matured we can move this
* message to MTD_DEBUG_LEVEL0 */
2008-12-10 16:37:21 +03:00
printk ( KERN_DEBUG " nand_read_bbt: Bad block at 0x%012llx \n " ,
( loff_t ) ( ( offs < < 2 ) + ( act > > 1 ) ) < < this - > bbt_erase_shift ) ;
2005-11-07 14:15:49 +03:00
/* Factory marked bad or worn out ? */
2005-04-17 02:20:36 +04:00
if ( tmp = = 0 )
this - > bbt [ offs + ( act > > 3 ) ] | = 0x3 < < ( act & 0x06 ) ;
else
this - > bbt [ offs + ( act > > 3 ) ] | = 0x1 < < ( act & 0x06 ) ;
2006-05-30 02:37:34 +04:00
mtd - > ecc_stats . badblocks + + ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
}
totlen - = len ;
from + = len ;
}
return 0 ;
}
/**
* read_abs_bbt - [ GENERIC ] Read the bad block table starting at a given page
* @ mtd : MTD device structure
* @ buf : temporary buffer
2005-11-07 14:15:49 +03:00
* @ td : descriptor for the bad block table
2005-04-17 02:20:36 +04:00
* @ chip : read the table for a specific chip , - 1 read all chips .
* Applies only if NAND_BBT_PERCHIP option is set
*
* Read the bad block table for all chips starting at a given page
* We assume that the bbt bits are in consecutive order .
*/
2006-05-13 21:07:53 +04:00
static int read_abs_bbt ( struct mtd_info * mtd , uint8_t * buf , struct nand_bbt_descr * td , int chip )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
int res = 0 , i ;
int bits ;
bits = td - > options & NAND_BBT_NRBITS_MSK ;
if ( td - > options & NAND_BBT_PERCHIP ) {
int offs = 0 ;
for ( i = 0 ; i < this - > numchips ; i + + ) {
if ( chip = = - 1 | | chip = = i )
res = read_bbt ( mtd , buf , td - > pages [ i ] , this - > chipsize > > this - > bbt_erase_shift , bits , offs , td - > reserved_block_code ) ;
if ( res )
return res ;
offs + = this - > chipsize > > ( this - > bbt_erase_shift + 2 ) ;
}
} else {
res = read_bbt ( mtd , buf , td - > pages [ 0 ] , mtd - > size > > this - > bbt_erase_shift , bits , 0 , td - > reserved_block_code ) ;
if ( res )
return res ;
}
return 0 ;
}
2006-05-29 05:26:58 +04:00
/*
* Scan read raw data from flash
*/
static int scan_read_raw ( struct mtd_info * mtd , uint8_t * buf , loff_t offs ,
size_t len )
{
struct mtd_oob_ops ops ;
ops . mode = MTD_OOB_RAW ;
ops . ooboffs = 0 ;
ops . ooblen = mtd - > oobsize ;
ops . oobbuf = buf ;
ops . datbuf = buf ;
ops . len = len ;
return mtd - > read_oob ( mtd , offs , & ops ) ;
}
/*
* Scan write data with oob to flash
*/
static int scan_write_bbt ( struct mtd_info * mtd , loff_t offs , size_t len ,
uint8_t * buf , uint8_t * oob )
{
struct mtd_oob_ops ops ;
ops . mode = MTD_OOB_PLACE ;
ops . ooboffs = 0 ;
ops . ooblen = mtd - > oobsize ;
ops . datbuf = buf ;
ops . oobbuf = oob ;
ops . len = len ;
return mtd - > write_oob ( mtd , offs , & ops ) ;
}
2005-04-17 02:20:36 +04:00
/**
* read_abs_bbts - [ GENERIC ] Read the bad block table ( s ) for all chips starting at a given page
* @ mtd : MTD device structure
* @ buf : temporary buffer
2005-11-07 14:15:49 +03:00
* @ td : descriptor for the bad block table
2005-04-17 02:20:36 +04:00
* @ md : descriptor for the bad block table mirror
*
* Read the bad block table ( s ) for all chips starting at a given page
* We assume that the bbt bits are in consecutive order .
*
*/
2006-05-29 05:26:58 +04:00
static int read_abs_bbts ( struct mtd_info * mtd , uint8_t * buf ,
struct nand_bbt_descr * td , struct nand_bbt_descr * md )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
2005-11-07 14:15:49 +03:00
/* Read the primary version, if available */
2005-04-17 02:20:36 +04:00
if ( td - > options & NAND_BBT_VERSION ) {
2008-12-10 16:37:21 +03:00
scan_read_raw ( mtd , buf , ( loff_t ) td - > pages [ 0 ] < < this - > page_shift ,
2006-05-29 05:26:58 +04:00
mtd - > writesize ) ;
2006-05-23 01:18:05 +04:00
td - > version [ 0 ] = buf [ mtd - > writesize + td - > veroffs ] ;
2006-05-29 05:26:58 +04:00
printk ( KERN_DEBUG " Bad block table at page %d, version 0x%02X \n " ,
td - > pages [ 0 ] , td - > version [ 0 ] ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:49 +03:00
/* Read the mirror version, if available */
2005-04-17 02:20:36 +04:00
if ( md & & ( md - > options & NAND_BBT_VERSION ) ) {
2008-12-10 16:37:21 +03:00
scan_read_raw ( mtd , buf , ( loff_t ) md - > pages [ 0 ] < < this - > page_shift ,
2006-05-29 05:26:58 +04:00
mtd - > writesize ) ;
2006-05-23 01:18:05 +04:00
md - > version [ 0 ] = buf [ mtd - > writesize + md - > veroffs ] ;
2006-05-29 05:26:58 +04:00
printk ( KERN_DEBUG " Bad block table at page %d, version 0x%02X \n " ,
md - > pages [ 0 ] , md - > version [ 0 ] ) ;
2005-04-17 02:20:36 +04:00
}
return 1 ;
}
2006-05-29 05:26:58 +04:00
/*
* Scan a given block full
*/
static int scan_block_full ( struct mtd_info * mtd , struct nand_bbt_descr * bd ,
loff_t offs , uint8_t * buf , size_t readlen ,
int scanlen , int len )
{
int ret , j ;
ret = scan_read_raw ( mtd , buf , offs , readlen ) ;
if ( ret )
return ret ;
for ( j = 0 ; j < len ; j + + , buf + = scanlen ) {
if ( check_pattern ( buf , scanlen , mtd - > writesize , bd ) )
return 1 ;
}
return 0 ;
}
/*
* Scan a given block partially
*/
static int scan_block_fast ( struct mtd_info * mtd , struct nand_bbt_descr * bd ,
loff_t offs , uint8_t * buf , int len )
{
struct mtd_oob_ops ops ;
int j , ret ;
ops . ooblen = mtd - > oobsize ;
ops . oobbuf = buf ;
ops . ooboffs = 0 ;
ops . datbuf = NULL ;
ops . mode = MTD_OOB_PLACE ;
for ( j = 0 ; j < len ; j + + ) {
/*
* Read the full oob until read_oob is fixed to
* handle single byte reads for 16 bit
* buswidth
*/
ret = mtd - > read_oob ( mtd , offs , & ops ) ;
if ( ret )
return ret ;
if ( check_short_pattern ( buf , bd ) )
return 1 ;
offs + = mtd - > writesize ;
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
/**
* create_bbt - [ GENERIC ] Create a bad block table by scanning the device
* @ mtd : MTD device structure
* @ buf : temporary buffer
* @ bd : descriptor for the good / bad block search pattern
* @ chip : create the table for a specific chip , - 1 read all chips .
* Applies only if NAND_BBT_PERCHIP option is set
*
* Create a bad block table by scanning the device
* for the given good / bad block identify pattern
*/
2006-05-29 05:26:58 +04:00
static int create_bbt ( struct mtd_info * mtd , uint8_t * buf ,
struct nand_bbt_descr * bd , int chip )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
2006-05-29 05:26:58 +04:00
int i , numblocks , len , scanlen ;
2005-04-17 02:20:36 +04:00
int startblock ;
loff_t from ;
2006-05-29 05:26:58 +04:00
size_t readlen ;
2005-04-17 02:20:36 +04:00
2006-05-13 21:07:53 +04:00
printk ( KERN_INFO " Scanning device for bad blocks \n " ) ;
2005-04-17 02:20:36 +04:00
if ( bd - > options & NAND_BBT_SCANALLPAGES )
len = 1 < < ( this - > bbt_erase_shift - this - > page_shift ) ;
else {
if ( bd - > options & NAND_BBT_SCAN2NDPAGE )
len = 2 ;
2005-11-07 14:15:49 +03:00
else
2005-04-17 02:20:36 +04:00
len = 1 ;
}
2005-02-16 20:09:39 +03:00
if ( ! ( bd - > options & NAND_BBT_SCANEMPTY ) ) {
/* We need only read few bytes from the OOB area */
2006-05-29 05:26:58 +04:00
scanlen = 0 ;
2005-02-11 13:14:15 +03:00
readlen = bd - > len ;
} else {
2005-02-16 20:09:39 +03:00
/* Full page content should be read */
2006-05-23 01:18:05 +04:00
scanlen = mtd - > writesize + mtd - > oobsize ;
readlen = len * mtd - > writesize ;
2005-02-11 13:14:15 +03:00
}
2005-04-17 02:20:36 +04:00
if ( chip = = - 1 ) {
2006-05-29 05:26:58 +04:00
/* Note that numblocks is 2 * (real numblocks) here, see i+=2
* below as it makes shifting and masking less painful */
2005-04-17 02:20:36 +04:00
numblocks = mtd - > size > > ( this - > bbt_erase_shift - 1 ) ;
startblock = 0 ;
from = 0 ;
} else {
if ( chip > = this - > numchips ) {
2006-05-13 21:07:53 +04:00
printk ( KERN_WARNING " create_bbt(): chipnr (%d) > available chips (%d) \n " ,
chip + 1 , this - > numchips ) ;
2005-02-16 20:09:39 +03:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
numblocks = this - > chipsize > > ( this - > bbt_erase_shift - 1 ) ;
startblock = chip * numblocks ;
numblocks + = startblock ;
2008-12-10 16:37:21 +03:00
from = ( loff_t ) startblock < < ( this - > bbt_erase_shift - 1 ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
for ( i = startblock ; i < numblocks ; ) {
2005-02-11 13:14:15 +03:00
int ret ;
2005-11-07 14:15:49 +03:00
2006-05-29 05:26:58 +04:00
if ( bd - > options & NAND_BBT_SCANALLPAGES )
ret = scan_block_full ( mtd , bd , from , buf , readlen ,
scanlen , len ) ;
else
ret = scan_block_fast ( mtd , bd , from , buf , len ) ;
if ( ret < 0 )
return ret ;
if ( ret ) {
this - > bbt [ i > > 3 ] | = 0x03 < < ( i & 0x6 ) ;
2008-12-10 16:37:21 +03:00
printk ( KERN_WARNING " Bad eraseblock %d at 0x%012llx \n " ,
i > > 1 , ( unsigned long long ) from ) ;
2006-05-30 02:37:34 +04:00
mtd - > ecc_stats . badblocks + + ;
2005-04-17 02:20:36 +04:00
}
2006-05-29 05:26:58 +04:00
2005-04-17 02:20:36 +04:00
i + = 2 ;
from + = ( 1 < < this - > bbt_erase_shift ) ;
}
2005-02-11 13:14:15 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/**
* search_bbt - [ GENERIC ] scan the device for a specific bad block table
* @ mtd : MTD device structure
* @ buf : temporary buffer
* @ td : descriptor for the bad block table
*
* Read the bad block table by searching for a given ident pattern .
2005-11-07 14:15:49 +03:00
* Search is preformed either from the beginning up or from the end of
2005-04-17 02:20:36 +04:00
* the device downwards . The search starts always at the start of a
* block .
2005-11-07 14:15:49 +03:00
* If the option NAND_BBT_PERCHIP is given , each chip is searched
2005-04-17 02:20:36 +04:00
* for a bbt , which contains the bad block information of this chip .
2006-05-13 21:07:53 +04:00
* This is necessary to provide support for certain DOC devices .
2005-04-17 02:20:36 +04:00
*
2005-11-07 14:15:49 +03:00
* The bbt ident pattern resides in the oob area of the first page
* in a block .
2005-04-17 02:20:36 +04:00
*/
2006-05-13 21:07:53 +04:00
static int search_bbt ( struct mtd_info * mtd , uint8_t * buf , struct nand_bbt_descr * td )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
int i , chips ;
int bits , startblock , block , dir ;
2006-05-23 01:18:05 +04:00
int scanlen = mtd - > writesize + mtd - > oobsize ;
2005-04-17 02:20:36 +04:00
int bbtblocks ;
2006-05-29 05:26:58 +04:00
int blocktopage = this - > bbt_erase_shift - this - > page_shift ;
2005-04-17 02:20:36 +04:00
/* Search direction top -> down ? */
if ( td - > options & NAND_BBT_LASTBLOCK ) {
2006-05-13 21:07:53 +04:00
startblock = ( mtd - > size > > this - > bbt_erase_shift ) - 1 ;
2005-04-17 02:20:36 +04:00
dir = - 1 ;
} else {
2005-11-07 14:15:49 +03:00
startblock = 0 ;
2005-04-17 02:20:36 +04:00
dir = 1 ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
/* Do we have a bbt per chip ? */
if ( td - > options & NAND_BBT_PERCHIP ) {
chips = this - > numchips ;
bbtblocks = this - > chipsize > > this - > bbt_erase_shift ;
startblock & = bbtblocks - 1 ;
} else {
chips = 1 ;
bbtblocks = mtd - > size > > this - > bbt_erase_shift ;
}
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Number of bits for each erase block in the bbt */
bits = td - > options & NAND_BBT_NRBITS_MSK ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < chips ; i + + ) {
/* Reset version information */
2005-11-07 14:15:49 +03:00
td - > version [ i ] = 0 ;
2005-04-17 02:20:36 +04:00
td - > pages [ i ] = - 1 ;
/* Scan the maximum number of blocks */
for ( block = 0 ; block < td - > maxblocks ; block + + ) {
2006-05-29 05:26:58 +04:00
2005-04-17 02:20:36 +04:00
int actblock = startblock + dir * block ;
2008-12-10 16:37:21 +03:00
loff_t offs = ( loff_t ) actblock < < this - > bbt_erase_shift ;
2006-05-29 05:26:58 +04:00
2005-04-17 02:20:36 +04:00
/* Read first page */
2006-05-29 05:26:58 +04:00
scan_read_raw ( mtd , buf , offs , mtd - > writesize ) ;
2006-05-23 01:18:05 +04:00
if ( ! check_pattern ( buf , scanlen , mtd - > writesize , td ) ) {
2006-05-29 05:26:58 +04:00
td - > pages [ i ] = actblock < < blocktopage ;
2005-04-17 02:20:36 +04:00
if ( td - > options & NAND_BBT_VERSION ) {
2006-05-23 01:18:05 +04:00
td - > version [ i ] = buf [ mtd - > writesize + td - > veroffs ] ;
2005-04-17 02:20:36 +04:00
}
break ;
}
}
startblock + = this - > chipsize > > this - > bbt_erase_shift ;
}
/* Check, if we found a bbt for each requested chip */
for ( i = 0 ; i < chips ; i + + ) {
if ( td - > pages [ i ] = = - 1 )
2006-05-13 21:07:53 +04:00
printk ( KERN_WARNING " Bad block table not found for chip %d \n " , i ) ;
2005-04-17 02:20:36 +04:00
else
2006-05-13 21:07:53 +04:00
printk ( KERN_DEBUG " Bad block table found at page %d, version 0x%02X \n " , td - > pages [ i ] ,
td - > version [ i ] ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:49 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/**
* search_read_bbts - [ GENERIC ] scan the device for bad block table ( s )
* @ mtd : MTD device structure
* @ buf : temporary buffer
2005-11-07 14:15:49 +03:00
* @ td : descriptor for the bad block table
2005-04-17 02:20:36 +04:00
* @ md : descriptor for the bad block table mirror
*
* Search and read the bad block table ( s )
*/
2006-05-13 21:07:53 +04:00
static int search_read_bbts ( struct mtd_info * mtd , uint8_t * buf , struct nand_bbt_descr * td , struct nand_bbt_descr * md )
2005-04-17 02:20:36 +04:00
{
/* Search the primary table */
2006-05-13 21:07:53 +04:00
search_bbt ( mtd , buf , td ) ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Search the mirror table */
if ( md )
2006-05-13 21:07:53 +04:00
search_bbt ( mtd , buf , md ) ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Force result check */
2005-11-07 14:15:49 +03:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:49 +03:00
/**
2005-04-17 02:20:36 +04:00
* write_bbt - [ GENERIC ] ( Re ) write the bad block table
*
* @ mtd : MTD device structure
* @ buf : temporary buffer
2005-11-07 14:15:49 +03:00
* @ td : descriptor for the bad block table
2005-04-17 02:20:36 +04:00
* @ md : descriptor for the bad block table mirror
* @ chipsel : selector for a specific chip , - 1 for all
*
* ( Re ) write the bad block table
*
*/
2006-05-13 21:07:53 +04:00
static int write_bbt ( struct mtd_info * mtd , uint8_t * buf ,
2006-05-23 19:21:03 +04:00
struct nand_bbt_descr * td , struct nand_bbt_descr * md ,
int chipsel )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
struct erase_info einfo ;
int i , j , res , chip = 0 ;
int bits , startblock , dir , page , offs , numblocks , sft , sftmsk ;
2006-05-23 19:21:03 +04:00
int nrchips , bbtoffs , pageoffs , ooboffs ;
2005-04-17 02:20:36 +04:00
uint8_t msk [ 4 ] ;
uint8_t rcode = td - > reserved_block_code ;
2006-05-29 05:26:58 +04:00
size_t retlen , len = 0 ;
2005-04-17 02:20:36 +04:00
loff_t to ;
2006-05-29 05:26:58 +04:00
struct mtd_oob_ops ops ;
ops . ooblen = mtd - > oobsize ;
ops . ooboffs = 0 ;
ops . datbuf = NULL ;
ops . mode = MTD_OOB_PLACE ;
2005-04-17 02:20:36 +04:00
if ( ! rcode )
rcode = 0xff ;
/* Write bad block table per chip rather than per device ? */
if ( td - > options & NAND_BBT_PERCHIP ) {
2006-05-13 21:07:53 +04:00
numblocks = ( int ) ( this - > chipsize > > this - > bbt_erase_shift ) ;
2005-11-07 14:15:49 +03:00
/* Full device write or specific chip ? */
2005-04-17 02:20:36 +04:00
if ( chipsel = = - 1 ) {
nrchips = this - > numchips ;
} else {
nrchips = chipsel + 1 ;
chip = chipsel ;
}
} else {
2006-05-13 21:07:53 +04:00
numblocks = ( int ) ( mtd - > size > > this - > bbt_erase_shift ) ;
2005-04-17 02:20:36 +04:00
nrchips = 1 ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
/* Loop through the chips */
for ( ; chip < nrchips ; chip + + ) {
2005-11-07 14:15:49 +03:00
/* There was already a version of the table, reuse the page
* This applies for absolute placement too , as we have the
2005-04-17 02:20:36 +04:00
* page nr . in td - > pages .
*/
if ( td - > pages [ chip ] ! = - 1 ) {
page = td - > pages [ chip ] ;
goto write ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
/* Automatic placement of the bad block table */
/* Search direction top -> down ? */
if ( td - > options & NAND_BBT_LASTBLOCK ) {
startblock = numblocks * ( chip + 1 ) - 1 ;
dir = - 1 ;
} else {
startblock = chip * numblocks ;
dir = 1 ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < td - > maxblocks ; i + + ) {
int block = startblock + dir * i ;
/* Check, if the block is bad */
2006-05-23 19:21:03 +04:00
switch ( ( this - > bbt [ block > > 2 ] > >
( 2 * ( block & 0x03 ) ) ) & 0x03 ) {
2005-04-17 02:20:36 +04:00
case 0x01 :
case 0x03 :
continue ;
}
2006-05-23 19:21:03 +04:00
page = block < <
( this - > bbt_erase_shift - this - > page_shift ) ;
2005-04-17 02:20:36 +04:00
/* Check, if the block is used by the mirror table */
if ( ! md | | md - > pages [ chip ] ! = page )
goto write ;
}
2006-05-13 21:07:53 +04:00
printk ( KERN_ERR " No space left to write bad block table \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOSPC ;
2006-05-13 21:07:53 +04:00
write :
2005-04-17 02:20:36 +04:00
/* Set up shift count and masks for the flash table */
bits = td - > options & NAND_BBT_NRBITS_MSK ;
2006-05-23 19:21:03 +04:00
msk [ 2 ] = ~ rcode ;
2005-04-17 02:20:36 +04:00
switch ( bits ) {
2006-05-23 19:21:03 +04:00
case 1 : sft = 3 ; sftmsk = 0x07 ; msk [ 0 ] = 0x00 ; msk [ 1 ] = 0x01 ;
msk [ 3 ] = 0x01 ;
break ;
case 2 : sft = 2 ; sftmsk = 0x06 ; msk [ 0 ] = 0x00 ; msk [ 1 ] = 0x01 ;
msk [ 3 ] = 0x03 ;
break ;
case 4 : sft = 1 ; sftmsk = 0x04 ; msk [ 0 ] = 0x00 ; msk [ 1 ] = 0x0C ;
msk [ 3 ] = 0x0f ;
break ;
case 8 : sft = 0 ; sftmsk = 0x00 ; msk [ 0 ] = 0x00 ; msk [ 1 ] = 0x0F ;
msk [ 3 ] = 0xff ;
break ;
2005-04-17 02:20:36 +04:00
default : return - EINVAL ;
}
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
bbtoffs = chip * ( numblocks > > 2 ) ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
to = ( ( loff_t ) page ) < < this - > page_shift ;
/* Must we save the block contents ? */
if ( td - > options & NAND_BBT_SAVECONTENT ) {
/* Make it block aligned */
to & = ~ ( ( loff_t ) ( ( 1 < < this - > bbt_erase_shift ) - 1 ) ) ;
len = 1 < < this - > bbt_erase_shift ;
2006-05-23 19:21:03 +04:00
res = mtd - > read ( mtd , to , len , & retlen , buf ) ;
2005-04-17 02:20:36 +04:00
if ( res < 0 ) {
if ( retlen ! = len ) {
2006-05-23 19:21:03 +04:00
printk ( KERN_INFO " nand_bbt: Error "
" reading block for writing "
" the bad block table \n " ) ;
2005-04-17 02:20:36 +04:00
return res ;
}
2006-05-23 19:21:03 +04:00
printk ( KERN_WARNING " nand_bbt: ECC error "
" while reading block for writing "
" bad block table \n " ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-23 19:21:03 +04:00
/* Read oob data */
2006-11-03 18:20:38 +03:00
ops . ooblen = ( len > > this - > page_shift ) * mtd - > oobsize ;
2006-05-29 05:26:58 +04:00
ops . oobbuf = & buf [ len ] ;
res = mtd - > read_oob ( mtd , to + mtd - > writesize , & ops ) ;
2006-11-03 18:20:38 +03:00
if ( res < 0 | | ops . oobretlen ! = ops . ooblen )
2006-05-23 19:21:03 +04:00
goto outerr ;
2005-04-17 02:20:36 +04:00
/* Calc the byte offset in the buffer */
pageoffs = page - ( int ) ( to > > this - > page_shift ) ;
offs = pageoffs < < this - > page_shift ;
/* Preset the bbt area with 0xff */
2006-05-13 21:07:53 +04:00
memset ( & buf [ offs ] , 0xff , ( size_t ) ( numblocks > > sft ) ) ;
2006-05-23 19:21:03 +04:00
ooboffs = len + ( pageoffs * mtd - > oobsize ) ;
2005-04-17 02:20:36 +04:00
} else {
/* Calc length */
len = ( size_t ) ( numblocks > > sft ) ;
/* Make it page aligned ! */
2006-05-23 19:21:03 +04:00
len = ( len + ( mtd - > writesize - 1 ) ) &
~ ( mtd - > writesize - 1 ) ;
2005-04-17 02:20:36 +04:00
/* Preset the buffer with 0xff */
2006-05-23 19:21:03 +04:00
memset ( buf , 0xff , len +
( len > > this - > page_shift ) * mtd - > oobsize ) ;
2005-04-17 02:20:36 +04:00
offs = 0 ;
2006-05-23 19:21:03 +04:00
ooboffs = len ;
2005-04-17 02:20:36 +04:00
/* Pattern is located in oob area of first page */
2006-05-23 19:21:03 +04:00
memcpy ( & buf [ ooboffs + td - > offs ] , td - > pattern , td - > len ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:49 +03:00
2006-05-23 19:21:03 +04:00
if ( td - > options & NAND_BBT_VERSION )
buf [ ooboffs + td - > veroffs ] = td - > version [ chip ] ;
2005-04-17 02:20:36 +04:00
/* walk through the memory table */
2006-05-13 21:07:53 +04:00
for ( i = 0 ; i < numblocks ; ) {
2005-04-17 02:20:36 +04:00
uint8_t dat ;
dat = this - > bbt [ bbtoffs + ( i > > 2 ) ] ;
2006-05-13 21:07:53 +04:00
for ( j = 0 ; j < 4 ; j + + , i + + ) {
2005-04-17 02:20:36 +04:00
int sftcnt = ( i < < ( 3 - sft ) ) & sftmsk ;
/* Do not store the reserved bbt blocks ! */
2006-05-23 19:21:03 +04:00
buf [ offs + ( i > > sft ) ] & =
~ ( msk [ dat & 0x03 ] < < sftcnt ) ;
2005-04-17 02:20:36 +04:00
dat > > = 2 ;
}
}
2005-11-07 14:15:49 +03:00
2006-05-13 21:07:53 +04:00
memset ( & einfo , 0 , sizeof ( einfo ) ) ;
2005-04-17 02:20:36 +04:00
einfo . mtd = mtd ;
2008-12-10 16:37:21 +03:00
einfo . addr = to ;
2005-04-17 02:20:36 +04:00
einfo . len = 1 < < this - > bbt_erase_shift ;
2006-05-13 21:07:53 +04:00
res = nand_erase_nand ( mtd , & einfo , 1 ) ;
2006-05-23 19:21:03 +04:00
if ( res < 0 )
goto outerr ;
2005-11-07 14:15:49 +03:00
2006-05-29 05:26:58 +04:00
res = scan_write_bbt ( mtd , to , len , buf , & buf [ len ] ) ;
2006-05-23 19:21:03 +04:00
if ( res < 0 )
goto outerr ;
2008-12-10 16:37:21 +03:00
printk ( KERN_DEBUG " Bad block table written to 0x%012llx, version "
" 0x%02X \n " , ( unsigned long long ) to , td - > version [ chip ] ) ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Mark it as used */
td - > pages [ chip ] = page ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
2006-05-23 19:21:03 +04:00
outerr :
printk ( KERN_WARNING
" nand_bbt: Error while writing bad block table %d \n " , res ) ;
return res ;
2005-04-17 02:20:36 +04:00
}
/**
* nand_memory_bbt - [ GENERIC ] create a memory based bad block table
* @ mtd : MTD device structure
* @ bd : descriptor for the good / bad block search pattern
*
2005-11-07 14:15:49 +03:00
* The function creates a memory based bbt by scanning the device
2005-04-17 02:20:36 +04:00
* for manufacturer / software marked good / bad blocks
*/
2006-05-13 21:07:53 +04:00
static inline int nand_memory_bbt ( struct mtd_info * mtd , struct nand_bbt_descr * bd )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
2005-02-16 20:09:39 +03:00
bd - > options & = ~ NAND_BBT_SCANEMPTY ;
2006-09-25 20:08:04 +04:00
return create_bbt ( mtd , this - > buffers - > databuf , bd , - 1 ) ;
2005-04-17 02:20:36 +04:00
}
/**
2006-05-13 21:07:53 +04:00
* check_create - [ GENERIC ] create and write bbt ( s ) if necessary
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
* @ buf : temporary buffer
* @ bd : descriptor for the good / bad block search pattern
*
* The function checks the results of the previous call to read_bbt
2006-05-13 21:07:53 +04:00
* and creates / updates the bbt ( s ) if necessary
* Creation is necessary if no bbt was found for the chip / device
* Update is necessary if one of the tables is missing or the
2005-04-17 02:20:36 +04:00
* version nr . of one table is less than the other
*/
2006-05-13 21:07:53 +04:00
static int check_create ( struct mtd_info * mtd , uint8_t * buf , struct nand_bbt_descr * bd )
2005-04-17 02:20:36 +04:00
{
int i , chips , writeops , chipsel , res ;
struct nand_chip * this = mtd - > priv ;
struct nand_bbt_descr * td = this - > bbt_td ;
struct nand_bbt_descr * md = this - > bbt_md ;
struct nand_bbt_descr * rd , * rd2 ;
/* Do we have a bbt per chip ? */
2005-11-07 14:15:49 +03:00
if ( td - > options & NAND_BBT_PERCHIP )
2005-04-17 02:20:36 +04:00
chips = this - > numchips ;
2005-11-07 14:15:49 +03:00
else
2005-04-17 02:20:36 +04:00
chips = 1 ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < chips ; i + + ) {
writeops = 0 ;
rd = NULL ;
rd2 = NULL ;
/* Per chip or per device ? */
chipsel = ( td - > options & NAND_BBT_PERCHIP ) ? i : - 1 ;
/* Mirrored table avilable ? */
if ( md ) {
if ( td - > pages [ i ] = = - 1 & & md - > pages [ i ] = = - 1 ) {
writeops = 0x03 ;
goto create ;
}
if ( td - > pages [ i ] = = - 1 ) {
2005-11-07 14:15:49 +03:00
rd = md ;
2005-04-17 02:20:36 +04:00
td - > version [ i ] = md - > version [ i ] ;
writeops = 1 ;
goto writecheck ;
}
if ( md - > pages [ i ] = = - 1 ) {
rd = td ;
md - > version [ i ] = td - > version [ i ] ;
writeops = 2 ;
goto writecheck ;
}
if ( td - > version [ i ] = = md - > version [ i ] ) {
rd = td ;
if ( ! ( td - > options & NAND_BBT_VERSION ) )
rd2 = md ;
goto writecheck ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
if ( ( ( int8_t ) ( td - > version [ i ] - md - > version [ i ] ) ) > 0 ) {
rd = td ;
md - > version [ i ] = td - > version [ i ] ;
writeops = 2 ;
} else {
rd = md ;
td - > version [ i ] = md - > version [ i ] ;
writeops = 1 ;
}
goto writecheck ;
} else {
if ( td - > pages [ i ] = = - 1 ) {
writeops = 0x01 ;
goto create ;
}
rd = td ;
goto writecheck ;
}
2006-05-13 21:07:53 +04:00
create :
2005-04-17 02:20:36 +04:00
/* Create the bad block table by scanning the device ? */
if ( ! ( td - > options & NAND_BBT_CREATE ) )
2005-11-07 14:15:49 +03:00
continue ;
2005-04-17 02:20:36 +04:00
/* Create the table in memory by scanning the chip(s) */
2006-05-13 21:07:53 +04:00
create_bbt ( mtd , buf , bd , chipsel ) ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
td - > version [ i ] = 1 ;
if ( md )
2005-11-07 14:15:49 +03:00
md - > version [ i ] = 1 ;
2006-05-13 21:07:53 +04:00
writecheck :
2005-04-17 02:20:36 +04:00
/* read back first ? */
if ( rd )
2006-05-13 21:07:53 +04:00
read_abs_bbt ( mtd , buf , rd , chipsel ) ;
2005-04-17 02:20:36 +04:00
/* If they weren't versioned, read both. */
if ( rd2 )
2006-05-13 21:07:53 +04:00
read_abs_bbt ( mtd , buf , rd2 , chipsel ) ;
2005-04-17 02:20:36 +04:00
/* Write the bad block table to the device ? */
if ( ( writeops & 0x01 ) & & ( td - > options & NAND_BBT_WRITE ) ) {
2006-05-13 21:07:53 +04:00
res = write_bbt ( mtd , buf , td , md , chipsel ) ;
2005-04-17 02:20:36 +04:00
if ( res < 0 )
return res ;
}
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Write the mirror bad block table to the device ? */
if ( ( writeops & 0x02 ) & & md & & ( md - > options & NAND_BBT_WRITE ) ) {
2006-05-13 21:07:53 +04:00
res = write_bbt ( mtd , buf , md , td , chipsel ) ;
2005-04-17 02:20:36 +04:00
if ( res < 0 )
return res ;
}
}
2005-11-07 14:15:49 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/**
2005-11-07 14:15:49 +03:00
* mark_bbt_regions - [ GENERIC ] mark the bad block table regions
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
* @ td : bad block table descriptor
*
* The bad block table regions are marked as " bad " to prevent
* accidental erasures / writes . The regions are identified by
* the mark 0x02 .
*/
2006-05-13 21:07:53 +04:00
static void mark_bbt_region ( struct mtd_info * mtd , struct nand_bbt_descr * td )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
int i , j , chips , block , nrblocks , update ;
uint8_t oldval , newval ;
/* Do we have a bbt per chip ? */
if ( td - > options & NAND_BBT_PERCHIP ) {
chips = this - > numchips ;
nrblocks = ( int ) ( this - > chipsize > > this - > bbt_erase_shift ) ;
} else {
chips = 1 ;
nrblocks = ( int ) ( mtd - > size > > this - > bbt_erase_shift ) ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < chips ; i + + ) {
if ( ( td - > options & NAND_BBT_ABSPAGE ) | |
! ( td - > options & NAND_BBT_WRITE ) ) {
2006-05-13 21:07:53 +04:00
if ( td - > pages [ i ] = = - 1 )
continue ;
2005-04-17 02:20:36 +04:00
block = td - > pages [ i ] > > ( this - > bbt_erase_shift - this - > page_shift ) ;
2005-11-07 14:15:49 +03:00
block < < = 1 ;
2005-04-17 02:20:36 +04:00
oldval = this - > bbt [ ( block > > 3 ) ] ;
newval = oldval | ( 0x2 < < ( block & 0x06 ) ) ;
this - > bbt [ ( block > > 3 ) ] = newval ;
if ( ( oldval ! = newval ) & & td - > reserved_block_code )
2008-12-10 16:37:21 +03:00
nand_update_bbt ( mtd , ( loff_t ) block < < ( this - > bbt_erase_shift - 1 ) ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
update = 0 ;
if ( td - > options & NAND_BBT_LASTBLOCK )
block = ( ( i + 1 ) * nrblocks ) - td - > maxblocks ;
2005-11-07 14:15:49 +03:00
else
2005-04-17 02:20:36 +04:00
block = i * nrblocks ;
2005-11-07 14:15:49 +03:00
block < < = 1 ;
2005-04-17 02:20:36 +04:00
for ( j = 0 ; j < td - > maxblocks ; j + + ) {
oldval = this - > bbt [ ( block > > 3 ) ] ;
newval = oldval | ( 0x2 < < ( block & 0x06 ) ) ;
this - > bbt [ ( block > > 3 ) ] = newval ;
2006-05-13 21:07:53 +04:00
if ( oldval ! = newval )
update = 1 ;
2005-04-17 02:20:36 +04:00
block + = 2 ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
/* If we want reserved blocks to be recorded to flash, and some
new ones have been marked , then we need to update the stored
bbts . This should only happen once . */
if ( update & & td - > reserved_block_code )
2008-12-10 16:37:21 +03:00
nand_update_bbt ( mtd , ( loff_t ) ( block - 2 ) < < ( this - > bbt_erase_shift - 1 ) ) ;
2005-04-17 02:20:36 +04:00
}
}
/**
* nand_scan_bbt - [ NAND Interface ] scan , find , read and maybe create bad block table ( s )
* @ mtd : MTD device structure
* @ bd : descriptor for the good / bad block search pattern
*
2005-11-07 14:15:49 +03:00
* The function checks , if a bad block table ( s ) is / are already
2005-04-17 02:20:36 +04:00
* available . If not it scans the device for manufacturer
* marked good / bad blocks and writes the bad block table ( s ) to
* the selected place .
*
* The bad block table memory is allocated here . It must be freed
* by calling the nand_free_bbt function .
*
*/
2006-05-13 21:07:53 +04:00
int nand_scan_bbt ( struct mtd_info * mtd , struct nand_bbt_descr * bd )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
int len , res = 0 ;
uint8_t * buf ;
struct nand_bbt_descr * td = this - > bbt_td ;
struct nand_bbt_descr * md = this - > bbt_md ;
len = mtd - > size > > ( this - > bbt_erase_shift + 2 ) ;
2006-11-15 22:10:29 +03:00
/* Allocate memory (2bit per block) and clear the memory bad block table */
this - > bbt = kzalloc ( len , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! this - > bbt ) {
2006-05-13 21:07:53 +04:00
printk ( KERN_ERR " nand_scan_bbt: Out of memory \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
/* If no primary table decriptor is given, scan the device
* to build a memory based bad block table
*/
2005-02-11 13:14:15 +03:00
if ( ! td ) {
if ( ( res = nand_memory_bbt ( mtd , bd ) ) ) {
2006-05-13 21:07:53 +04:00
printk ( KERN_ERR " nand_bbt: Can't scan flash and build the RAM-based BBT \n " ) ;
kfree ( this - > bbt ) ;
2005-02-11 13:14:15 +03:00
this - > bbt = NULL ;
}
return res ;
}
2005-04-17 02:20:36 +04:00
/* Allocate a temporary buffer for one eraseblock incl. oob */
len = ( 1 < < this - > bbt_erase_shift ) ;
len + = ( len > > this - > page_shift ) * mtd - > oobsize ;
2006-05-13 07:03:42 +04:00
buf = vmalloc ( len ) ;
2005-04-17 02:20:36 +04:00
if ( ! buf ) {
2006-05-13 21:07:53 +04:00
printk ( KERN_ERR " nand_bbt: Out of memory \n " ) ;
kfree ( this - > bbt ) ;
2005-04-17 02:20:36 +04:00
this - > bbt = NULL ;
return - ENOMEM ;
}
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Is the bbt at a given page ? */
if ( td - > options & NAND_BBT_ABSPAGE ) {
2006-05-13 21:07:53 +04:00
res = read_abs_bbts ( mtd , buf , td , md ) ;
2005-11-07 14:15:49 +03:00
} else {
2005-04-17 02:20:36 +04:00
/* Search the bad block table using a pattern in oob */
2006-05-13 21:07:53 +04:00
res = search_read_bbts ( mtd , buf , td , md ) ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
2005-11-07 14:15:49 +03:00
if ( res )
2006-05-13 21:07:53 +04:00
res = check_create ( mtd , buf , bd ) ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Prevent the bbt regions from erasing / writing */
2006-05-13 21:07:53 +04:00
mark_bbt_region ( mtd , td ) ;
2005-04-17 02:20:36 +04:00
if ( md )
2006-05-13 21:07:53 +04:00
mark_bbt_region ( mtd , md ) ;
2005-11-07 14:15:49 +03:00
2006-05-13 21:07:53 +04:00
vfree ( buf ) ;
2005-04-17 02:20:36 +04:00
return res ;
}
/**
2005-11-07 14:15:49 +03:00
* nand_update_bbt - [ NAND Interface ] update bad block table ( s )
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
* @ offs : the offset of the newly marked block
*
* The function updates the bad block table ( s )
*/
2006-05-13 21:07:53 +04:00
int nand_update_bbt ( struct mtd_info * mtd , loff_t offs )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
int len , res = 0 , writeops = 0 ;
int chip , chipsel ;
uint8_t * buf ;
struct nand_bbt_descr * td = this - > bbt_td ;
struct nand_bbt_descr * md = this - > bbt_md ;
if ( ! this - > bbt | | ! td )
return - EINVAL ;
/* Allocate a temporary buffer for one eraseblock incl. oob */
len = ( 1 < < this - > bbt_erase_shift ) ;
len + = ( len > > this - > page_shift ) * mtd - > oobsize ;
2006-05-13 21:07:53 +04:00
buf = kmalloc ( len , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! buf ) {
2006-05-13 21:07:53 +04:00
printk ( KERN_ERR " nand_update_bbt: Out of memory \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
writeops = md ! = NULL ? 0x03 : 0x01 ;
/* Do we have a bbt per chip ? */
if ( td - > options & NAND_BBT_PERCHIP ) {
2006-05-13 21:07:53 +04:00
chip = ( int ) ( offs > > this - > chip_shift ) ;
2005-04-17 02:20:36 +04:00
chipsel = chip ;
} else {
chip = 0 ;
chipsel = - 1 ;
}
td - > version [ chip ] + + ;
if ( md )
2005-11-07 14:15:49 +03:00
md - > version [ chip ] + + ;
2005-04-17 02:20:36 +04:00
/* Write the bad block table to the device ? */
if ( ( writeops & 0x01 ) & & ( td - > options & NAND_BBT_WRITE ) ) {
2006-05-13 21:07:53 +04:00
res = write_bbt ( mtd , buf , td , md , chipsel ) ;
2005-04-17 02:20:36 +04:00
if ( res < 0 )
goto out ;
}
/* Write the mirror bad block table to the device ? */
if ( ( writeops & 0x02 ) & & md & & ( md - > options & NAND_BBT_WRITE ) ) {
2006-05-13 21:07:53 +04:00
res = write_bbt ( mtd , buf , md , td , chipsel ) ;
2005-04-17 02:20:36 +04:00
}
2006-05-13 21:07:53 +04:00
out :
kfree ( buf ) ;
2005-04-17 02:20:36 +04:00
return res ;
}
2005-11-07 14:15:49 +03:00
/* Define some generic bad / good block scan pattern which are used
2005-02-16 20:09:39 +03:00
* while scanning a device for factory marked good / bad blocks . */
2005-04-17 02:20:36 +04:00
static uint8_t scan_ff_pattern [ ] = { 0xff , 0xff } ;
static struct nand_bbt_descr smallpage_memorybased = {
2005-02-16 20:09:39 +03:00
. options = NAND_BBT_SCAN2NDPAGE ,
2005-04-17 02:20:36 +04:00
. offs = 5 ,
. len = 1 ,
. pattern = scan_ff_pattern
} ;
static struct nand_bbt_descr largepage_memorybased = {
. options = 0 ,
. offs = 0 ,
. len = 2 ,
. pattern = scan_ff_pattern
} ;
static struct nand_bbt_descr smallpage_flashbased = {
2006-05-13 19:14:26 +04:00
. options = NAND_BBT_SCAN2NDPAGE ,
2005-04-17 02:20:36 +04:00
. offs = 5 ,
. len = 1 ,
. pattern = scan_ff_pattern
} ;
static struct nand_bbt_descr largepage_flashbased = {
2006-05-13 19:14:26 +04:00
. options = NAND_BBT_SCAN2NDPAGE ,
2005-04-17 02:20:36 +04:00
. offs = 0 ,
. len = 2 ,
. pattern = scan_ff_pattern
} ;
static uint8_t scan_agand_pattern [ ] = { 0x1C , 0x71 , 0xC7 , 0x1C , 0x71 , 0xC7 } ;
static struct nand_bbt_descr agand_flashbased = {
. options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES ,
. offs = 0x20 ,
. len = 6 ,
. pattern = scan_agand_pattern
} ;
/* Generic flash bbt decriptors
*/
static uint8_t bbt_pattern [ ] = { ' B ' , ' b ' , ' t ' , ' 0 ' } ;
static uint8_t mirror_pattern [ ] = { ' 1 ' , ' t ' , ' b ' , ' B ' } ;
static struct nand_bbt_descr bbt_main_descr = {
2005-11-07 14:15:49 +03:00
. options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
2005-04-17 02:20:36 +04:00
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP ,
. offs = 8 ,
. len = 4 ,
. veroffs = 12 ,
. maxblocks = 4 ,
. pattern = bbt_pattern
} ;
static struct nand_bbt_descr bbt_mirror_descr = {
2005-11-07 14:15:49 +03:00
. options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
2005-04-17 02:20:36 +04:00
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP ,
. offs = 8 ,
. len = 4 ,
. veroffs = 12 ,
. maxblocks = 4 ,
. pattern = mirror_pattern
} ;
/**
2005-11-07 14:15:49 +03:00
* nand_default_bbt - [ NAND Interface ] Select a default bad block table for the device
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
*
* This function selects the default bad block table
* support for the device and calls the nand_scan_bbt function
*
*/
2006-05-13 21:07:53 +04:00
int nand_default_bbt ( struct mtd_info * mtd )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
2005-11-07 14:15:49 +03:00
/* Default for AG-AND. We must use a flash based
2005-04-17 02:20:36 +04:00
* bad block table as the devices have factory marked
* _good_ blocks . Erasing those blocks leads to loss
* of the good / bad information , so we _must_ store
2005-11-07 14:15:49 +03:00
* this information in a good / bad table during
2005-04-17 02:20:36 +04:00
* startup
2006-05-13 21:07:53 +04:00
*/
2005-04-17 02:20:36 +04:00
if ( this - > options & NAND_IS_AND ) {
/* Use the default pattern descriptors */
2005-11-07 14:15:49 +03:00
if ( ! this - > bbt_td ) {
2005-04-17 02:20:36 +04:00
this - > bbt_td = & bbt_main_descr ;
this - > bbt_md = & bbt_mirror_descr ;
2005-11-07 14:15:49 +03:00
}
2005-04-17 02:20:36 +04:00
this - > options | = NAND_USE_FLASH_BBT ;
2006-05-13 21:07:53 +04:00
return nand_scan_bbt ( mtd , & agand_flashbased ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Is a flash based bad block table requested ? */
if ( this - > options & NAND_USE_FLASH_BBT ) {
2005-11-07 14:15:49 +03:00
/* Use the default pattern descriptors */
if ( ! this - > bbt_td ) {
2005-04-17 02:20:36 +04:00
this - > bbt_td = & bbt_main_descr ;
this - > bbt_md = & bbt_mirror_descr ;
}
if ( ! this - > badblock_pattern ) {
2006-05-23 01:18:05 +04:00
this - > badblock_pattern = ( mtd - > writesize > 512 ) ? & largepage_flashbased : & smallpage_flashbased ;
2005-04-17 02:20:36 +04:00
}
} else {
this - > bbt_td = NULL ;
this - > bbt_md = NULL ;
if ( ! this - > badblock_pattern ) {
2006-05-23 01:18:05 +04:00
this - > badblock_pattern = ( mtd - > writesize > 512 ) ?
2006-05-13 21:07:53 +04:00
& largepage_memorybased : & smallpage_memorybased ;
2005-04-17 02:20:36 +04:00
}
}
2006-05-13 21:07:53 +04:00
return nand_scan_bbt ( mtd , this - > badblock_pattern ) ;
2005-04-17 02:20:36 +04:00
}
/**
2005-11-07 14:15:49 +03:00
* nand_isbad_bbt - [ NAND Interface ] Check if a block is bad
2005-04-17 02:20:36 +04:00
* @ mtd : MTD device structure
* @ offs : offset in the device
* @ allowbbt : allow access to bad block table region
*
*/
2006-05-13 21:07:53 +04:00
int nand_isbad_bbt ( struct mtd_info * mtd , loff_t offs , int allowbbt )
2005-04-17 02:20:36 +04:00
{
struct nand_chip * this = mtd - > priv ;
int block ;
2006-05-13 21:07:53 +04:00
uint8_t res ;
2005-11-07 14:15:49 +03:00
2005-04-17 02:20:36 +04:00
/* Get block number * 2 */
2006-05-13 21:07:53 +04:00
block = ( int ) ( offs > > ( this - > bbt_erase_shift - 1 ) ) ;
2005-04-17 02:20:36 +04:00
res = ( this - > bbt [ block > > 3 ] > > ( block & 0x06 ) ) & 0x03 ;
2006-05-13 21:07:53 +04:00
DEBUG ( MTD_DEBUG_LEVEL2 , " nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x \n " ,
( unsigned int ) offs , block > > 1 , res ) ;
2005-04-17 02:20:36 +04:00
switch ( ( int ) res ) {
2006-05-13 21:07:53 +04:00
case 0x00 :
return 0 ;
case 0x01 :
return 1 ;
case 0x02 :
return allowbbt ? 0 : 1 ;
2005-04-17 02:20:36 +04:00
}
return 1 ;
}
2006-05-13 21:07:53 +04:00
EXPORT_SYMBOL ( nand_scan_bbt ) ;
EXPORT_SYMBOL ( nand_default_bbt ) ;