mtd: rawnand: Support bad block markers in first, second or last page
authorFrieder Schrempf <frieder.schrempf@kontron.de>
Wed, 17 Apr 2019 12:36:37 +0000 (12:36 +0000)
committerMiquel Raynal <miquel.raynal@bootlin.com>
Thu, 18 Apr 2019 06:54:08 +0000 (08:54 +0200)
Currently supported bad block marker positions within the block are:
* in first page only
* in last page only
* in first or second page

Some ESMT NANDs are known to have been shipped by the manufacturer
with bad block markers in the first or last page, instead of the
first or second page.

Also the datasheets for Cypress/Spansion/AMD NANDs claim that the
first, second *and* last page needs to be checked.

Therefore we make it possible to set NAND_BBM_FIRSTPAGE,
NAND_BBM_SECONDPAGE and NAND_BBM_LASTPAGE independently in any
combination.

To simplify the code, the logic to evaluate the flags is moved to a
a new function nand_bbm_get_next_page().

Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de>
Reviewed-by: Boris Brezillon <bbrezillon@kernel.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
drivers/mtd/nand/raw/internals.h
drivers/mtd/nand/raw/nand_base.c
drivers/mtd/nand/raw/nand_bbt.c

index a204f9d7e1232f862362386e94e76b1de8927b70..cba6fe7dd8c4d163243e1ba41e5b6ffead7e3ce8 100644 (file)
@@ -76,6 +76,7 @@ extern const struct nand_manufacturer_ops toshiba_nand_manuf_ops;
 
 /* Core functions */
 const struct nand_manufacturer *nand_get_manufacturer(u8 id);
+int nand_bbm_get_next_page(struct nand_chip *chip, int page);
 int nand_markbad_bbm(struct nand_chip *chip, loff_t ofs);
 int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
                    int allowbbt);
index 6e2d728d2fd9ed2e06aff26a4839db95064a80ca..2cf71060d6f8459c7377ee2c29303efd5c23ec45 100644 (file)
@@ -282,6 +282,31 @@ static void nand_release_device(struct nand_chip *chip)
        mutex_unlock(&chip->lock);
 }
 
+/**
+ * nand_bbm_get_next_page - Get the next page for bad block markers
+ * @chip: NAND chip object
+ * @page: First page to start checking for bad block marker usage
+ *
+ * Returns an integer that corresponds to the page offset within a block, for
+ * a page that is used to store bad block markers. If no more pages are
+ * available, -EINVAL is returned.
+ */
+int nand_bbm_get_next_page(struct nand_chip *chip, int page)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       int last_page = ((mtd->erasesize - mtd->writesize) >>
+                        chip->page_shift) & chip->pagemask;
+
+       if (page == 0 && chip->options & NAND_BBM_FIRSTPAGE)
+               return 0;
+       else if (page <= 1 && chip->options & NAND_BBM_SECONDPAGE)
+               return 1;
+       else if (page <= last_page && chip->options & NAND_BBM_LASTPAGE)
+               return last_page;
+
+       return -EINVAL;
+}
+
 /**
  * nand_block_bad - [DEFAULT] Read bad block marker from the chip
  * @chip: NAND chip object
@@ -291,19 +316,15 @@ static void nand_release_device(struct nand_chip *chip)
  */
 static int nand_block_bad(struct nand_chip *chip, loff_t ofs)
 {
-       struct mtd_info *mtd = nand_to_mtd(chip);
-       int page, page_end, res;
+       int first_page, page_offset;
+       int res;
        u8 bad;
 
-       if (chip->options & NAND_BBM_LASTPAGE)
-               ofs += mtd->erasesize - mtd->writesize;
+       first_page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+       page_offset = nand_bbm_get_next_page(chip, 0);
 
-       page = (int)(ofs >> chip->page_shift) & chip->pagemask;
-       page_end = page + (((chip->options & NAND_BBM_FIRSTPAGE) &&
-                           (chip->options & NAND_BBM_SECONDPAGE)) ? 2 : 1);
-
-       for (; page < page_end; page++) {
-               res = chip->ecc.read_oob(chip, page);
+       while (page_offset >= 0) {
+               res = chip->ecc.read_oob(chip, first_page + page_offset);
                if (res < 0)
                        return res;
 
@@ -315,6 +336,8 @@ static int nand_block_bad(struct nand_chip *chip, loff_t ofs)
                        res = hweight8(bad) < chip->badblockbits;
                if (res)
                        return res;
+
+               page_offset = nand_bbm_get_next_page(chip, page_offset + 1);
        }
 
        return 0;
@@ -494,7 +517,7 @@ static int nand_default_block_markbad(struct nand_chip *chip, loff_t ofs)
        struct mtd_info *mtd = nand_to_mtd(chip);
        struct mtd_oob_ops ops;
        uint8_t buf[2] = { 0, 0 };
-       int ret = 0, res, i = 0;
+       int ret = 0, res, page_offset;
 
        memset(&ops, 0, sizeof(ops));
        ops.oobbuf = buf;
@@ -507,18 +530,18 @@ static int nand_default_block_markbad(struct nand_chip *chip, loff_t ofs)
        }
        ops.mode = MTD_OPS_PLACE_OOB;
 
-       /* Write to first/last page(s) if necessary */
-       if (chip->options & NAND_BBM_LASTPAGE)
-               ofs += mtd->erasesize - mtd->writesize;
-       do {
-               res = nand_do_write_oob(chip, ofs, &ops);
+       page_offset = nand_bbm_get_next_page(chip, 0);
+
+       while (page_offset >= 0) {
+               res = nand_do_write_oob(chip,
+                                       ofs + (page_offset * mtd->writesize),
+                                       &ops);
+
                if (!ret)
                        ret = res;
 
-               i++;
-               ofs += mtd->writesize;
-       } while ((chip->options & NAND_BBM_FIRSTPAGE) &&
-                (chip->options & NAND_BBM_SECONDPAGE) && i < 2);
+               page_offset = nand_bbm_get_next_page(chip, page_offset + 1);
+       }
 
        return ret;
 }
index 4cb664847ef54ada61fd37e6f6c8264783198d55..fd3c10216eda0bbf9ecf0f434f85bed7204df232 100644 (file)
@@ -416,11 +416,12 @@ static void read_abs_bbts(struct nand_chip *this, uint8_t *buf,
 
 /* Scan a given block partially */
 static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd,
-                          loff_t offs, uint8_t *buf, int numpages)
+                          loff_t offs, uint8_t *buf)
 {
        struct mtd_info *mtd = nand_to_mtd(this);
+
        struct mtd_oob_ops ops;
-       int j, ret;
+       int ret, page_offset;
 
        ops.ooblen = mtd->oobsize;
        ops.oobbuf = buf;
@@ -428,12 +429,15 @@ static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd,
        ops.datbuf = NULL;
        ops.mode = MTD_OPS_PLACE_OOB;
 
-       for (j = 0; j < numpages; j++) {
+       page_offset = nand_bbm_get_next_page(this, 0);
+
+       while (page_offset >= 0) {
                /*
                 * Read the full oob until read_oob is fixed to handle single
                 * byte reads for 16 bit buswidth.
                 */
-               ret = mtd_read_oob(mtd, offs, &ops);
+               ret = mtd_read_oob(mtd, offs + (page_offset * mtd->writesize),
+                                  &ops);
                /* Ignore ECC errors when checking for BBM */
                if (ret && !mtd_is_bitflip_or_eccerr(ret))
                        return ret;
@@ -441,8 +445,9 @@ static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd,
                if (check_short_pattern(buf, bd))
                        return 1;
 
-               offs += mtd->writesize;
+               page_offset = nand_bbm_get_next_page(this, page_offset + 1);
        }
+
        return 0;
 }
 
@@ -462,18 +467,11 @@ static int create_bbt(struct nand_chip *this, uint8_t *buf,
 {
        u64 targetsize = nanddev_target_size(&this->base);
        struct mtd_info *mtd = nand_to_mtd(this);
-       int i, numblocks, numpages;
-       int startblock;
+       int i, numblocks, startblock;
        loff_t from;
 
        pr_info("Scanning device for bad blocks\n");
 
-       if ((this->options & NAND_BBM_FIRSTPAGE) &&
-           (this->options & NAND_BBM_SECONDPAGE))
-               numpages = 2;
-       else
-               numpages = 1;
-
        if (chip == -1) {
                numblocks = mtd->size >> this->bbt_erase_shift;
                startblock = 0;
@@ -490,15 +488,12 @@ static int create_bbt(struct nand_chip *this, uint8_t *buf,
                from = (loff_t)startblock << this->bbt_erase_shift;
        }
 
-       if (this->options & NAND_BBM_LASTPAGE)
-               from += mtd->erasesize - (mtd->writesize * numpages);
-
        for (i = startblock; i < numblocks; i++) {
                int ret;
 
                BUG_ON(bd->options & NAND_BBT_NO_OOB);
 
-               ret = scan_block_fast(this, bd, from, buf, numpages);
+               ret = scan_block_fast(this, bd, from, buf);
                if (ret < 0)
                        return ret;