nand: Merge changes to BBT from Linux nand driver

[backport from linux commit 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe]

This patch synchronizes the nand driver with the Linux 3.0 state.

Signed-off-by: Christian Hitz <christian.hitz@aizo.com>
Cc: Scott Wood <scottwood@freescale.com>
Signed-off-by: Scott Wood <scottwood@freescale.com>
master
Christian Hitz 13 years ago committed by Scott Wood
parent 2a8e0fc8b3
commit ff8a8a7183
  1. 427
      drivers/mtd/nand/nand_bbt.c
  2. 2
      include/linux/mtd/bbm.h

@ -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 BBTs 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 bbts like the one found on M-SYS DOC devices * For manufacturer created BBTs 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 autogenerated 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
* 11b: block is good * 11b: block is good
* 00b: block is factory marked bad * 00b: block is factory marked bad
* 01b, 10b: block is marked bad due to wear * 01b, 10b: 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 available ? */
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:

@ -105,6 +105,8 @@ struct nand_bbt_descr {
#define NAND_BBT_SCANBYTE1AND6 0x00100000 #define NAND_BBT_SCANBYTE1AND6 0x00100000
/* The nand_bbt_descr was created dynamicaly and must be freed */ /* The nand_bbt_descr was created dynamicaly and must be freed */
#define NAND_BBT_DYNAMICSTRUCT 0x00200000 #define NAND_BBT_DYNAMICSTRUCT 0x00200000
/* The bad block table does not OOB for marker */
#define NAND_BBT_NO_OOB 0x00400000
/* The maximum number of blocks to scan for a bbt */ /* The maximum number of blocks to scan for a bbt */
#define NAND_BBT_SCAN_MAXBLOCKS 4 #define NAND_BBT_SCAN_MAXBLOCKS 4

Loading…
Cancel
Save