From a0b431065180b62363749d5df7c747f8dd87f458 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:21:20 +0100 Subject: mtd: nand: refactor chip->block_markbad interface Linux commit 5a0edb251ae9 adapted for Barebox: The chip->block_markbad pointer should really only be responsible for writing a bad block marker for new bad blocks. It should not take care of BBT-related functionality, nor should it handle bookkeeping of bad block stats. Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_base.c | 87 ++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 128802fa5c..31093858e3 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -326,13 +326,58 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) } /** - * nand_default_block_markbad - [DEFAULT] mark a block bad + * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker * @mtd: MTD device structure * @ofs: offset from device start * * This is the default implementation, which can be overridden by a hardware - * specific driver. We try operations in the following order, according to our - * bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH): + * specific driver. It provides the details for writing a bad block marker to a + * block. + */ +static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + struct mtd_oob_ops ops; + uint8_t buf[2] = { 0, 0 }; + int ret = 0, res, i = 0; + + ops.datbuf = NULL; + ops.oobbuf = buf; + ops.ooboffs = chip->badblockpos; + if (chip->options & NAND_BUSWIDTH_16) { + ops.ooboffs &= ~0x01; + ops.len = ops.ooblen = 2; + } else { + ops.len = ops.ooblen = 1; + } + ops.mode = MTD_OPS_PLACE_OOB; + + /* Write to first/last page(s) if necessary */ + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) + ofs += mtd->erasesize - mtd->writesize; + do { + res = nand_do_write_oob(mtd, ofs, &ops); + if (!ret) + ret = res; + + i++; + ofs += mtd->writesize; + } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); + + return ret; +} + +/** + * nand_block_markbad_lowlevel - mark a block bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This function performs the generic NAND bad block marking steps (i.e., bad + * block table(s) and/or marker(s)). We only allow the hardware driver to + * specify how to write bad block markers to OOB (chip->block_markbad). + * + * We try operations in the following order, according to our bbt_options + * (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH): * (1) erase the affected block, to allow OOB marker to be written cleanly * (2) update in-memory BBT * (3) write bad block marker to OOB area of affected block @@ -340,11 +385,10 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) * Note that we retain the first error encountered in (3) or (4), finish the * procedures, and dump the error in the end. */ -static __maybe_unused int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; - uint8_t buf[2] = { 0, 0 }; - int block, res, ret = 0, i = 0; + int block, res, ret = 0; int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM); if (write_oob) { @@ -366,34 +410,8 @@ static __maybe_unused int nand_default_block_markbad(struct mtd_info *mtd, loff /* Write bad block marker to OOB */ if (write_oob) { - struct mtd_oob_ops ops; - loff_t wr_ofs = ofs; - nand_get_device(mtd, FL_WRITING); - - ops.datbuf = NULL; - ops.oobbuf = buf; - ops.ooboffs = chip->badblockpos; - if (chip->options & NAND_BUSWIDTH_16) { - ops.ooboffs &= ~0x01; - ops.len = ops.ooblen = 2; - } else { - ops.len = ops.ooblen = 1; - } - ops.mode = MTD_OPS_PLACE_OOB; - - /* Write to first/last page(s) if necessary */ - if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) - wr_ofs += mtd->erasesize - mtd->writesize; - do { - res = nand_do_write_oob(mtd, wr_ofs, &ops); - if (!ret) - ret = res; - - i++; - wr_ofs += mtd->writesize; - } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); - + ret = chip->block_markbad(mtd, ofs); nand_release_device(mtd); } @@ -2804,7 +2822,6 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) */ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) { - struct nand_chip *chip = mtd->priv; int ret; if (!IS_ENABLED(CONFIG_MTD_WRITE)) @@ -2818,7 +2835,7 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) return ret; } - return chip->block_markbad(mtd, ofs); + return nand_block_markbad_lowlevel(mtd, ofs); } /** -- cgit v1.2.3 From b3f26b56a352ea78a029bad19a6e60d3b1e3fa19 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:21:51 +0100 Subject: mtd: nand: remove multiplied-by-2 block logic Linux commit b4d20d601f1e adapted for Barebox This patch removes any points where the block number is doubled/halved/otherwise-shifted, instead representing the block number in its most natural form: as the actual block number. Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_bbt.c | 83 +++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 48 deletions(-) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 94a2e57834..4a0ee1db65 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -179,7 +179,7 @@ static u32 add_marker_len(struct nand_bbt_descr *td) * @page: the starting page * @num: the number of bbt descriptors to read * @td: the bbt describtion table - * @offs: offset in the memory table + * @offs: block number offset in the table * * Read the bad block table starting from page. */ @@ -229,29 +229,28 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, /* Analyse data */ for (i = 0; i < len; i++) { uint8_t dat = buf[i]; - for (j = 0; j < 8; j += bits, act += 2) { + for (j = 0; j < 8; j += bits, act++) { uint8_t tmp = (dat >> j) & msk; if (tmp == msk) continue; if (reserved_block_code && (tmp == reserved_block_code)) { pr_info("nand_read_bbt: reserved block at 0x%012llx\n", - (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); - bbt_mark_entry(this, (offs << 2) + - (act >> 1), + (loff_t)(offs + act) << + this->bbt_erase_shift); + bbt_mark_entry(this, offs + act, BBT_BLOCK_RESERVED); mtd->ecc_stats.bbtblocks++; continue; } pr_debug("nand_read_bbt: bad block at 0x%012llx\n", - (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + (loff_t)(offs + act) << + this->bbt_erase_shift); /* Factory marked bad or worn out? */ if (tmp == 0) - bbt_mark_entry(this, (offs << 2) + - (act >> 1), + bbt_mark_entry(this, offs + act, BBT_BLOCK_FACTORY_BAD); else - bbt_mark_entry(this, (offs << 2) + - (act >> 1), + bbt_mark_entry(this, offs + act, BBT_BLOCK_WORN); mtd->ecc_stats.badblocks++; } @@ -287,7 +286,7 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc td, offs); if (res) return res; - offs += this->chipsize >> (this->bbt_erase_shift + 2); + offs += this->chipsize >> this->bbt_erase_shift; } } else { res = read_bbt(mtd, buf, td->pages[0], @@ -511,11 +510,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, } if (chip == -1) { - /* - * Note that numblocks is 2 * (real numblocks) here, see i+=2 - * below as it makes shifting and masking less painful - */ - numblocks = mtd->size >> (this->bbt_erase_shift - 1); + numblocks = mtd->size >> this->bbt_erase_shift; startblock = 0; from = 0; } else { @@ -524,16 +519,16 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, chip + 1, this->numchips); return -EINVAL; } - numblocks = this->chipsize >> (this->bbt_erase_shift - 1); + numblocks = this->chipsize >> this->bbt_erase_shift; startblock = chip * numblocks; numblocks += startblock; - from = (loff_t)startblock << (this->bbt_erase_shift - 1); + from = (loff_t)startblock << this->bbt_erase_shift; } if (this->bbt_options & NAND_BBT_SCANLASTPAGE) from += mtd->erasesize - (mtd->writesize * numpages); - for (i = startblock; i < numblocks;) { + for (i = startblock; i < numblocks; i++) { int ret; BUG_ON(bd->options & NAND_BBT_NO_OOB); @@ -548,13 +543,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, return ret; if (ret) { - bbt_mark_entry(this, i >> 1, BBT_BLOCK_FACTORY_BAD); + bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD); pr_warn("Bad eraseblock %d at 0x%012llx\n", - i >> 1, (unsigned long long)from); + i, (unsigned long long)from); mtd->ecc_stats.badblocks++; } - i += 2; from += (1 << this->bbt_erase_shift); } return 0; @@ -674,9 +668,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, { struct nand_chip *this = mtd->priv; struct erase_info einfo; - int i, j, res, chip = 0; + int i, res, chip = 0; int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; - int nrchips, bbtoffs, pageoffs, ooboffs; + int nrchips, pageoffs, ooboffs; uint8_t msk[4]; uint8_t rcode = td->reserved_block_code; size_t retlen, len = 0; @@ -766,8 +760,6 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, default: return -EINVAL; } - bbtoffs = chip * (numblocks >> 2); - to = ((loff_t)page) << this->page_shift; /* Must we save the block contents? */ @@ -830,16 +822,12 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, buf[ooboffs + td->veroffs] = td->version[chip]; /* Walk through the memory table */ - for (i = 0; i < numblocks;) { + for (i = 0; i < numblocks; i++) { uint8_t dat; - dat = bbt_get_entry(this, (bbtoffs << 2) + i); - for (j = 0; j < 4; j++, i++) { - int sftcnt = (i << (3 - sft)) & sftmsk; - /* Do not store the reserved bbt blocks! */ - buf[offs + (i >> sft)] &= - ~(msk[dat & 0x03] << sftcnt); - dat >>= 2; - } + int sftcnt = (i << (3 - sft)) & sftmsk; + dat = bbt_get_entry(this, chip * numblocks + i); + /* Do not store the reserved bbt blocks! */ + buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt); } memset(&einfo, 0, sizeof(einfo)); @@ -1042,12 +1030,12 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) if (td->pages[i] == -1) continue; block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); - block <<= 1; - oldval = bbt_get_entry(this, block >> 1); - bbt_mark_entry(this, block >> 1, BBT_BLOCK_RESERVED); + oldval = bbt_get_entry(this, block); + bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); if ((oldval != BBT_BLOCK_RESERVED) && td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)block << + this->bbt_erase_shift); continue; } update = 0; @@ -1055,13 +1043,12 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) block = ((i + 1) * nrblocks) - td->maxblocks; else block = i * nrblocks; - block <<= 1; for (j = 0; j < td->maxblocks; j++) { - oldval = bbt_get_entry(this, block >> 1); - bbt_mark_entry(this, block >> 1, BBT_BLOCK_RESERVED); + oldval = bbt_get_entry(this, block); + bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); if (oldval != BBT_BLOCK_RESERVED) update = 1; - block += 2; + block++; } /* * If we want reserved blocks to be recorded to flash, and some @@ -1069,7 +1056,8 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) * bbts. This should only happen once. */ if (update && td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)(block - 1) << + this->bbt_erase_shift); } } @@ -1374,12 +1362,11 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) int block; uint8_t res; - /* Get block number * 2 */ - block = (int)(offs >> (this->bbt_erase_shift - 1)); - res = bbt_get_entry(this, block >> 1); + block = (int)(offs >> this->bbt_erase_shift); + res = bbt_get_entry(this, block); pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", - (unsigned int)offs, block >> 1, res); + (unsigned int)offs, block, res); switch ((int)res) { case BBT_BLOCK_GOOD: -- cgit v1.2.3 From f421fd7ba180811dfed190c4729371a16b356833 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:22:13 +0100 Subject: mtd: nand: hide in-memory BBT implementation details Linux commit b32843b772db adapted for Barebox: nand_base.c shouldn't have to know the implementation details of nand_bbt's in-memory BBT. Specifically, nand_base shouldn't perform the bit masking and shifting to isolate a BBT entry. Instead, just move some of the BBT code into a new nand_markbad_bbt() interface. This interface allows external users (i.e., nand_base) to mark a single block as bad in the BBT. Then nand_bbt will take care of modifying the in-memory BBT and updating the flash-based BBT (if applicable). Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_base.c | 128 ++++++++++++++++++++++--------------------- drivers/mtd/nand/nand_bbt.c | 49 ++++++++++++++++- include/linux/mtd/nand.h | 4 +- 3 files changed, 115 insertions(+), 66 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 31093858e3..ecbf6c992e 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -36,13 +36,13 @@ #include #include #include -#include +#include #include +#include #include #include #include #include -#include /* Define default oob placement schemes for large and small page devices */ static struct nand_ecclayout nand_oob_8 = { @@ -376,22 +376,20 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) * block table(s) and/or marker(s)). We only allow the hardware driver to * specify how to write bad block markers to OOB (chip->block_markbad). * - * We try operations in the following order, according to our bbt_options - * (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH): + * We try operations in the following order: * (1) erase the affected block, to allow OOB marker to be written cleanly - * (2) update in-memory BBT - * (3) write bad block marker to OOB area of affected block - * (4) update flash-based BBT - * Note that we retain the first error encountered in (3) or (4), finish the + * (2) write bad block marker to OOB area of affected block (unless flag + * NAND_BBT_NO_OOB_BBM is present) + * (3) update the BBT + * Note that we retain the first error encountered in (2) or (3), finish the * procedures, and dump the error in the end. -*/ + */ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; - int block, res, ret = 0; - int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM); + int res, ret = 0; - if (write_oob) { + if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) { struct erase_info einfo; /* Attempt erase before marking OOB */ @@ -400,24 +398,16 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) einfo.addr = ofs; einfo.len = 1 << chip->phys_erase_shift; nand_erase_nand(mtd, &einfo, 0); - } - - /* Get block number */ - block = (int)(ofs >> chip->bbt_erase_shift); - /* Mark block bad in memory-based BBT */ - if (chip->bbt) - chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); - /* Write bad block marker to OOB */ - if (write_oob) { + /* Write bad block marker to OOB */ nand_get_device(mtd, FL_WRITING); ret = chip->block_markbad(mtd, ofs); nand_release_device(mtd); } - /* Update flash-based bad block table */ - if (IS_ENABLED(CONFIG_NAND_BBT) && chip->bbt_options & NAND_BBT_USE_FLASH) { - res = nand_update_bbt(mtd, ofs); + /* Mark block bad in BBT */ + if (chip->bbt) { + res = nand_markbad_bbt(mtd, ofs); if (!ret) ret = res; } @@ -428,6 +418,57 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) return ret; } +/** + * nand_block_markgood_lowlevel - mark a block good + * @mtd: MTD device structure + * @ofs: offset from device start + * + * We try operations in the following order: + * (1) erase the affected block + * (2) check bad block marker + * (3) update the BBT + */ +static int nand_block_markgood_lowlevel(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + bool allow_erasebad; + int ret; + + if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) { + struct erase_info einfo; + + /* Attempt erase possibly bad block */ + allow_erasebad = mtd->allow_erasebad; + mtd->allow_erasebad = true; + memset(&einfo, 0, sizeof(einfo)); + einfo.mtd = mtd; + einfo.addr = ofs; + einfo.len = 1 << chip->phys_erase_shift; + nand_erase_nand(mtd, &einfo, 0); + mtd->allow_erasebad = allow_erasebad; + + /* + * Verify erase succeded. We need to select chip again, + * as nand_erase_nand deselected it. + */ + ret = chip->block_bad(mtd, ofs, 1); + if (ret) + return ret; + } + + /* Mark block good in BBT */ + if (chip->bbt) { + ret = nand_markgood_bbt(mtd, ofs); + if (ret) + return ret; + } + + if (mtd->ecc_stats.badblocks > 0) + mtd->ecc_stats.badblocks--; + + return 0; +} + /** * nand_check_wp - [GENERIC] check if the chip is write protected * @mtd: MTD device structure @@ -471,38 +512,6 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, return chip->block_bad(mtd, ofs, getchip); } -/** - * nand_default_block_markgood - [DEFAULT] mark a block good - * @mtd: MTD device structure - * @ofs: offset from device start - * - * This is the default implementation, which can be overridden by - * a hardware specific driver. -*/ -static __maybe_unused int nand_default_block_markgood(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd->priv; - int block, res, ret = 0; - - /* Get block number */ - block = (int)(ofs >> chip->bbt_erase_shift); - /* Mark block good in memory-based BBT */ - if (chip->bbt) - chip->bbt[block >> 2] &= ~(0x01 << ((block & 0x03) << 1)); - - /* Update flash-based bad block table */ - if (IS_ENABLED(CONFIG_NAND_BBT) && chip->bbt_options & NAND_BBT_USE_FLASH) { - res = nand_update_bbt(mtd, ofs); - if (!ret) - ret = res; - } - - if (!ret) - mtd->ecc_stats.badblocks++; - - return ret; -} - /* Wait for the ready pin, after a command. The timeout is caught later. */ void nand_wait_ready(struct mtd_info *mtd) { @@ -2839,13 +2848,12 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) } /** - * nand_block_markgood - [MTD Interface] Mark block at the given offset as bad + * nand_block_markgood - [MTD Interface] Mark block at the given offset as good * @mtd: MTD device structure * @ofs: offset relative to mtd start */ static int nand_block_markgood(struct mtd_info *mtd, loff_t ofs) { - struct nand_chip *chip = mtd->priv; int ret; if (!IS_ENABLED(CONFIG_MTD_WRITE)) @@ -2859,7 +2867,7 @@ static int nand_block_markgood(struct mtd_info *mtd, loff_t ofs) if (!ret) return 0; - return chip->block_markgood(mtd, ofs); + return nand_block_markgood_lowlevel(mtd, ofs); } /** @@ -2943,8 +2951,6 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) #ifdef CONFIG_MTD_WRITE if (!chip->block_markbad) chip->block_markbad = nand_default_block_markbad; - if (!chip->block_markgood) - chip->block_markgood = nand_default_block_markgood; if (!chip->write_buf) chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; #endif diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 4a0ee1db65..f6beb1dae7 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -87,8 +87,14 @@ static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) static inline void bbt_mark_entry(struct nand_chip *chip, int block, uint8_t mark) { - uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2); - chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk; + /* + * Unlike original Linux implementation, Barebox needs also + * mark block as good again, so mask entry comletely. + */ + int index = block >> BBT_ENTRY_SHIFT; + int shift = (block & BBT_ENTRY_MASK) * 2; + chip->bbt[index] &= ~(BBT_ENTRY_MASK << shift); + chip->bbt[index] |= (mark & BBT_ENTRY_MASK) << shift; } static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) @@ -1183,7 +1189,7 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) } /** - * nand_update_bbt - [NAND Interface] update bad block table(s) + * nand_update_bbt - update bad block table(s) * @mtd: MTD device structure * @offs: the offset of the newly marked block * @@ -1379,6 +1385,43 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) return 1; } +static int nand_mark_bbt(struct mtd_info *mtd, loff_t offs, uint8_t mark) +{ + struct nand_chip *this = mtd->priv; + int block, ret = 0; + + block = (int)(offs >> this->bbt_erase_shift); + + /* Mark bad block in memory */ + bbt_mark_entry(this, block, mark); + + /* Update flash-based bad block table */ + if (this->bbt_options & NAND_BBT_USE_FLASH) + ret = nand_update_bbt(mtd, offs); + + return ret; +} + +/** + * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT + * @mtd: MTD device structure + * @offs: offset of the bad block + */ +int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs) +{ + return nand_mark_bbt(mtd, offs, BBT_BLOCK_WORN); +} + +/** + * nand_markbad_bbt - [NAND Interface] Mark a block good in the BBT + * @mtd: MTD device structure + * @offs: offset of the good block + */ +int nand_markgood_bbt(struct mtd_info *mtd, loff_t offs) +{ + return nand_mark_bbt(mtd, offs, BBT_BLOCK_GOOD); +} + EXPORT_SYMBOL(nand_scan_bbt); EXPORT_SYMBOL(nand_default_bbt); EXPORT_SYMBOL_GPL(nand_update_bbt); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index ec2237f7c9..8caf5edecd 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -398,7 +398,6 @@ struct nand_buffers { * @select_chip: [REPLACEABLE] select chip nr * @block_bad: [REPLACEABLE] check, if the block is bad * @block_markbad: [REPLACEABLE] mark the block bad - * @block_markgood: [REPLACEABLE] mark the block good * @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling * ALE/CLE/nCE. Also used to write command and address * @init_size: [BOARDSPECIFIC] hardwarespecific function for setting @@ -484,7 +483,6 @@ struct nand_chip { void (*select_chip)(struct mtd_info *mtd, int chip); int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); - int (*block_markgood)(struct mtd_info *mtd, loff_t ofs); void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); int (*init_size)(struct mtd_info *mtd, struct nand_chip *this, u8 *id_data); @@ -639,6 +637,8 @@ extern struct nand_manufacturers nand_manuf_ids[]; extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd); extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs); extern int nand_default_bbt(struct mtd_info *mtd); +extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs); +extern int nand_markgood_bbt(struct mtd_info *mtd, loff_t offs); extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt); extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int allowbbt); -- cgit v1.2.3 From ec17281b690b9884558e185721cc3b77c3e2a1ff Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:22:30 +0100 Subject: mtd: nand: remove NAND_BBT_SCANEMPTY Relevant part of Linux commit dad2256269cb: NAND_BBT_SCANEMPTY is a strange, badly-supported option with omap as its single remaining user. NAND_BBT_SCANEMPTY was likely used by accident in omap2[1]. And anyway, omap2 doesn't scan the chip for bad blocks (courtesy of NAND_SKIP_BBTSCAN), and so its use of this option is irrelevant. This patch drops the NAND_BBT_SCANEMPTY option. [1] http://lists.infradead.org/pipermail/linux-mtd/2012-July/042902.html Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_bbt.c | 33 +++++---------------------------- drivers/mtd/nand/nand_omap_gpmc.c | 2 +- include/linux/mtd/bbm.h | 2 -- 3 files changed, 6 insertions(+), 31 deletions(-) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index f6beb1dae7..8e7c8eb336 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -112,33 +112,17 @@ static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) * @td: search pattern descriptor * * Check for a pattern at the given place. Used to search bad block tables and - * good / bad block identifiers. If the SCAN_EMPTY option is set then check, if - * all bytes except the pattern area contain 0xff. + * good / bad block identifiers. */ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) { - int 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) - if (memchr_inv(p, 0xff, end)) - return -1; - p += end; - /* Compare the pattern */ - if (memcmp(p, td->pattern, td->len)) + if (memcmp(buf + paglen + td->offs, td->pattern, td->len)) return -1; - if (td->options & NAND_BBT_SCANEMPTY) { - p += td->len; - end += td->len; - if (memchr_inv(p, 0xff, len - end)) - return -1; - } return 0; } @@ -505,15 +489,9 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, else numpages = 1; - if (!(bd->options & NAND_BBT_SCANEMPTY)) { - /* We need only read few bytes from the OOB area */ - scanlen = 0; - readlen = bd->len; - } else { - /* Full page content should be read */ - scanlen = mtd->writesize + mtd->oobsize; - readlen = numpages * mtd->writesize; - } + /* We need only read few bytes from the OOB area */ + scanlen = 0; + readlen = bd->len; if (chip == -1) { numblocks = mtd->size >> this->bbt_erase_shift; @@ -875,7 +853,6 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b { struct nand_chip *this = mtd->priv; - bd->options &= ~NAND_BBT_SCANEMPTY; return create_bbt(mtd, this->buffers->databuf, bd, -1); } diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c index 323a9c7532..dca1a60bee 100644 --- a/drivers/mtd/nand/nand_omap_gpmc.c +++ b/drivers/mtd/nand/nand_omap_gpmc.c @@ -125,7 +125,7 @@ static struct nand_ecclayout omap_oobinfo; */ static uint8_t scan_ff_pattern[] = { 0xff }; static struct nand_bbt_descr bb_descrip_flashbased = { - .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .options = NAND_BBT_SCANALLPAGES, .offs = 0, .len = 1, .pattern = scan_ff_pattern, diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 211ff67e8b..95fc482cef 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -93,8 +93,6 @@ struct nand_bbt_descr { #define NAND_BBT_CREATE_EMPTY 0x00000400 /* Search good / bad pattern through all pages of a block */ #define NAND_BBT_SCANALLPAGES 0x00000800 -/* Scan block empty during good / bad block scan */ -#define NAND_BBT_SCANEMPTY 0x00001000 /* Write bbt if neccecary */ #define NAND_BBT_WRITE 0x00002000 /* Read and write back block contents when writing bbt */ -- cgit v1.2.3 From 3c9c9a6fee481e7d365455625e69f398680127e8 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:22:57 +0100 Subject: mtd: nand: Request strength instead of bytes for soft BCH Linux commits e0377cdebaf3 and 438320dd34a4 combined and adapted for Barebox: Previously, we requested that drivers pass ecc.size and ecc.bytes when using NAND_ECC_SOFT_BCH. However, a driver is likely to only know the ECC strength required for its NAND, so each driver would need to perform a strength-to-bytes calculation. Avoid duplicating this calculation in each driver by asking drivers to pass ecc.size and ecc.strength so that the strength-to-bytes calculation need only be implemented once. Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_base.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ecbf6c992e..faf8f2aea2 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3698,15 +3698,18 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.read_oob = nand_read_oob_std; chip->ecc.write_oob = nand_write_oob_std; /* - * Board driver should supply ecc.size and ecc.bytes values to - * select how many bits are correctable; see nand_bch_init() - * for details. Otherwise, default to 4 bits for large page - * devices. + * Board driver should supply ecc.size and ecc.strength values + * to select how many bits are correctable. Otherwise, default + * to 4 bits for large page devices. */ if (!chip->ecc.size && (mtd->oobsize >= 64)) { chip->ecc.size = 512; - chip->ecc.bytes = 7; + chip->ecc.strength = 4; } + + /* See nand_bch_init() for details. */ + chip->ecc.bytes = DIV_ROUND_UP( + chip->ecc.strength * fls(8 * chip->ecc.size), 8); chip->ecc.priv = nand_bch_init(mtd, chip->ecc.size, chip->ecc.bytes, @@ -3715,8 +3718,6 @@ int nand_scan_tail(struct mtd_info *mtd) pr_warn("BCH ECC initialization failed!\n"); BUG(); } - chip->ecc.strength = - chip->ecc.bytes * 8 / fls(8 * chip->ecc.size); break; #endif #ifdef CONFIG_NAND_ECC_NONE -- cgit v1.2.3 From babffbb19350d0119f128390c80101dbedd4dbca Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:23:22 +0100 Subject: mtd: atmel_nand: Add per board ECC setup Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- arch/arm/mach-at91/include/mach/board.h | 2 ++ drivers/mtd/nand/atmel_nand.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index 0f2c269732..886f81e9ad 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -64,6 +64,8 @@ struct atmel_nand_data { u8 cle; /* address line number connected to CLE */ u8 bus_width_16; /* buswidth is 16 bit */ u8 ecc_mode; /* NAND_ECC_* */ + u8 ecc_strength; /* number of bits to correct per ECC step */ + u8 ecc_size_shift; /* data bytes covered by a single ECC step.*/ u8 on_flash_bbt; /* Use flash based bbt */ u8 has_pmecc; /* Use PMECC */ u8 bus_on_d0; diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 4c1725d096..b4d4e79d91 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1297,6 +1297,7 @@ static int atmel_hw_nand_init_params(struct device_d *dev, nand_chip->ecc.hwctl = atmel_nand_hwctl; nand_chip->ecc.read_page = atmel_nand_read_page; nand_chip->ecc.bytes = 4; + nand_chip->ecc.strength = 1; return 0; } @@ -1387,6 +1388,8 @@ static int __init atmel_nand_probe(struct device_d *dev) } nand_chip->ecc.mode = pdata->ecc_mode; + nand_chip->ecc.strength = pdata->ecc_strength ? : 1; + nand_chip->ecc.size = 1 << pdata->ecc_size_shift ? : 512; if (IS_ENABLED(CONFIG_NAND_ECC_HW) && pdata->ecc_mode == NAND_ECC_HW) { -- cgit v1.2.3 From 939a4e9dc0e6a014ad1d2c73b170cc18bf27fad9 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:23:50 +0100 Subject: mtd: nand: simplify nand_bch_init() usage Linux commit a8c65d504e0b modified for Barebox: nand_bch_init() requires several arguments which could directly be deduced from the mtd device. Get rid of those useless parameters. nand_bch_init() is also requiring the caller to provide a proper eccbytes value, while this value could be deduced from the ecc.size and ecc.strength value. Fallback to eccbytes calculation when it is set to 0. Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_base.c | 8 ++------ drivers/mtd/nand/nand_bch.c | 27 +++++++++++++++++---------- include/linux/mtd/nand_bch.h | 8 ++------ 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index faf8f2aea2..12455b45a2 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3708,12 +3708,8 @@ int nand_scan_tail(struct mtd_info *mtd) } /* See nand_bch_init() for details. */ - chip->ecc.bytes = DIV_ROUND_UP( - chip->ecc.strength * fls(8 * chip->ecc.size), 8); - chip->ecc.priv = nand_bch_init(mtd, - chip->ecc.size, - chip->ecc.bytes, - &chip->ecc.layout); + chip->ecc.bytes = 0; + chip->ecc.priv = nand_bch_init(mtd); if (!chip->ecc.priv) { pr_warn("BCH ECC initialization failed!\n"); BUG(); diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c index e37135f514..d4f2a8cbe4 100644 --- a/drivers/mtd/nand/nand_bch.c +++ b/drivers/mtd/nand/nand_bch.c @@ -104,9 +104,6 @@ EXPORT_SYMBOL(nand_bch_correct_data); /** * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction * @mtd: MTD block structure - * @eccsize: ecc block size in bytes - * @eccbytes: ecc length in bytes - * @ecclayout: output default layout * * Returns: * a pointer to a new NAND BCH control structure, or NULL upon failure @@ -120,14 +117,21 @@ EXPORT_SYMBOL(nand_bch_correct_data); * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) */ -struct nand_bch_control * -nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes, - struct nand_ecclayout **ecclayout) +struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) { + struct nand_chip *nand = mtd->priv; unsigned int m, t, eccsteps, i; - struct nand_ecclayout *layout; + struct nand_ecclayout *layout = nand->ecc.layout; struct nand_bch_control *nbc = NULL; unsigned char *erased_page; + unsigned int eccsize = nand->ecc.size; + unsigned int eccbytes = nand->ecc.bytes; + unsigned int eccstrength = nand->ecc.strength; + + if (!eccbytes && eccstrength) { + eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); + nand->ecc.bytes = eccbytes; + } if (!eccsize || !eccbytes) { printk(KERN_WARNING "ecc parameters not supplied\n"); @@ -155,7 +159,7 @@ nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes, eccsteps = mtd->writesize/eccsize; /* if no ecc placement scheme was provided, build one */ - if (!*ecclayout) { + if (!layout) { /* handle large page devices only */ if (mtd->oobsize < 64) { @@ -181,7 +185,7 @@ nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes, layout->oobfree[0].offset = 2; layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; - *ecclayout = layout; + nand->ecc.layout = layout; } /* sanity checks */ @@ -189,7 +193,7 @@ nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes, printk(KERN_WARNING "eccsize %u is too large\n", eccsize); goto fail; } - if ((*ecclayout)->eccbytes != (eccsteps*eccbytes)) { + if (layout->eccbytes != (eccsteps*eccbytes)) { printk(KERN_WARNING "invalid ecc layout\n"); goto fail; } @@ -213,6 +217,9 @@ nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes, for (i = 0; i < eccbytes; i++) nbc->eccmask[i] ^= 0xff; + if (!eccstrength) + nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); + return nbc; fail: nand_bch_free(nbc); diff --git a/include/linux/mtd/nand_bch.h b/include/linux/mtd/nand_bch.h index 61c4607fa8..5465ddd132 100644 --- a/include/linux/mtd/nand_bch.h +++ b/include/linux/mtd/nand_bch.h @@ -32,9 +32,7 @@ int nand_bch_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, /* * Initialize BCH encoder/decoder */ -struct nand_bch_control * -nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, - unsigned int eccbytes, struct nand_ecclayout **ecclayout); +struct nand_bch_control *nand_bch_init(struct mtd_info *mtd); /* * Release BCH encoder/decoder resources */ @@ -58,9 +56,7 @@ nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, return -1; } -static inline struct nand_bch_control * -nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, - unsigned int eccbytes, struct nand_ecclayout **ecclayout) +static inline struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) { return NULL; } -- cgit v1.2.3 From 33f3c16b56cde630a1de7791a8723905fce6fdb2 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:24:21 +0100 Subject: mtd: nand_bbt: kill NAND_BBT_SCANALLPAGES Linux commit 5961ad2cb4dd adapted for Barebox: Now that the last user of NAND_BBT_SCANALLPAGES has been removed, let's kill this peculiar BBT feature flag. Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_bbt.c | 37 +++---------------------------------- drivers/mtd/nand/nand_omap_gpmc.c | 1 - include/linux/mtd/bbm.h | 2 -- 3 files changed, 3 insertions(+), 37 deletions(-) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 8e7c8eb336..467797b438 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -410,25 +410,6 @@ static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, } } -/* Scan a given block full */ -static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, - loff_t offs, uint8_t *buf, size_t readlen, - int scanlen, int numpages) -{ - int ret, j; - - ret = scan_read_oob(mtd, buf, offs, readlen); - /* Ignore ECC errors when checking for BBM */ - if (ret && !mtd_is_bitflip_or_eccerr(ret)) - return ret; - - for (j = 0; j < numpages; j++, buf += scanlen) { - if (check_pattern(buf, scanlen, mtd->writesize, bd)) - return 1; - } - return 0; -} - /* Scan a given block partially */ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, loff_t offs, uint8_t *buf, int numpages) @@ -475,24 +456,17 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) { struct nand_chip *this = mtd->priv; - int i, numblocks, numpages, scanlen; + int i, numblocks, numpages; int startblock; loff_t from; - size_t readlen; pr_info("Scanning device for bad blocks\n"); - if (bd->options & NAND_BBT_SCANALLPAGES) - numpages = 1 << (this->bbt_erase_shift - this->page_shift); - else if (bd->options & NAND_BBT_SCAN2NDPAGE) + if (bd->options & NAND_BBT_SCAN2NDPAGE) numpages = 2; else numpages = 1; - /* We need only read few bytes from the OOB area */ - scanlen = 0; - readlen = bd->len; - if (chip == -1) { numblocks = mtd->size >> this->bbt_erase_shift; startblock = 0; @@ -517,12 +491,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, BUG_ON(bd->options & NAND_BBT_NO_OOB); - if (bd->options & NAND_BBT_SCANALLPAGES) - ret = scan_block_full(mtd, bd, from, buf, readlen, - scanlen, numpages); - else - ret = scan_block_fast(mtd, bd, from, buf, numpages); - + ret = scan_block_fast(mtd, bd, from, buf, numpages); if (ret < 0) return ret; diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c index dca1a60bee..31edb91238 100644 --- a/drivers/mtd/nand/nand_omap_gpmc.c +++ b/drivers/mtd/nand/nand_omap_gpmc.c @@ -125,7 +125,6 @@ static struct nand_ecclayout omap_oobinfo; */ static uint8_t scan_ff_pattern[] = { 0xff }; static struct nand_bbt_descr bb_descrip_flashbased = { - .options = NAND_BBT_SCANALLPAGES, .offs = 0, .len = 1, .pattern = scan_ff_pattern, diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 95fc482cef..36bb6a503f 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -91,8 +91,6 @@ struct nand_bbt_descr { * with NAND_BBT_CREATE. */ #define NAND_BBT_CREATE_EMPTY 0x00000400 -/* Search good / bad pattern through all pages of a block */ -#define NAND_BBT_SCANALLPAGES 0x00000800 /* Write bbt if neccecary */ #define NAND_BBT_WRITE 0x00002000 /* Read and write back block contents when writing bbt */ -- cgit v1.2.3 From d806765bd117ddf5db47fa652be818bd607b2d14 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:24:44 +0100 Subject: mtd: nand_bbt: handle error case for nand_create_badblock_pattern() Linux commit abb9cf78e80a adapted for Barebox. Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_bbt.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 467797b438..dc33f15ba5 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -1278,6 +1278,7 @@ static int nand_create_badblock_pattern(struct nand_chip *this) int nand_default_bbt(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; + int ret; /* Is a flash based bad block table requested? */ if (this->bbt_options & NAND_BBT_USE_FLASH) { @@ -1296,8 +1297,11 @@ int nand_default_bbt(struct mtd_info *mtd) this->bbt_md = NULL; } - if (!this->badblock_pattern) - nand_create_badblock_pattern(this); + if (!this->badblock_pattern) { + ret = nand_create_badblock_pattern(this); + if (ret) + return ret; + } return nand_scan_bbt(mtd, this->badblock_pattern); } -- cgit v1.2.3 From 5664a5e046684745a760b8aa58f398869fe74e1c Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:25:11 +0100 Subject: mtd: nand_bbt: make nand_scan_bbt() static Linux commit 17799359e7b adapted for Barebox: This implementation detail is no longer needed outside of nand_bbt.c. Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_bbt.c | 2 +- include/linux/mtd/nand.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index dc33f15ba5..7fa920ff18 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -1073,7 +1073,7 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) * The bad block table memory is allocated here. It must be freed by calling * the nand_free_bbt function. */ -int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct nand_chip *this = mtd->priv; int len, res = 0; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 8caf5edecd..ed70c18a1e 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -634,7 +634,6 @@ struct nand_manufacturers { extern struct nand_flash_dev nand_flash_ids[]; extern struct nand_manufacturers nand_manuf_ids[]; -extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd); extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs); extern int nand_default_bbt(struct mtd_info *mtd); extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs); -- cgit v1.2.3 From 597a2b595c2db5249ef6e4fd5e178724523471aa Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:25:39 +0100 Subject: mtd: nand_bbt: unify/fix error handling in nand_scan_bbt() Linux commit 83c59542d0af adapted for Barebox: Don't leak this->bbt, and return early if check_create() fails. It helps to have a single error path to avoid these problems. Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_bbt.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 7fa920ff18..94fe02f587 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -1076,7 +1076,7 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct nand_chip *this = mtd->priv; - int len, res = 0; + int len, res; uint8_t *buf; struct nand_bbt_descr *td = this->bbt_td; struct nand_bbt_descr *md = this->bbt_md; @@ -1097,10 +1097,9 @@ static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) if (!td) { if ((res = nand_memory_bbt(mtd, bd))) { pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); - kfree(this->bbt); - this->bbt = NULL; + goto err; } - return res; + return 0; } verify_bbt_descr(mtd, td); verify_bbt_descr(mtd, md); @@ -1110,9 +1109,8 @@ static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) len += (len >> this->page_shift) * mtd->oobsize; buf = vmalloc(len); if (!buf) { - kfree(this->bbt); - this->bbt = NULL; - return -ENOMEM; + res = -ENOMEM; + goto err; } /* Is the bbt at a given page? */ @@ -1124,6 +1122,8 @@ static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) } res = check_create(mtd, buf, bd); + if (res) + goto err; /* Prevent the bbt regions from erasing / writing */ mark_bbt_region(mtd, td); @@ -1131,6 +1131,11 @@ static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) mark_bbt_region(mtd, md); vfree(buf); + return 0; + +err: + kfree(this->bbt); + this->bbt = NULL; return res; } -- cgit v1.2.3 From b8571beadec61831629fbab61661fd9569096468 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:25:56 +0100 Subject: mtd: nand_bbt: Move BBT block selection logic out of write_bbt() Linux commit c3baf278d3bf adapted for Barebox: This clarifies the write_bbt() function by removing the write label and simplifying the error/exit path. Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_bbt.c | 110 +++++++++++++++++++++++++++++--------------- 1 file changed, 74 insertions(+), 36 deletions(-) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 94fe02f587..23f57131ef 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -605,6 +605,69 @@ static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf, search_bbt(mtd, buf, md); } +/** + * get_bbt_block - Get the first valid eraseblock suitable to store a BBT + * @this: the NAND device + * @td: the BBT description + * @md: the mirror BBT descriptor + * @chip: the CHIP selector + * + * This functions returns a positive block number pointing a valid eraseblock + * suitable to store a BBT (i.e. in the range reserved for BBT), or -ENOSPC if + * all blocks are already used of marked bad. If td->pages[chip] was already + * pointing to a valid block we re-use it, otherwise we search for the next + * valid one. + */ +static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td, + struct nand_bbt_descr *md, int chip) +{ + int startblock, dir, page, numblocks, i; + + /* + * There was already a version of the table, reuse the page. This + * applies for absolute placement too, as we have the page number in + * td->pages. + */ + if (td->pages[chip] != -1) + return td->pages[chip] >> + (this->bbt_erase_shift - this->page_shift); + + numblocks = (int)(this->chipsize >> this->bbt_erase_shift); + if (!(td->options & NAND_BBT_PERCHIP)) + numblocks *= this->numchips; + + /* + * Automatic placement of the bad block table. Search direction + * top -> down? + */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = numblocks * (chip + 1) - 1; + dir = -1; + } else { + startblock = chip * numblocks; + dir = 1; + } + + for (i = 0; i < td->maxblocks; i++) { + int block = startblock + dir * i; + + /* Check, if the block is bad */ + switch (bbt_get_entry(this, block)) { + case BBT_BLOCK_WORN: + case BBT_BLOCK_FACTORY_BAD: + continue; + } + + page = block << (this->bbt_erase_shift - this->page_shift); + + /* Check, if the block is used by the mirror table */ + if (!md || md->pages[chip] != page) + return block; + } + + return -ENOSPC; +} + /** * write_bbt - [GENERIC] (Re)write the bad block table * @mtd: MTD device structure @@ -622,7 +685,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_chip *this = mtd->priv; struct erase_info einfo; int i, res, chip = 0; - int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; + int bits, page, offs, numblocks, sft, sftmsk; int nrchips, pageoffs, ooboffs; uint8_t msk[4]; uint8_t rcode = td->reserved_block_code; @@ -654,45 +717,20 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, /* Loop through the chips */ for (; chip < nrchips; chip++) { - /* - * There was already a version of the table, reuse the page - * This applies for absolute placement too, as we have the - * page nr. in td->pages. - */ - if (td->pages[chip] != -1) { - page = td->pages[chip]; - goto write; + int block; + + block = get_bbt_block(this, td, md, chip); + if (block < 0) { + pr_err("No space left to write bad block table\n"); + res = block; + goto outerr; } /* - * Automatic placement of the bad block table. Search direction - * top -> down? + * get_bbt_block() returns a block number, shift the value to + * get a page number. */ - if (td->options & NAND_BBT_LASTBLOCK) { - startblock = numblocks * (chip + 1) - 1; - dir = -1; - } else { - startblock = chip * numblocks; - dir = 1; - } - - for (i = 0; i < td->maxblocks; i++) { - int block = startblock + dir * i; - /* Check, if the block is bad */ - switch (bbt_get_entry(this, block)) { - case BBT_BLOCK_WORN: - case BBT_BLOCK_FACTORY_BAD: - continue; - } - page = block << - (this->bbt_erase_shift - this->page_shift); - /* Check, if the block is used by the mirror table */ - if (!md || md->pages[chip] != page) - goto write; - } - pr_err("No space left to write bad block table\n"); - return -ENOSPC; - write: + page = block << (this->bbt_erase_shift - this->page_shift); /* Set up shift count and masks for the flash table */ bits = td->options & NAND_BBT_NRBITS_MSK; -- cgit v1.2.3 From eaea56bc2a30ff9d6a43b16951c0bf73d8c462d6 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:26:23 +0100 Subject: mtd: nand_bbt: scan for next free bbt block if writing bbt fails Linux commit 10ffd570f117 adapted for Barebox: If erasing or writing the BBT fails, we should mark the current BBT block as bad and use the BBT descriptor to scan for the next available unused block in the BBT. We should only return a failure if there isn't any space left. Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_bbt.c | 51 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 23f57131ef..a908a36544 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -668,6 +668,37 @@ static int get_bbt_block(struct nand_chip *this, struct nand_bbt_descr *td, return -ENOSPC; } +/** + * mark_bbt_block_bad - Mark one of the block reserved for BBT bad + * @mtd: the MTD device + * @td: the BBT description + * @chip: the CHIP selector + * @block: the BBT block to mark + * + * Blocks reserved for BBT can become bad. This functions is an helper to mark + * such blocks as bad. It takes care of updating the in-memory BBT, marking the + * block as bad using a bad block marker and invalidating the associated + * td->pages[] entry. + */ +static void mark_bbt_block_bad(struct mtd_info *mtd, + struct nand_bbt_descr *td, + int chip, int block) +{ + struct nand_chip *this = mtd->priv; + loff_t to; + int res; + + bbt_mark_entry(this, block, BBT_BLOCK_WORN); + + to = (loff_t)block << this->bbt_erase_shift; + res = this->block_markbad(mtd, to); + if (res) + pr_warn("nand_bbt: error %d while marking block %d bad\n", + res, block); + + td->pages[chip] = -1; +} + /** * write_bbt - [GENERIC] (Re)write the bad block table * @mtd: MTD device structure @@ -716,7 +747,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, } /* Loop through the chips */ - for (; chip < nrchips; chip++) { + while (chip < nrchips) { int block; block = get_bbt_block(this, td, md, chip); @@ -826,20 +857,28 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, einfo.addr = to; einfo.len = 1 << this->bbt_erase_shift; res = nand_erase_nand(mtd, &einfo, 1); - if (res < 0) - goto outerr; + if (res < 0) { + pr_warn("nand_bbt: error while erasing BBT block %d\n", + res); + mark_bbt_block_bad(mtd, td, chip, block); + continue; + } res = scan_write_bbt(mtd, to, len, buf, td->options & NAND_BBT_NO_OOB ? NULL : &buf[len]); - if (res < 0) - goto outerr; + if (res < 0) { + pr_warn("nand_bbt: error while writing BBT block %d\n", + res); + mark_bbt_block_bad(mtd, td, chip, block); + continue; + } pr_info("Bad block table written to 0x%012llx, version 0x%02X\n", (unsigned long long)to, td->version[chip]); /* Mark it as used */ - td->pages[chip] = page; + td->pages[chip++] = page; } return 0; -- cgit v1.2.3 From 95ce69795506293eae28d6e64055d2c7ae27f164 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:26:38 +0100 Subject: mtd: nand: Kill the chip->scan_bbt() hook Linux commit e80eba758151 adapted for Barebox: None of the existing drivers are overloading the ->scan_bbt() method, let's get rid of it and replace calls to ->scan_bbt() by nand_create_bbt() ones. Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_base.c | 6 +----- drivers/mtd/nand/nand_bbt.c | 10 +++++----- drivers/mtd/nand/nand_imx_bbm.c | 2 +- drivers/mtd/nand/nand_mxs.c | 27 +++++++++------------------ include/linux/mtd/nand.h | 5 +---- 5 files changed, 17 insertions(+), 33 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 12455b45a2..d9201d9fbc 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2956,10 +2956,6 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) #endif if (!chip->read_buf) chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; -#ifdef CONFIG_NAND_BBT - if (!chip->scan_bbt) - chip->scan_bbt = nand_default_bbt; -#endif if (!chip->controller) { chip->controller = &chip->hwcontrol; } @@ -3825,7 +3821,7 @@ int nand_scan_tail(struct mtd_info *mtd) return 0; /* Build bad block table */ - return chip->scan_bbt(mtd); + return nand_create_bbt(mtd); } EXPORT_SYMBOL(nand_scan_tail); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index a908a36544..90c10862c5 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -77,6 +77,8 @@ #define BBT_ENTRY_MASK 0x03 #define BBT_ENTRY_SHIFT 2 +static int nand_update_bbt(struct mtd_info *mtd, loff_t offs); + static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) { uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; @@ -1223,7 +1225,7 @@ err: * * The function updates the bad block table(s). */ -int nand_update_bbt(struct mtd_info *mtd, loff_t offs) +static int nand_update_bbt(struct mtd_info *mtd, loff_t offs) { struct nand_chip *this = mtd->priv; int len, res = 0; @@ -1351,13 +1353,13 @@ static int nand_create_badblock_pattern(struct nand_chip *this) } /** - * nand_default_bbt - [NAND Interface] Select a default bad block table for the device + * nand_create_bbt - [NAND Interface] Select a default bad block table for the device * @mtd: MTD device structure * * This function selects the default bad block table support for the device and * calls the nand_scan_bbt function. */ -int nand_default_bbt(struct mtd_info *mtd) +int nand_create_bbt(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; int ret; @@ -1455,5 +1457,3 @@ int nand_markgood_bbt(struct mtd_info *mtd, loff_t offs) } EXPORT_SYMBOL(nand_scan_bbt); -EXPORT_SYMBOL(nand_default_bbt); -EXPORT_SYMBOL_GPL(nand_update_bbt); diff --git a/drivers/mtd/nand/nand_imx_bbm.c b/drivers/mtd/nand/nand_imx_bbm.c index 23722a9064..4fd5487aa2 100644 --- a/drivers/mtd/nand/nand_imx_bbm.c +++ b/drivers/mtd/nand/nand_imx_bbm.c @@ -129,7 +129,7 @@ static int attach_bbt(struct mtd_info *mtd, void *bbt) free(chip->bbt); chip->bbt = bbt; - return nand_update_bbt(mtd, 0); + return nand_create_bbt(mtd); } static int do_imx_nand_bbm(int argc, char *argv[]) diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/nand_mxs.c index 28a07d4cba..f69453aba5 100644 --- a/drivers/mtd/nand/nand_mxs.c +++ b/drivers/mtd/nand/nand_mxs.c @@ -1201,21 +1201,7 @@ static int mxs_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) return 0; } -/* - * Nominally, the purpose of this function is to look for or create the bad - * block table. In fact, since the we call this function at the very end of - * the initialization process started by nand_scan(), and we doesn't have a - * more formal mechanism, we "hook" this function to continue init process. - * - * At this point, the physical NAND Flash chips have been identified and - * counted, so we know the physical geometry. This enables us to make some - * important configuration decisions. - * - * The return value of this function propogates directly back to this driver's - * call to nand_scan(). Anything other than zero will cause this driver to - * tear everything down and declare failure. - */ -static int mxs_nand_scan_bbt(struct mtd_info *mtd) +static int mxs_nand_init_bch(struct mtd_info *mtd) { struct nand_chip *nand = mtd->priv; struct mxs_nand_info *nand_info = nand->priv; @@ -1252,8 +1238,7 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd) mtd->block_markbad = mxs_nand_hook_block_markbad; } - /* We use the reference implementation for bad block management. */ - return nand_default_bbt(mtd); + return 0; } /* @@ -2183,7 +2168,6 @@ static int mxs_nand_probe(struct device_d *dev) nand->dev_ready = mxs_nand_device_ready; nand->select_chip = mxs_nand_select_chip; nand->block_bad = mxs_nand_block_bad; - nand->scan_bbt = mxs_nand_scan_bbt; nand->read_byte = mxs_nand_read_byte; @@ -2215,6 +2199,13 @@ static int mxs_nand_probe(struct device_d *dev) mxs_nand_setup_timing(nand_info); + err = mxs_nand_init_bch(mtd); + if (err) + goto err2; + err = nand_create_bbt(mtd); + if (err) + goto err2; + /* second phase scan */ err = nand_scan_tail(mtd); if (err) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index ed70c18a1e..e1c7837a8b 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -417,7 +417,6 @@ struct nand_buffers { * @hwcontrol: platform-specific hardware control structure * @erase_cmd: [INTERN] erase command write function, selectable due * to AND support. - * @scan_bbt: [REPLACEABLE] function to scan bad block table * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transferring * data from array to read regs (tR). * @state: [INTERN] the current state of the NAND device @@ -491,7 +490,6 @@ struct nand_chip { int page_addr); int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this); void (*erase_cmd)(struct mtd_info *mtd, int page); - int (*scan_bbt)(struct mtd_info *mtd); int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, @@ -634,8 +632,7 @@ struct nand_manufacturers { extern struct nand_flash_dev nand_flash_ids[]; extern struct nand_manufacturers nand_manuf_ids[]; -extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs); -extern int nand_default_bbt(struct mtd_info *mtd); +extern int nand_create_bbt(struct mtd_info *mtd); extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs); extern int nand_markgood_bbt(struct mtd_info *mtd, loff_t offs); extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt); -- cgit v1.2.3 From 7a62820664944b42b0cdea6bf06d5df2910ca7aa Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:27:02 +0100 Subject: mtd: nand: Kill cellinfo The only information used from cellinfo field is whenever flash is SLC or MLC, therefore eliminate it completely. This patch is based on Linux commit 7db906b79f69. Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_base.c | 29 ++++++++++++++++++++--------- include/linux/mtd/nand.h | 3 +-- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index d9201d9fbc..c42b1aae5f 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3119,6 +3119,16 @@ static int nand_id_len(u8 *id_data, int arrlen) return arrlen; } +/* Extract the bits of per cell from the 3rd byte of the extended ID */ +static int nand_get_bits_per_cell(u8 cellinfo) +{ + int bits; + + bits = cellinfo & NAND_CI_CELLTYPE_MSK; + bits >>= NAND_CI_CELLTYPE_SHIFT; + return bits + 1; +} + /* * Many new NAND share similar device ID codes, which represent the size of the * chip. The rest of the parameters must be decoded according to generic or @@ -3129,7 +3139,7 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, { int extid, id_len; /* The 3rd id byte holds MLC / multichip data */ - chip->cellinfo = id_data[2]; + chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); /* The 4th id byte is the important one */ extid = id_data[3]; @@ -3145,8 +3155,7 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, * ID to decide what to do. */ if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG && - (chip->cellinfo & NAND_CI_CELLTYPE_MSK) && - id_data[5] != 0x00) { + !nand_is_slc(chip) && id_data[5] != 0x00) { /* Calc pagesize */ mtd->writesize = 2048 << (extid & 0x03); extid >>= 2; @@ -3178,7 +3187,7 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, (((extid >> 1) & 0x04) | (extid & 0x03)); *busw = 0; } else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX && - (chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { + !nand_is_slc(chip)) { unsigned int tmp; /* Calc pagesize */ @@ -3250,6 +3259,9 @@ static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip, mtd->oobsize = mtd->writesize / 32; *busw = type->options & NAND_BUSWIDTH_16; + /* All legacy ID NAND are small-page, SLC */ + chip->bits_per_cell = 1; + /* * Check for Spansion/AMD ID + repeating 5th, 6th byte since * some Spansion chips have erasesize that conflicts with size @@ -3286,11 +3298,11 @@ static void nand_decode_bbm_options(struct mtd_info *mtd, * Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba, * AMD/Spansion, and Macronix. All others scan only the first page. */ - if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + if (!nand_is_slc(chip) && (maf_id == NAND_MFR_SAMSUNG || maf_id == NAND_MFR_HYNIX)) chip->bbt_options |= NAND_BBT_SCANLASTPAGE; - else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + else if ((nand_is_slc(chip) && (maf_id == NAND_MFR_SAMSUNG || maf_id == NAND_MFR_HYNIX || maf_id == NAND_MFR_TOSHIBA || @@ -3314,7 +3326,7 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, mtd->erasesize = type->erasesize; mtd->oobsize = type->oobsize; - chip->cellinfo = id_data[2]; + chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); chip->chipsize = (uint64_t)type->chipsize << 20; chip->options |= type->options; @@ -3760,8 +3772,7 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ - if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && - !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { + if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { switch (chip->ecc.steps) { case 2: mtd->subpage_sft = 1; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index e1c7837a8b..c3eb16f906 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -203,6 +203,7 @@ typedef enum { /* Cell info constants */ #define NAND_CI_CHIPNR_MSK 0x03 #define NAND_CI_CELLTYPE_MSK 0x0C +#define NAND_CI_CELLTYPE_SHIFT 2 /* Keep gcc happy */ struct nand_chip; @@ -439,7 +440,6 @@ struct nand_buffers { * bad block marker position; i.e., BBM == 11110111b is * not bad when badblockbits == 7 * @bits_per_cell: [INTERN] number of bits per cell. i.e., 1 means SLC. - * @cellinfo: [INTERN] MLC/multichip data from chip ident * @numchips: [INTERN] number of physical chips * @chipsize: [INTERN] the size of one chip for multichip arrays * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 @@ -515,7 +515,6 @@ struct nand_chip { unsigned int pagebuf_bitflips; int subpagesize; uint8_t bits_per_cell; - uint8_t cellinfo; int badblockpos; int badblockbits; -- cgit v1.2.3 From 0d326d8beb2d75d5bd9d0a8a5f1ab7714840f016 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 28 Oct 2018 22:27:38 +0100 Subject: mtd: nand: detect OOB size for Toshiba 24nm raw SLC Linux commit 60c673824561. Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_base.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index c42b1aae5f..05bfac15f8 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3240,6 +3240,20 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, extid >>= 2; /* Get buswidth information */ *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + /* + * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per + * 512B page. For Toshiba SLC, we decode the 5th/6th byte as + * follows: + * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm, + * 110b -> 24nm + * - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC + */ + if (id_len >= 6 && id_data[0] == NAND_MFR_TOSHIBA && + nand_is_slc(chip) && + (id_data[5] & 0x7) == 0x6 /* 24nm */ && + !(id_data[4] & 0x80) /* !BENAND */) { + mtd->oobsize = 32 * mtd->writesize >> 9; + } } } -- cgit v1.2.3 From a550aaba61974fef8eeb25b8e91e05a0255a1516 Mon Sep 17 00:00:00 2001 From: Teresa Remmet Date: Mon, 17 Dec 2018 16:43:43 +0100 Subject: mtd: nand: nand_base: Fix compile warning if CONFIG_MTD_WRITE is not enabled drivers/mtd/nand/nand_base.c:337:12: warning: 'nand_default_block_markbad' defined but not used [-Wunused-function] Add __maybe_unused again to silence warning when CONFIG_MTD_WRITE is not enabled. Signed-off-by: Teresa Remmet Signed-off-by: Sascha Hauer --- drivers/mtd/nand/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 05bfac15f8..8ae6f34468 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -334,7 +334,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) * specific driver. It provides the details for writing a bad block marker to a * block. */ -static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +static __maybe_unused int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; struct mtd_oob_ops ops; -- cgit v1.2.3 From 9d0e9062cae80554112d8a6d94a0abc5aa446e8e Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Wed, 9 Jan 2019 12:28:14 +0100 Subject: mtd: core: Fix erase area alignment for non power of 2 erasesize Devices as AT45DB161 DataFlash uses non power of two page size (528) while present alignment algorithm relies on erasesize being power of 2. Fix that by introducing helper functions rounding to any multiply. Note that logic is sligthly changed to be consistent as ending address is moved forward to include also last byte meant to be erased while previous implementation moved it backward. Signed-off-by: Ladislav Michl Signed-off-by: Sascha Hauer --- drivers/mtd/core.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c index d3cbe502fa..f44c6cfc69 100644 --- a/drivers/mtd/core.c +++ b/drivers/mtd/core.c @@ -141,15 +141,28 @@ static struct mtd_erase_region_info *mtd_find_erase_region(struct mtd_info *mtd, return NULL; } +static loff_t __mtd_erase_round(loff_t x, uint32_t esize, int up) +{ + uint64_t dividend = x; + uint32_t mod = do_div(dividend, esize); + if (mod == 0) + return x; + if (up) + x += esize; + return x - mod; +} +#define mtd_erase_round_up(x, esize) __mtd_erase_round(x, esize, 1) +#define mtd_erase_round_down(x, esize) __mtd_erase_round(x, esize, 0) + static int mtd_erase_align(struct mtd_info *mtd, loff_t *count, loff_t *offset) { struct mtd_erase_region_info *e; loff_t ofs; if (mtd->numeraseregions == 0) { - ofs = *offset & ~(loff_t)(mtd->erasesize - 1); - *count += (*offset - ofs); - *count = ALIGN(*count, mtd->erasesize); + ofs = mtd_erase_round_down(*offset, mtd->erasesize); + *count += *offset - ofs; + *count = mtd_erase_round_up(*count, mtd->erasesize); *offset = ofs; return 0; } @@ -158,14 +171,14 @@ static int mtd_erase_align(struct mtd_info *mtd, loff_t *count, loff_t *offset) if (!e) return -EINVAL; - ofs = *offset & ~(e->erasesize - 1); - *count += (*offset - ofs); + ofs = mtd_erase_round_down(*offset, e->erasesize); + *count += *offset - ofs; e = mtd_find_erase_region(mtd, *offset + *count); if (!e) return -EINVAL; - *count = ALIGN(*count, e->erasesize); + *count = mtd_erase_round_up(*count, e->erasesize); *offset = ofs; return 0; -- cgit v1.2.3