|
@@ -13,28 +13,37 @@
|
|
|
* Description:
|
|
|
*
|
|
|
* 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
|
|
|
- * 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.
|
|
|
+ * depending on the options in the BBT descriptor(s). If no flash based BBT
|
|
|
+ * (NAND_USE_FLASH_BBT) is specified then the device is scanned for factory
|
|
|
+ * marked good / bad blocks. This information is used to create a memory BBT.
|
|
|
+ * Once a new bad block is discovered then the "factory" information is updated
|
|
|
+ * 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 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 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
|
|
|
* good / bad blocks and the bad block tables are created.
|
|
|
*
|
|
|
- * For manufacturer created bbts like the one found on M-SYS DOC devices
|
|
|
- * the bbt is searched and read but never created
|
|
|
+ * For manufacturer created BBTs like the one found on M-SYS DOC devices
|
|
|
+ * 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.
|
|
|
- * 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.
|
|
|
+ * 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. 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
|
|
|
- * 11b: block is good
|
|
|
- * 00b: block is factory marked bad
|
|
|
- * 01b, 10b: block is marked bad due to wear
|
|
|
+ * 11b: block is good
|
|
|
+ * 00b: block is factory marked bad
|
|
|
+ * 01b, 10b: block is marked bad due to wear
|
|
|
*
|
|
|
* The memory bad block table uses the following scheme:
|
|
|
* 00b: block is good
|
|
@@ -59,6 +68,16 @@
|
|
|
#include <linux/delay.h>
|
|
|
#include <linux/vmalloc.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
|
|
|
* @buf: the buffer to search
|
|
@@ -77,6 +96,9 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc
|
|
|
int i, end = 0;
|
|
|
uint8_t *p = buf;
|
|
|
|
|
|
+ if (td->options & NAND_BBT_NO_OOB)
|
|
|
+ return check_pattern_no_oob(buf, td);
|
|
|
+
|
|
|
end = paglen + td->offs;
|
|
|
if (td->options & NAND_BBT_SCANEMPTY) {
|
|
|
for (i = 0; i < end; i++) {
|
|
@@ -155,6 +177,25 @@ static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
|
|
|
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
|
|
|
* @mtd: MTD device structure
|
|
@@ -176,13 +217,24 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
|
|
|
loff_t from;
|
|
|
int bits = td->options & NAND_BBT_NRBITS_MSK;
|
|
|
uint8_t msk = (uint8_t) ((1 << bits) - 1);
|
|
|
+ u32 marker_len;
|
|
|
int reserved_block_code = td->reserved_block_code;
|
|
|
|
|
|
totlen = (num * bits) >> 3;
|
|
|
+ marker_len = add_marker_len(td);
|
|
|
from = ((loff_t) page) << this->page_shift;
|
|
|
|
|
|
while (totlen) {
|
|
|
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);
|
|
|
if (res < 0) {
|
|
|
if (retlen != len) {
|
|
@@ -260,10 +312,26 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
|
|
|
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
|
|
|
*/
|
|
|
-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)
|
|
|
{
|
|
|
struct mtd_oob_ops ops;
|
|
@@ -296,6 +364,15 @@ static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
|
|
|
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);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Scan write data with oob to flash
|
|
|
*/
|
|
@@ -314,6 +391,15 @@ static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
|
|
|
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
|
|
|
* @mtd: MTD device structure
|
|
@@ -333,8 +419,8 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
|
|
|
/* Read the primary version, if available */
|
|
|
if (td->options & NAND_BBT_VERSION) {
|
|
|
scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
|
|
|
- mtd->writesize);
|
|
|
- td->version[0] = buf[mtd->writesize + td->veroffs];
|
|
|
+ mtd->writesize, td);
|
|
|
+ td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
|
|
|
printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
|
|
|
td->pages[0], td->version[0]);
|
|
|
}
|
|
@@ -342,8 +428,8 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
|
|
|
/* Read the mirror version, if available */
|
|
|
if (md && (md->options & NAND_BBT_VERSION)) {
|
|
|
scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
|
|
|
- mtd->writesize);
|
|
|
- md->version[0] = buf[mtd->writesize + md->veroffs];
|
|
|
+ mtd->writesize, td);
|
|
|
+ md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
|
|
|
printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
|
|
|
md->pages[0], md->version[0]);
|
|
|
}
|
|
@@ -359,7 +445,7 @@ static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
|
|
|
{
|
|
|
int ret, j;
|
|
|
|
|
|
- ret = scan_read_raw(mtd, buf, offs, readlen);
|
|
|
+ ret = scan_read_raw_oob(mtd, buf, offs, readlen);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
@@ -466,6 +552,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
|
|
|
for (i = startblock; i < numblocks;) {
|
|
|
int ret;
|
|
|
|
|
|
+ BUG_ON(bd->options & NAND_BBT_NO_OOB);
|
|
|
+
|
|
|
if (bd->options & NAND_BBT_SCANALLPAGES)
|
|
|
ret = scan_block_full(mtd, bd, from, buf, readlen,
|
|
|
scanlen, len);
|
|
@@ -547,11 +635,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;
|
|
|
|
|
|
/* 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)) {
|
|
|
td->pages[i] = actblock << blocktopage;
|
|
|
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;
|
|
|
}
|
|
@@ -735,6 +824,21 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
|
|
memset(&buf[offs], 0xff, (size_t) (numblocks >> sft));
|
|
|
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 {
|
|
|
/* Calc length */
|
|
|
len = (size_t) (numblocks >> sft);
|
|
@@ -773,7 +877,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
|
|
if (res < 0)
|
|
|
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)
|
|
|
goto outerr;
|
|
|
|
|
@@ -983,6 +1089,49 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * verify_bbt_descr - verify the bad block description
|
|
|
+ * @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 = bd->len;
|
|
|
+ u32 bits = bd->options & NAND_BBT_NRBITS_MSK;
|
|
|
+ u32 table_size;
|
|
|
+
|
|
|
+ if (!bd)
|
|
|
+ return;
|
|
|
+ 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)
|
|
|
* @mtd: MTD device structure
|
|
@@ -1024,6 +1173,8 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
|
|
}
|
|
|
return res;
|
|
|
}
|
|
|
+ verify_bbt_descr(mtd, td);
|
|
|
+ verify_bbt_descr(mtd, md);
|
|
|
|
|
|
/* Allocate a temporary buffer for one eraseblock incl. oob */
|
|
|
len = (1 << this->bbt_erase_shift);
|
|
@@ -1167,6 +1318,26 @@ static struct nand_bbt_descr bbt_mirror_descr = {
|
|
|
.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)
|
|
|
/**
|
|
@@ -1237,8 +1408,13 @@ int nand_default_bbt(struct mtd_info *mtd)
|
|
|
if (this->options & NAND_USE_FLASH_BBT) {
|
|
|
/* Use the default pattern descriptors */
|
|
|
if (!this->bbt_td) {
|
|
|
- this->bbt_td = &bbt_main_descr;
|
|
|
- this->bbt_md = &bbt_mirror_descr;
|
|
|
+ if (this->options & NAND_USE_FLASH_BBT_NO_OOB) {
|
|
|
+ this->bbt_td = &bbt_main_no_bbt_descr;
|
|
|
+ this->bbt_md = &bbt_mirror_no_bbt_descr;
|
|
|
+ } else {
|
|
|
+ this->bbt_td = &bbt_main_descr;
|
|
|
+ this->bbt_md = &bbt_mirror_descr;
|
|
|
+ }
|
|
|
}
|
|
|
if (!this->badblock_pattern) {
|
|
|
this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;
|