Commit 05ff4de5 authored by Dmitry Konyshev's avatar Dmitry Konyshev

RM1349 Backport newer NAND bad block table management code

parent 0de06cfb
...@@ -13,27 +13,36 @@ ...@@ -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,21 +64,20 @@ ...@@ -55,21 +64,20 @@
#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>
/* XXX U-BOOT XXX */ static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
#if 0 {
#include <linux/slab.h> int ret;
#include <linux/types.h>
#include <linux/mtd/mtd.h> ret = memcmp(buf, td->pattern, td->len);
#include <linux/mtd/nand.h> if (!ret)
#include <linux/mtd/nand_ecc.h> return ret;
#include <linux/mtd/compatmac.h> return -1;
#include <linux/bitops.h> }
#include <linux/delay.h>
#include <linux/vmalloc.h>
#endif
/** /**
* check_pattern - [GENERIC] check if a pattern is in the buffer * check_pattern - [GENERIC] check if a pattern is in the buffer
...@@ -89,6 +97,9 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc ...@@ -89,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++) {
...@@ -104,6 +115,28 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc ...@@ -104,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;
...@@ -135,36 +168,74 @@ static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) ...@@ -135,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) {
...@@ -183,9 +254,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, ...@@ -183,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;
...@@ -193,8 +262,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, ...@@ -193,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);
...@@ -224,42 +292,86 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc ...@@ -224,42 +292,86 @@ 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;
} }
return 0; return 0;
} }
/*
* 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);
} }
/* /*
...@@ -280,6 +392,15 @@ static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, ...@@ -280,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
...@@ -298,18 +419,18 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, ...@@ -298,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]);
} }
...@@ -325,7 +446,7 @@ static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, ...@@ -325,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;
...@@ -389,16 +510,14 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, ...@@ -389,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 */
...@@ -428,9 +547,14 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, ...@@ -428,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);
...@@ -442,7 +566,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, ...@@ -442,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++;
...@@ -475,7 +599,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr ...@@ -475,7 +599,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
int i, chips; int i, chips;
int bits, startblock, block, dir; int startblock, block, dir;
int scanlen = mtd->writesize + mtd->oobsize; int scanlen = mtd->writesize + mtd->oobsize;
int bbtblocks; int bbtblocks;
int blocktopage = this->bbt_erase_shift - this->page_shift; int blocktopage = this->bbt_erase_shift - this->page_shift;
...@@ -499,9 +623,6 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr ...@@ -499,9 +623,6 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
bbtblocks = mtd->size >> this->bbt_erase_shift; bbtblocks = mtd->size >> this->bbt_erase_shift;
} }
/* Number of bits for each erase block in the bbt */
bits = td->options & NAND_BBT_NRBITS_MSK;
for (i = 0; i < chips; i++) { for (i = 0; i < chips; i++) {
/* Reset version information */ /* Reset version information */
td->version[i] = 0; td->version[i] = 0;
...@@ -513,11 +634,12 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr ...@@ -513,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;
} }
...@@ -701,12 +823,26 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, ...@@ -701,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);
...@@ -740,13 +876,14 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, ...@@ -740,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;
...@@ -807,7 +944,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc ...@@ -807,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;
...@@ -861,7 +998,8 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc ...@@ -861,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)
...@@ -926,8 +1064,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) ...@@ -926,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;
...@@ -948,11 +1085,58 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) ...@@ -948,11 +1085,58 @@ 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);
}