Continue with Brain Norris's work. Allocate and initialize struct nand_bbt in nand_default_bbt()(nand_base.c). Remove .bbt from nand_chip. Complete hook nand_is_bad_bbm(nand_bbt.is_bad_bbm). Allocate and initialize badblock_pattern in nand_chip if badblock_pattern does not exist. And clean some checkpatch warnings.
TBD: Allocate badblock_pattern is a temporary way to make new BBT work. Remove .badblock_pattern from struct nand_chip and use .badblockpos, .bbt_options and .options instead is the next thing to do. Signed-off-by: Peter Pan <peterpand...@micron.com> --- drivers/mtd/nand/diskonchip.c | 3 +- drivers/mtd/nand/docg4.c | 5 ++- drivers/mtd/nand/nand_base.c | 100 ++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/nand_bbt.c | 16 ++++--- include/linux/mtd/nand.h | 1 - include/linux/mtd/nand_bbt.h | 8 +++- 6 files changed, 121 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index e580014..b980d77 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -1613,7 +1613,8 @@ static int __init doc_probe(unsigned long physadr) else numchips = doc2001_init(mtd); - if ((ret = nand_scan(mtd, numchips)) || (ret = doc->late_init(mtd))) { + if ((ret = nand_scan(mtd, numchips)) + || (ret = doc->late_init(mtd))) { /* DBB note: i believe nand_release is necessary here, as buffers may have been allocated in nand_base. Check with Thomas. FIX ME! */ diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index 9b671b9..5ed7c7c 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -1037,7 +1037,7 @@ static int __init read_factory_bbt(struct mtd_info *mtd) * operation after device power-up. The above read ensures it never is. * Ugly, I know. */ - if (nand->bbt == NULL) /* no memory-based bbt */ + if (nand->nand_bbt == NULL) /* no memory-based bbt */ goto exit; if (mtd->ecc_stats.failed > eccfailed_stats) { @@ -1064,7 +1064,8 @@ static int __init read_factory_bbt(struct mtd_info *mtd) unsigned long bits = ~buf[i]; for_each_set_bit(bitnum, &bits, 8) { int badblock = block + 7 - bitnum; - nand_bbt_markbad(nand->nand_bbt, badblock << nand->bbt_erase_shift); + nand_bbt_markbad(nand->nand_bbt, badblock + << nand->bbt_erase_shift); mtd->ecc_stats.badblocks++; dev_notice(doc->dev, "factory-marked bad block: %d\n", badblock); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ff9db05..dbd9955 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -98,9 +98,13 @@ static int nand_get_device(struct mtd_info *mtd, int new_state); static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); +static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops); + static int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int allowbbt); +static int nand_bbt_erase_block(struct mtd_info *mtd, loff_t addr); /* * For devices which display every fart in the system on a separate LED. Is * compiled away when LED support is disabled. @@ -531,13 +535,102 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, return nand_bbt_isreserved(bbt, ofs); } +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; +#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB) +/** + * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure + * @chip: 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_badblock_pattern(struct nand_chip *chip) +{ + struct nand_bbt_descr *bd; + + if (chip->badblock_pattern) { + pr_warn("Bad block pattern already allocated; not replacing\n"); + return -EINVAL; + } + bd = kzalloc(sizeof(*bd), GFP_KERNEL); + if (!bd) + return -ENOMEM; + bd->options = chip->bbt_options & BADBLOCK_SCAN_MASK; + bd->offs = chip->badblockpos; + bd->len = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; + bd->pattern = scan_ff_pattern; + bd->options |= NAND_BBT_DYNAMICSTRUCT; + chip->badblock_pattern = bd; + + return 0; +} + +static int nand_is_bad_bbm(struct mtd_info *mtd, loff_t addr) +{ + struct nand_chip *chip = mtd->priv; + struct mtd_oob_ops ops; + struct nand_bbt_descr *bd = chip->badblock_pattern; + int j, ret; + int numpages; + + if (bd->options & NAND_BBT_SCAN2NDPAGE) + numpages = 2; + else + numpages = 1; + + ops.ooblen = mtd->oobsize; + ops.oobbuf = chip->buffers->databuf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + + for (j = 0; j < numpages; j++) { + /* + * Read the full oob until read_oob is fixed to handle single + * byte reads for 16 bit buswidth. + */ + ret = nand_do_read_oob(mtd, addr, &ops); + /* Ignore ECC errors when checking for BBM */ + if (ret && !mtd_is_bitflip_or_eccerr(ret)) + return ret; + + if (memcmp(chip->buffers->databuf + bd->offs, + bd->pattern, bd->len)) + return 1; + + addr += mtd->writesize; + } + + return 0; +} + static int nand_default_bbt(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; + struct nand_bbt *bbt = kzalloc(sizeof(struct nand_bbt), GFP_KERNEL); + + if (!bbt) + return -ENOMEM; + + bbt->bbt_options = chip->bbt_options; + bbt->mtd = mtd; + bbt->numchips = chip->numchips; + bbt->chipsize = chip->chipsize; + bbt->chip_shift = chip->chip_shift; + bbt->bbt_erase_shift = chip->phys_erase_shift; + bbt->page_shift = chip->page_shift; + bbt->bbt_td = chip->bbt_td; + bbt->bbt_md = chip->bbt_md; + bbt->is_bad_bbm = nand_is_bad_bbm; + bbt->erase = nand_bbt_erase_block; + chip->nand_bbt = bbt; return nand_bbt_init(chip->nand_bbt); } + /** * panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands. * @mtd: MTD device structure @@ -4208,6 +4301,8 @@ int nand_scan_tail(struct mtd_info *mtd) if (chip->options & NAND_SKIP_BBTSCAN) return 0; + if (!chip->badblock_pattern && nand_create_badblock_pattern(chip)) + return -ENOMEM; /* Build bad block table */ return chip->scan_bbt(mtd); } @@ -4271,6 +4366,11 @@ void nand_release(struct mtd_info *mtd) kfree(chip->nand_bbt); if (!(chip->options & NAND_OWN_BUFFERS)) kfree(chip->buffers); + + /* Free bad block descriptor memory */ + if (chip->badblock_pattern && chip->badblock_pattern->options + & NAND_BBT_DYNAMICSTRUCT) + kfree(chip->badblock_pattern); } EXPORT_SYMBOL_GPL(nand_release); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 27269ea..ebbe6bf 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -245,7 +245,8 @@ static int read_bbt(struct nand_bbt *bbt, uint8_t *buf, int page, int num, * Read the bad block table for all chips starting at a given page. We assume * that the bbt bits are in consecutive order. */ -static int read_abs_bbt(struct nand_bbt *bbt, uint8_t *buf, struct nand_bbt_descr *td, int chip) +static int read_abs_bbt(struct nand_bbt *bbt, uint8_t *buf, + struct nand_bbt_descr *td, int chip) { struct mtd_info *mtd = bbt->mtd; int res = 0, i; @@ -398,14 +399,13 @@ static void read_abs_bbts(struct nand_bbt *bbt, uint8_t *buf, /** * create_bbt - [GENERIC] Create a bad block table by scanning the device * @bbt: NAND BBT structure - * @buf: temporary buffer * @chip: create the table for a specific chip, -1 read all chips; applies only * if NAND_BBT_PERCHIP option is set * * Create a bad block table by scanning the device for the given good/bad block * identify pattern. */ -static int create_bbt(struct nand_bbt *bbt, uint8_t *buf, int chip) +static int create_bbt(struct nand_bbt *bbt, int chip) { struct mtd_info *mtd = bbt->mtd; int i, startblock, numblocks; @@ -463,7 +463,8 @@ static int create_bbt(struct nand_bbt *bbt, uint8_t *buf, int chip) * * The bbt ident pattern resides in the oob area of the first page in a block. */ -static int search_bbt(struct nand_bbt *bbt, uint8_t *buf, struct nand_bbt_descr *td) +static int search_bbt(struct nand_bbt *bbt, uint8_t *buf, + struct nand_bbt_descr *td) { struct mtd_info *mtd = bbt->mtd; int i, chips; @@ -755,7 +756,7 @@ static int write_bbt(struct nand_bbt *bbt, uint8_t *buf, */ static inline int nand_memory_bbt(struct nand_bbt *bbt) { - return create_bbt(bbt, NULL /* FIXME: this->buffers->databuf */, -1); + return create_bbt(bbt, -1); } /** @@ -827,7 +828,7 @@ static int check_create(struct nand_bbt *bbt, uint8_t *buf) /* Create the table in memory by scanning the chip(s) */ if (!(bbt->bbt_options & NAND_BBT_CREATE_EMPTY)) - create_bbt(bbt, buf, chipsel); + create_bbt(bbt, chipsel); td->version[i] = 1; if (md) @@ -912,7 +913,8 @@ static void mark_bbt_region(struct nand_bbt *bbt, struct nand_bbt_descr *td) !(td->options & NAND_BBT_WRITE)) { if (td->pages[i] == -1) continue; - block = td->pages[i] >> (bbt->bbt_erase_shift - bbt->page_shift); + block = td->pages[i] >> + (bbt->bbt_erase_shift - bbt->page_shift); oldval = bbt_get_entry(bbt, block); bbt_mark_entry(bbt, block, BBT_BLOCK_RESERVED); if ((oldval != BBT_BLOCK_RESERVED) && diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 539c9bf..fc53882 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -712,7 +712,6 @@ struct nand_chip { struct nand_hw_control hwcontrol; struct nand_bbt *nand_bbt; - uint8_t *bbt; struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_md; diff --git a/include/linux/mtd/nand_bbt.h b/include/linux/mtd/nand_bbt.h index f776695..ed71e0e 100644 --- a/include/linux/mtd/nand_bbt.h +++ b/include/linux/mtd/nand_bbt.h @@ -83,7 +83,7 @@ struct nand_bbt_descr { * with NAND_BBT_CREATE. */ #define NAND_BBT_CREATE_EMPTY 0x00000400 -/* Write bbt if neccecary */ +/* Write bbt if necessary */ #define NAND_BBT_WRITE 0x00002000 /* Read and write back block contents when writing bbt */ #define NAND_BBT_SAVECONTENT 0x00004000 @@ -107,6 +107,12 @@ struct nand_bbt_descr { */ #define NAND_BBT_NO_OOB_BBM 0x00080000 +/* + * Flag to mark that the nand_bbt_descr was allocated dynamicaly and must + * be freed in nand_release(). + */ +#define NAND_BBT_DYNAMICSTRUCT 0x80000000 + /* The maximum number of blocks to scan for a bbt */ #define NAND_BBT_SCAN_MAXBLOCKS 4 -- 1.9.1