@ -13,27 +13,36 @@
* Description :
* Description :
*
*
* When nand_scan_bbt is called , then it tries to find the bad block table
* 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
* depending on the options in the BBT descriptor ( s ) . If no flash based BBT
* then the contents are read and the memory based bbt is created . If a
* ( NAND_USE_FLASH_BBT ) is specified then the device is scanned for factory
* mirrored bbt is selected then the mirror is searched too and the
* marked good / bad blocks . This information is used to create a memory BBT .
* versions are compared . If the mirror has a greater version number
* Once a new bad block is discovered then the " factory " information is updated
* than the mirror bbt is used to build the memory based bbt .
* on the device .
* If a flash based BBT is specified then the function first tries to find the
* BBT on flash . If a BBT is found then the contents are read and the memory
* based BBT is created . If a mirrored BBT is selected then the mirror is
* searched too and the versions are compared . If the mirror has a greater
* version number 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 .
* If the tables are not versioned , then we " or " the bad block information .
* If one of the bbt ' s is out of date or does not exist it is ( re ) created .
* 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
* If no BBT exists at all then the device is scanned for factory marked
* good / bad blocks and the bad block tables are created .
* good / bad blocks and the bad block tables are created .
*
*
* For manufacturer created bbt s like the one found on M - SYS DOC devices
* For manufacturer created BBT s like the one found on M - SYS DOC devices
* the bbt is searched and read but never created
* the BBT is searched and read but never created
*
*
* The auto generated bad block table is located in the last good blocks
* The auto generated bad block table is located in the last good blocks
* of the device . The table is mirrored , so it can be updated eventually .
* 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
* The table is marked in the OOB area with an ident pattern and a version
* number which indicates which of both tables is more up to date .
* number which indicates which of both tables is more up to date . If the NAND
* controller needs the complete OOB area for the ECC information then the
* option NAND_USE_FLASH_BBT_NO_OOB should be used : it moves the ident pattern
* and the version byte into the data area and the OOB area will remain
* untouched .
*
*
* The table uses 2 bits per block
* The table uses 2 bits per block
* 11 b : block is good
* 11 b : block is good
* 00 b : block is factory marked bad
* 00 b : block is factory marked bad
* 01 b , 10 b : block is marked bad due to wear
* 01 b , 10 b : block is marked bad due to wear
*
*
* The memory bad block table uses the following scheme :
* The memory bad block table uses the following scheme :
@ -55,9 +64,21 @@
# include <linux/mtd/compat.h>
# include <linux/mtd/compat.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/nand.h>
# include <linux/mtd/nand.h>
# include <linux/mtd/nand_ecc.h>
# include <linux/bitops.h>
# include <asm/errno.h>
# include <asm/errno.h>
static int check_pattern_no_oob ( uint8_t * buf , struct nand_bbt_descr * td )
{
int ret ;
ret = memcmp ( buf , td - > pattern , td - > len ) ;
if ( ! ret )
return ret ;
return - 1 ;
}
/**
/**
* check_pattern - [ GENERIC ] check if a pattern is in the buffer
* check_pattern - [ GENERIC ] check if a pattern is in the buffer
* @ buf : the buffer to search
* @ buf : the buffer to search
@ -76,6 +97,9 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc
int i , end = 0 ;
int i , end = 0 ;
uint8_t * p = buf ;
uint8_t * p = buf ;
if ( td - > options & NAND_BBT_NO_OOB )
return check_pattern_no_oob ( buf , td ) ;
end = paglen + td - > offs ;
end = paglen + td - > offs ;
if ( td - > options & NAND_BBT_SCANEMPTY ) {
if ( td - > options & NAND_BBT_SCANEMPTY ) {
for ( i = 0 ; i < end ; i + + ) {
for ( i = 0 ; i < end ; i + + ) {
@ -91,6 +115,28 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc
return - 1 ;
return - 1 ;
}
}
/* Check both positions 1 and 6 for pattern? */
if ( td - > options & NAND_BBT_SCANBYTE1AND6 ) {
if ( td - > options & NAND_BBT_SCANEMPTY ) {
p + = td - > len ;
end + = NAND_SMALL_BADBLOCK_POS - td - > offs ;
/* Check region between positions 1 and 6 */
for ( i = 0 ; i < NAND_SMALL_BADBLOCK_POS - td - > offs - td - > len ;
i + + ) {
if ( * p + + ! = 0xff )
return - 1 ;
}
}
else {
p + = NAND_SMALL_BADBLOCK_POS - td - > offs ;
}
/* Compare the pattern */
for ( i = 0 ; i < td - > len ; i + + ) {
if ( p [ i ] ! = td - > pattern [ i ] )
return - 1 ;
}
}
if ( td - > options & NAND_BBT_SCANEMPTY ) {
if ( td - > options & NAND_BBT_SCANEMPTY ) {
p + = td - > len ;
p + = td - > len ;
end + = td - > len ;
end + = td - > len ;
@ -122,36 +168,74 @@ static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
if ( p [ td - > offs + i ] ! = td - > pattern [ i ] )
if ( p [ td - > offs + i ] ! = td - > pattern [ i ] )
return - 1 ;
return - 1 ;
}
}
/* Need to check location 1 AND 6? */
if ( td - > options & NAND_BBT_SCANBYTE1AND6 ) {
for ( i = 0 ; i < td - > len ; i + + ) {
if ( p [ NAND_SMALL_BADBLOCK_POS + i ] ! = td - > pattern [ i ] )
return - 1 ;
}
}
return 0 ;
return 0 ;
}
}
/**
/**
* add_marker_len - compute the length of the marker in data area
* @ td : BBT descriptor used for computation
*
* The length will be 0 if the markeris located in OOB area .
*/
static u32 add_marker_len ( struct nand_bbt_descr * td )
{
u32 len ;
if ( ! ( td - > options & NAND_BBT_NO_OOB ) )
return 0 ;
len = td - > len ;
if ( td - > options & NAND_BBT_VERSION )
len + + ;
return len ;
}
/**
* read_bbt - [ GENERIC ] Read the bad block table starting from page
* read_bbt - [ GENERIC ] Read the bad block table starting from page
* @ mtd : MTD device structure
* @ mtd : MTD device structure
* @ buf : temporary buffer
* @ buf : temporary buffer
* @ page : the starting page
* @ page : the starting page
* @ num : the number of bbt descriptors to read
* @ num : the number of bbt descriptors to read
* @ bits : number of bits per block
* @ td : the bbt describtion table
* @ offs : offset in the memory table
* @ offs : offset in the memory table
* @ reserved_block_code : Pattern to identify reserved blocks
*
*
* Read the bad block table starting from page .
* Read the bad block table starting from page .
*
*
*/
*/
static int read_bbt ( struct mtd_info * mtd , uint8_t * buf , int page , int num ,
static int read_bbt ( struct mtd_info * mtd , uint8_t * buf , int page , int num ,
int bits , int offs , int reserved_block_code )
struct nand_bbt_descr * td , int offs )
{
{
int res , i , j , act = 0 ;
int res , i , j , act = 0 ;
struct nand_chip * this = mtd - > priv ;
struct nand_chip * this = mtd - > priv ;
size_t retlen , len , totlen ;
size_t retlen , len , totlen ;
loff_t from ;
loff_t from ;
int bits = td - > options & NAND_BBT_NRBITS_MSK ;
uint8_t msk = ( uint8_t ) ( ( 1 < < bits ) - 1 ) ;
uint8_t msk = ( uint8_t ) ( ( 1 < < bits ) - 1 ) ;
u32 marker_len ;
int reserved_block_code = td - > reserved_block_code ;
totlen = ( num * bits ) > > 3 ;
totlen = ( num * bits ) > > 3 ;
marker_len = add_marker_len ( td ) ;
from = ( ( loff_t ) page ) < < this - > page_shift ;
from = ( ( loff_t ) page ) < < this - > page_shift ;
while ( totlen ) {
while ( totlen ) {
len = min ( totlen , ( size_t ) ( 1 < < this - > bbt_erase_shift ) ) ;
len = min ( totlen , ( size_t ) ( 1 < < this - > bbt_erase_shift ) ) ;
if ( marker_len ) {
/*
* In case the BBT marker is not in the OOB area it
* will be just in the first page .
*/
len - = marker_len ;
from + = marker_len ;
marker_len = 0 ;
}
res = mtd - > read ( mtd , from , len , & retlen , buf ) ;
res = mtd - > read ( mtd , from , len , & retlen , buf ) ;
if ( res < 0 ) {
if ( res < 0 ) {
if ( retlen ! = len ) {
if ( retlen ! = len ) {
@ -170,9 +254,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
continue ;
continue ;
if ( reserved_block_code & & ( tmp = = reserved_block_code ) ) {
if ( reserved_block_code & & ( tmp = = reserved_block_code ) ) {
printk ( KERN_DEBUG " nand_read_bbt: Reserved block at 0x%012llx \n " ,
printk ( KERN_DEBUG " nand_read_bbt: Reserved block at 0x%012llx \n " ,
( loff_t ) ( ( offs < < 2 ) +
( loff_t ) ( ( offs < < 2 ) + ( act > > 1 ) ) < < this - > bbt_erase_shift ) ;
( act > > 1 ) ) < <
this - > bbt_erase_shift ) ;
this - > bbt [ offs + ( act > > 3 ) ] | = 0x2 < < ( act & 0x06 ) ;
this - > bbt [ offs + ( act > > 3 ) ] | = 0x2 < < ( act & 0x06 ) ;
mtd - > ecc_stats . bbtblocks + + ;
mtd - > ecc_stats . bbtblocks + + ;
continue ;
continue ;
@ -180,8 +262,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
/* Leave it for now, if its matured we can move this
/* Leave it for now, if its matured we can move this
* message to MTD_DEBUG_LEVEL0 */
* message to MTD_DEBUG_LEVEL0 */
printk ( KERN_DEBUG " nand_read_bbt: Bad block at 0x%012llx \n " ,
printk ( KERN_DEBUG " nand_read_bbt: Bad block at 0x%012llx \n " ,
( loff_t ) ( ( offs < < 2 ) + ( act > > 1 ) ) < <
( loff_t ) ( ( offs < < 2 ) + ( act > > 1 ) ) < < this - > bbt_erase_shift ) ;
this - > bbt_erase_shift ) ;
/* Factory marked bad or worn out ? */
/* Factory marked bad or worn out ? */
if ( tmp = = 0 )
if ( tmp = = 0 )
this - > bbt [ offs + ( act > > 3 ) ] | = 0x3 < < ( act & 0x06 ) ;
this - > bbt [ offs + ( act > > 3 ) ] | = 0x3 < < ( act & 0x06 ) ;
@ -211,20 +292,21 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
{
{
struct nand_chip * this = mtd - > priv ;
struct nand_chip * this = mtd - > priv ;
int res = 0 , i ;
int res = 0 , i ;
int bits ;
bits = td - > options & NAND_BBT_NRBITS_MSK ;
if ( td - > options & NAND_BBT_PERCHIP ) {
if ( td - > options & NAND_BBT_PERCHIP ) {
int offs = 0 ;
int offs = 0 ;
for ( i = 0 ; i < this - > numchips ; i + + ) {
for ( i = 0 ; i < this - > numchips ; i + + ) {
if ( chip = = - 1 | | chip = = 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 ) ;
res = read_bbt ( mtd , buf , td - > pages [ i ] ,
this - > chipsize > > this - > bbt_erase_shift ,
td , offs ) ;
if ( res )
if ( res )
return res ;
return res ;
offs + = this - > chipsize > > ( this - > bbt_erase_shift + 2 ) ;
offs + = this - > chipsize > > ( this - > bbt_erase_shift + 2 ) ;
}
}
} else {
} else {
res = read_bbt ( mtd , buf , td - > pages [ 0 ] , mtd - > size > > this - > bbt_erase_shift , bits , 0 , td - > reserved_block_code ) ;
res = read_bbt ( mtd , buf , td - > pages [ 0 ] ,
mtd - > size > > this - > bbt_erase_shift , td , 0 ) ;
if ( res )
if ( res )
return res ;
return res ;
}
}
@ -232,21 +314,64 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
}
}
/*
/*
* BBT marker is in the first page , no OOB .
*/
static int scan_read_raw_data ( struct mtd_info * mtd , uint8_t * buf , loff_t offs ,
struct nand_bbt_descr * td )
{
size_t retlen ;
size_t len ;
len = td - > len ;
if ( td - > options & NAND_BBT_VERSION )
len + + ;
return mtd - > read ( mtd , offs , len , & retlen , buf ) ;
}
/*
* Scan read raw data from flash
* Scan read raw data from flash
*/
*/
static int scan_read_raw ( struct mtd_info * mtd , uint8_t * buf , loff_t offs ,
static int scan_read_raw_oob ( struct mtd_info * mtd , uint8_t * buf , loff_t offs ,
size_t len )
size_t len )
{
{
struct mtd_oob_ops ops ;
struct mtd_oob_ops ops ;
int res ;
ops . mode = MTD_OOB_RAW ;
ops . mode = MTD_OOB_RAW ;
ops . ooboffs = 0 ;
ops . ooboffs = 0 ;
ops . ooblen = mtd - > oobsize ;
ops . ooblen = mtd - > oobsize ;
ops . oobbuf = buf ;
ops . datbuf = buf ;
ops . len = len ;
return mtd - > read_oob ( mtd , offs , & ops ) ;
while ( len > 0 ) {
if ( len < = mtd - > writesize ) {
ops . oobbuf = buf + len ;
ops . datbuf = buf ;
ops . len = len ;
return mtd - > read_oob ( mtd , offs , & ops ) ;
} else {
ops . oobbuf = buf + mtd - > writesize ;
ops . datbuf = buf ;
ops . len = mtd - > writesize ;
res = mtd - > read_oob ( mtd , offs , & ops ) ;
if ( res )
return res ;
}
buf + = mtd - > oobsize + mtd - > writesize ;
len - = mtd - > writesize ;
}
return 0 ;
}
static int scan_read_raw ( struct mtd_info * mtd , uint8_t * buf , loff_t offs ,
size_t len , struct nand_bbt_descr * td )
{
if ( td - > options & NAND_BBT_NO_OOB )
return scan_read_raw_data ( mtd , buf , offs , td ) ;
else
return scan_read_raw_oob ( mtd , buf , offs , len ) ;
}
}
/*
/*
@ -267,6 +392,15 @@ static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
return mtd - > write_oob ( mtd , offs , & ops ) ;
return mtd - > write_oob ( mtd , offs , & ops ) ;
}
}
static u32 bbt_get_ver_offs ( struct mtd_info * mtd , struct nand_bbt_descr * td )
{
u32 ver_offs = td - > veroffs ;
if ( ! ( td - > options & NAND_BBT_NO_OOB ) )
ver_offs + = mtd - > writesize ;
return ver_offs ;
}
/**
/**
* read_abs_bbts - [ GENERIC ] Read the bad block table ( s ) for all chips starting at a given page
* read_abs_bbts - [ GENERIC ] Read the bad block table ( s ) for all chips starting at a given page
* @ mtd : MTD device structure
* @ mtd : MTD device structure
@ -285,18 +419,18 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
/* Read the primary version, if available */
/* Read the primary version, if available */
if ( td - > options & NAND_BBT_VERSION ) {
if ( td - > options & NAND_BBT_VERSION ) {
scan_read_raw ( mtd , buf , ( loff_t ) td - > pages [ 0 ] < <
scan_read_raw ( mtd , buf , ( loff_t ) td - > pages [ 0 ] < < this - > page_shift ,
this - > page_shift , mtd - > writesize ) ;
mtd - > writesize , td ) ;
td - > version [ 0 ] = buf [ mtd - > writesize + td - > veroffs ] ;
td - > version [ 0 ] = buf [ bbt_get_ver_offs ( mtd , td ) ] ;
printk ( KERN_DEBUG " Bad block table at page %d, version 0x%02X \n " ,
printk ( KERN_DEBUG " Bad block table at page %d, version 0x%02X \n " ,
td - > pages [ 0 ] , td - > version [ 0 ] ) ;
td - > pages [ 0 ] , td - > version [ 0 ] ) ;
}
}
/* Read the mirror version, if available */
/* Read the mirror version, if available */
if ( md & & ( md - > options & NAND_BBT_VERSION ) ) {
if ( md & & ( md - > options & NAND_BBT_VERSION ) ) {
scan_read_raw ( mtd , buf , ( loff_t ) md - > pages [ 0 ] < <
scan_read_raw ( mtd , buf , ( loff_t ) md - > pages [ 0 ] < < this - > page_shift ,
this - > page_shift , mtd - > writesize ) ;
mtd - > writesize , td ) ;
md - > version [ 0 ] = buf [ mtd - > writesize + md - > veroffs ] ;
md - > version [ 0 ] = buf [ bbt_get_ver_offs ( mtd , md ) ] ;
printk ( KERN_DEBUG " Bad block table at page %d, version 0x%02X \n " ,
printk ( KERN_DEBUG " Bad block table at page %d, version 0x%02X \n " ,
md - > pages [ 0 ] , md - > version [ 0 ] ) ;
md - > pages [ 0 ] , md - > version [ 0 ] ) ;
}
}
@ -312,7 +446,7 @@ static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
{
{
int ret , j ;
int ret , j ;
ret = scan_read_raw ( mtd , buf , offs , readlen ) ;
ret = scan_read_raw_oob ( mtd , buf , offs , readlen ) ;
if ( ret )
if ( ret )
return ret ;
return ret ;
@ -376,16 +510,14 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
loff_t from ;
loff_t from ;
size_t readlen ;
size_t readlen ;
MTDDEBUG ( MTD_DEBUG_LEVEL0 , " Scanning device for bad blocks \n " ) ;
MTDDEBUG ( MTD_DEBUG_LEVEL0 , " Scanning device for bad blocks \n " ) ;
if ( bd - > options & NAND_BBT_SCANALLPAGES )
if ( bd - > options & NAND_BBT_SCANALLPAGES )
len = 1 < < ( this - > bbt_erase_shift - this - > page_shift ) ;
len = 1 < < ( this - > bbt_erase_shift - this - > page_shift ) ;
else {
else if ( bd - > options & NAND_BBT_SCAN2NDPAGE )
if ( bd - > options & NAND_BBT_SCAN2NDPAGE )
len = 2 ;
len = 2 ;
else
else
len = 1 ;
len = 1 ;
}
if ( ! ( bd - > options & NAND_BBT_SCANEMPTY ) ) {
if ( ! ( bd - > options & NAND_BBT_SCANEMPTY ) ) {
/* We need only read few bytes from the OOB area */
/* We need only read few bytes from the OOB area */
@ -415,9 +547,14 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
from = ( loff_t ) startblock < < ( this - > bbt_erase_shift - 1 ) ;
from = ( loff_t ) startblock < < ( this - > bbt_erase_shift - 1 ) ;
}
}
if ( this - > options & NAND_BBT_SCANLASTPAGE )
from + = mtd - > erasesize - ( mtd - > writesize * len ) ;
for ( i = startblock ; i < numblocks ; ) {
for ( i = startblock ; i < numblocks ; ) {
int ret ;
int ret ;
BUG_ON ( bd - > options & NAND_BBT_NO_OOB ) ;
if ( bd - > options & NAND_BBT_SCANALLPAGES )
if ( bd - > options & NAND_BBT_SCANALLPAGES )
ret = scan_block_full ( mtd , bd , from , buf , readlen ,
ret = scan_block_full ( mtd , bd , from , buf , readlen ,
scanlen , len ) ;
scanlen , len ) ;
@ -429,7 +566,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
if ( ret ) {
if ( ret ) {
this - > bbt [ i > > 3 ] | = 0x03 < < ( i & 0x6 ) ;
this - > bbt [ i > > 3 ] | = 0x03 < < ( i & 0x6 ) ;
MTDDEBUG ( MTD_DEBUG_LEVEL0 ,
MTDDEBUG ( MTD_DEBUG_LEVEL0 ,
" Bad eraseblock %d at 0x%012llx \n " ,
" Bad eraseblock %d at 0x%012llx \n " ,
i > > 1 , ( unsigned long long ) from ) ;
i > > 1 , ( unsigned long long ) from ) ;
mtd - > ecc_stats . badblocks + + ;
mtd - > ecc_stats . badblocks + + ;
@ -497,11 +634,12 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
loff_t offs = ( loff_t ) actblock < < this - > bbt_erase_shift ;
loff_t offs = ( loff_t ) actblock < < this - > bbt_erase_shift ;
/* Read first page */
/* Read first page */
scan_read_raw ( mtd , buf , offs , mtd - > writesize ) ;
scan_read_raw ( mtd , buf , offs , mtd - > writesize , td ) ;
if ( ! check_pattern ( buf , scanlen , mtd - > writesize , td ) ) {
if ( ! check_pattern ( buf , scanlen , mtd - > writesize , td ) ) {
td - > pages [ i ] = actblock < < blocktopage ;
td - > pages [ i ] = actblock < < blocktopage ;
if ( td - > options & NAND_BBT_VERSION ) {
if ( td - > options & NAND_BBT_VERSION ) {
td - > version [ i ] = buf [ mtd - > writesize + td - > veroffs ] ;
offs = bbt_get_ver_offs ( mtd , td ) ;
td - > version [ i ] = buf [ offs ] ;
}
}
break ;
break ;
}
}
@ -685,12 +823,26 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
memset ( & buf [ offs ] , 0xff , ( size_t ) ( numblocks > > sft ) ) ;
memset ( & buf [ offs ] , 0xff , ( size_t ) ( numblocks > > sft ) ) ;
ooboffs = len + ( pageoffs * mtd - > oobsize ) ;
ooboffs = len + ( pageoffs * mtd - > oobsize ) ;
} else if ( td - > options & NAND_BBT_NO_OOB ) {
ooboffs = 0 ;
offs = td - > len ;
/* the version byte */
if ( td - > options & NAND_BBT_VERSION )
offs + + ;
/* Calc length */
len = ( size_t ) ( numblocks > > sft ) ;
len + = offs ;
/* Make it page aligned ! */
len = ALIGN ( len , mtd - > writesize ) ;
/* Preset the buffer with 0xff */
memset ( buf , 0xff , len ) ;
/* Pattern is located at the begin of first page */
memcpy ( buf , td - > pattern , td - > len ) ;
} else {
} else {
/* Calc length */
/* Calc length */
len = ( size_t ) ( numblocks > > sft ) ;
len = ( size_t ) ( numblocks > > sft ) ;
/* Make it page aligned ! */
/* Make it page aligned ! */
len = ( len + ( mtd - > writesize - 1 ) ) &
len = ALIGN ( len , mtd - > writesize ) ;
~ ( mtd - > writesize - 1 ) ;
/* Preset the buffer with 0xff */
/* Preset the buffer with 0xff */
memset ( buf , 0xff , len +
memset ( buf , 0xff , len +
( len > > this - > page_shift ) * mtd - > oobsize ) ;
( len > > this - > page_shift ) * mtd - > oobsize ) ;
@ -724,13 +876,14 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
if ( res < 0 )
if ( res < 0 )
goto outerr ;
goto outerr ;
res = scan_write_bbt ( mtd , to , len , buf , & buf [ len ] ) ;
res = scan_write_bbt ( mtd , to , len , buf ,
td - > options & NAND_BBT_NO_OOB ? NULL :
& buf [ len ] ) ;
if ( res < 0 )
if ( res < 0 )
goto outerr ;
goto outerr ;
printk ( KERN_DEBUG " Bad block table written to 0x%012llx, "
printk ( KERN_DEBUG " Bad block table written to 0x%012llx, version "
" version 0x%02X \n " , ( unsigned long long ) to ,
" 0x%02X \n " , ( unsigned long long ) to , td - > version [ chip ] ) ;
td - > version [ chip ] ) ;
/* Mark it as used */
/* Mark it as used */
td - > pages [ chip ] = page ;
td - > pages [ chip ] = page ;
@ -791,7 +944,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
rd2 = NULL ;
rd2 = NULL ;
/* Per chip or per device ? */
/* Per chip or per device ? */
chipsel = ( td - > options & NAND_BBT_PERCHIP ) ? i : - 1 ;
chipsel = ( td - > options & NAND_BBT_PERCHIP ) ? i : - 1 ;
/* Mirrored table avilable ? */
/* Mirrored table ava ilable ? */
if ( md ) {
if ( md ) {
if ( td - > pages [ i ] = = - 1 & & md - > pages [ i ] = = - 1 ) {
if ( td - > pages [ i ] = = - 1 & & md - > pages [ i ] = = - 1 ) {
writeops = 0x03 ;
writeops = 0x03 ;
@ -845,7 +998,8 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
continue ;
continue ;
/* Create the table in memory by scanning the chip(s) */
/* Create the table in memory by scanning the chip(s) */
create_bbt ( mtd , buf , bd , chipsel ) ;
if ( ! ( this - > options & NAND_CREATE_EMPTY_BBT ) )
create_bbt ( mtd , buf , bd , chipsel ) ;
td - > version [ i ] = 1 ;
td - > version [ i ] = 1 ;
if ( md )
if ( md )
@ -910,8 +1064,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
newval = oldval | ( 0x2 < < ( block & 0x06 ) ) ;
newval = oldval | ( 0x2 < < ( block & 0x06 ) ) ;
this - > bbt [ ( block > > 3 ) ] = newval ;
this - > bbt [ ( block > > 3 ) ] = newval ;
if ( ( oldval ! = newval ) & & td - > reserved_block_code )
if ( ( oldval ! = newval ) & & td - > reserved_block_code )
nand_update_bbt ( mtd , ( loff_t ) block < <
nand_update_bbt ( mtd , ( loff_t ) block < < ( this - > bbt_erase_shift - 1 ) ) ;
( this - > bbt_erase_shift - 1 ) ) ;
continue ;
continue ;
}
}
update = 0 ;
update = 0 ;
@ -932,12 +1085,59 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
new ones have been marked , then we need to update the stored
new ones have been marked , then we need to update the stored
bbts . This should only happen once . */
bbts . This should only happen once . */
if ( update & & td - > reserved_block_code )
if ( update & & td - > reserved_block_code )
nand_update_bbt ( mtd , ( loff_t ) ( block - 2 ) < <
nand_update_bbt ( mtd , ( loff_t ) ( block - 2 ) < < ( this - > bbt_erase_shift - 1 ) ) ;
( this - > bbt_erase_shift - 1 ) ) ;
}
}
}
}
/**
/**
* verify_bbt_descr - verify the bad block description
* @ mtd : MTD device structure
* @ bd : the table to verify
*
* This functions performs a few sanity checks on the bad block description
* table .
*/
static void verify_bbt_descr ( struct mtd_info * mtd , struct nand_bbt_descr * bd )
{
struct nand_chip * this = mtd - > priv ;
u32 pattern_len ;
u32 bits ;
u32 table_size ;
if ( ! bd )
return ;
pattern_len = bd - > len ;
bits = bd - > options & NAND_BBT_NRBITS_MSK ;
BUG_ON ( ( this - > options & NAND_USE_FLASH_BBT_NO_OOB ) & &
! ( this - > options & NAND_USE_FLASH_BBT ) ) ;
BUG_ON ( ! bits ) ;
if ( bd - > options & NAND_BBT_VERSION )
pattern_len + + ;
if ( bd - > options & NAND_BBT_NO_OOB ) {
BUG_ON ( ! ( this - > options & NAND_USE_FLASH_BBT ) ) ;
BUG_ON ( ! ( this - > options & NAND_USE_FLASH_BBT_NO_OOB ) ) ;
BUG_ON ( bd - > offs ) ;
if ( bd - > options & NAND_BBT_VERSION )
BUG_ON ( bd - > veroffs ! = bd - > len ) ;
BUG_ON ( bd - > options & NAND_BBT_SAVECONTENT ) ;
}
if ( bd - > options & NAND_BBT_PERCHIP )
table_size = this - > chipsize > > this - > bbt_erase_shift ;
else
table_size = mtd - > size > > this - > bbt_erase_shift ;
table_size > > = 3 ;
table_size * = bits ;
if ( bd - > options & NAND_BBT_NO_OOB )
table_size + = pattern_len ;
BUG_ON ( table_size > ( 1 < < this - > bbt_erase_shift ) ) ;
}
/**
* nand_scan_bbt - [ NAND Interface ] scan , find , read and maybe create bad block table ( s )
* nand_scan_bbt - [ NAND Interface ] scan , find , read and maybe create bad block table ( s )
* @ mtd : MTD device structure
* @ mtd : MTD device structure
* @ bd : descriptor for the good / bad block search pattern
* @ bd : descriptor for the good / bad block search pattern
@ -978,6 +1178,8 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
}
}
return res ;
return res ;
}
}
verify_bbt_descr ( mtd , td ) ;
verify_bbt_descr ( mtd , md ) ;
/* Allocate a temporary buffer for one eraseblock incl. oob */
/* Allocate a temporary buffer for one eraseblock incl. oob */
len = ( 1 < < this - > bbt_erase_shift ) ;
len = ( 1 < < this - > bbt_erase_shift ) ;
@ -1073,34 +1275,6 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
* while scanning a device for factory marked good / bad blocks . */
* while scanning a device for factory marked good / bad blocks . */
static uint8_t scan_ff_pattern [ ] = { 0xff , 0xff } ;
static uint8_t scan_ff_pattern [ ] = { 0xff , 0xff } ;
static struct nand_bbt_descr smallpage_memorybased = {
. options = NAND_BBT_SCAN2NDPAGE ,
. 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 = {
. options = NAND_BBT_SCAN2NDPAGE ,
. offs = 5 ,
. len = 1 ,
. pattern = scan_ff_pattern
} ;
static struct nand_bbt_descr largepage_flashbased = {
. options = NAND_BBT_SCAN2NDPAGE ,
. offs = 0 ,
. len = 2 ,
. pattern = scan_ff_pattern
} ;
static uint8_t scan_agand_pattern [ ] = { 0x1C , 0x71 , 0xC7 , 0x1C , 0x71 , 0xC7 } ;
static uint8_t scan_agand_pattern [ ] = { 0x1C , 0x71 , 0xC7 , 0x1C , 0x71 , 0xC7 } ;
static struct nand_bbt_descr agand_flashbased = {
static struct nand_bbt_descr agand_flashbased = {
@ -1135,6 +1309,59 @@ static struct nand_bbt_descr bbt_mirror_descr = {
. pattern = mirror_pattern
. pattern = mirror_pattern
} ;
} ;
static struct nand_bbt_descr bbt_main_no_bbt_descr = {
. options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
| NAND_BBT_NO_OOB ,
. len = 4 ,
. veroffs = 4 ,
. maxblocks = 4 ,
. pattern = bbt_pattern
} ;
static struct nand_bbt_descr bbt_mirror_no_bbt_descr = {
. options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
| NAND_BBT_NO_OOB ,
. len = 4 ,
. veroffs = 4 ,
. maxblocks = 4 ,
. pattern = mirror_pattern
} ;
# define BBT_SCAN_OPTIONS (NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE | \
NAND_BBT_SCANBYTE1AND6 )
/**
* nand_create_default_bbt_descr - [ Internal ] Creates a BBT descriptor structure
* @ this : NAND chip to create descriptor for
*
* This function allocates and initializes a nand_bbt_descr for BBM detection
* based on the properties of " this " . The new descriptor is stored in
* this - > badblock_pattern . Thus , this - > badblock_pattern should be NULL when
* passed to this function .
*
*/
static int nand_create_default_bbt_descr ( struct nand_chip * this )
{
struct nand_bbt_descr * bd ;
if ( this - > badblock_pattern ) {
printk ( KERN_WARNING " BBT descr already allocated; not replacing. \n " ) ;
return - EINVAL ;
}
bd = kzalloc ( sizeof ( * bd ) , GFP_KERNEL ) ;
if ( ! bd ) {
printk ( KERN_ERR " nand_create_default_bbt_descr: Out of memory \n " ) ;
return - ENOMEM ;
}
bd - > options = this - > options & BBT_SCAN_OPTIONS ;
bd - > offs = this - > badblockpos ;
bd - > len = ( this - > options & NAND_BUSWIDTH_16 ) ? 2 : 1 ;
bd - > pattern = scan_ff_pattern ;
bd - > options | = NAND_BBT_DYNAMICSTRUCT ;
this - > badblock_pattern = bd ;
return 0 ;
}
/**
/**
* nand_default_bbt - [ NAND Interface ] Select a default bad block table for the device
* nand_default_bbt - [ NAND Interface ] Select a default bad block table for the device
* @ mtd : MTD device structure
* @ mtd : MTD device structure
@ -1168,20 +1395,22 @@ int nand_default_bbt(struct mtd_info *mtd)
if ( this - > options & NAND_USE_FLASH_BBT ) {
if ( this - > options & NAND_USE_FLASH_BBT ) {
/* Use the default pattern descriptors */
/* Use the default pattern descriptors */
if ( ! this - > bbt_td ) {
if ( ! this - > bbt_td ) {
this - > bbt_td = & bbt_main_descr ;
if ( this - > options & NAND_USE_FLASH_BBT_NO_OOB ) {
this - > bbt_md = & bbt_mirror_descr ;
this - > bbt_td = & bbt_main_no_bbt_descr ;
}
this - > bbt_md = & bbt_mirror_no_bbt_descr ;
if ( ! this - > badblock_pattern ) {
} else {
this - > badblock_pattern = ( mtd - > writesize > 512 ) ? & largepage_flashbased : & smallpage_flashbased ;
this - > bbt_td = & bbt_main_descr ;
this - > bbt_md = & bbt_mirror_descr ;
}
}
}
} else {
} else {
this - > bbt_td = NULL ;
this - > bbt_td = NULL ;
this - > bbt_md = NULL ;
this - > bbt_md = NULL ;
if ( ! this - > badblock_pattern ) {
this - > badblock_pattern = ( mtd - > writesize > 512 ) ?
& largepage_memorybased : & smallpage_memorybased ;
}
}
}
if ( ! this - > badblock_pattern )
nand_create_default_bbt_descr ( this ) ;
return nand_scan_bbt ( mtd , this - > badblock_pattern ) ;
return nand_scan_bbt ( mtd , this - > badblock_pattern ) ;
}
}
@ -1202,8 +1431,8 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
block = ( int ) ( offs > > ( this - > bbt_erase_shift - 1 ) ) ;
block = ( int ) ( offs > > ( this - > bbt_erase_shift - 1 ) ) ;
res = ( this - > bbt [ block > > 3 ] > > ( block & 0x06 ) ) & 0x03 ;
res = ( this - > bbt [ block > > 3 ] > > ( block & 0x06 ) ) & 0x03 ;
MTDDEBUG ( MTD_DEBUG_LEVEL2 , " nand_isbad_bbt(): bbt info for offs 0x%08x: "
MTDDEBUG ( MTD_DEBUG_LEVEL2 , " nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x \n " ,
" (block %d) 0x%02x \n " , ( unsigned int ) offs , res , block > > 1 ) ;
( unsigned int ) offs , block > > 1 , res ) ;
switch ( ( int ) res ) {
switch ( ( int ) res ) {
case 0x00 :
case 0x00 :