Merge git://git.infradead.org/mtd-2.6
[firefly-linux-kernel-4.4.55.git] / drivers / mtd / nand / nand_bbt.c
index 76f496d2bfedf0a5dc244a3e704bb07eb27cad83..69148ae3bf58aaf806be617894725eb72d1483f1 100644 (file)
@@ -67,6 +67,7 @@
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/vmalloc.h>
+#include <linux/export.h>
 
 static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
 {
@@ -107,10 +108,8 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc
        p += end;
 
        /* Compare the pattern */
-       for (i = 0; i < td->len; i++) {
-               if (p[i] != td->pattern[i])
-                       return -1;
-       }
+       if (memcmp(p, td->pattern, td->len))
+               return -1;
 
        if (td->options & NAND_BBT_SCANEMPTY) {
                p += td->len;
@@ -178,21 +177,21 @@ static u32 add_marker_len(struct nand_bbt_descr *td)
 static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
                struct nand_bbt_descr *td, int offs)
 {
-       int res, i, j, act = 0;
+       int res, ret = 0, i, j, act = 0;
        struct nand_chip *this = mtd->priv;
        size_t retlen, len, totlen;
        loff_t from;
        int bits = td->options & NAND_BBT_NRBITS_MSK;
-       uint8_t msk = (uint8_t) ((1 << bits) - 1);
+       uint8_t msk = (uint8_t)((1 << bits) - 1);
        u32 marker_len;
        int reserved_block_code = td->reserved_block_code;
 
        totlen = (num * bits) >> 3;
        marker_len = add_marker_len(td);
-       from = ((loff_t) page) << this->page_shift;
+       from = ((loff_t)page) << this->page_shift;
 
        while (totlen) {
-               len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
+               len = min(totlen, (size_t)(1 << this->bbt_erase_shift));
                if (marker_len) {
                        /*
                         * In case the BBT marker is not in the OOB area it
@@ -204,11 +203,18 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
                }
                res = mtd->read(mtd, from, len, &retlen, buf);
                if (res < 0) {
-                       if (retlen != len) {
-                               pr_info("nand_bbt: Error reading bad block table\n");
+                       if (mtd_is_eccerr(res)) {
+                               pr_info("nand_bbt: ECC error in BBT at "
+                                       "0x%012llx\n", from & ~mtd->writesize);
+                               return res;
+                       } else if (mtd_is_bitflip(res)) {
+                               pr_info("nand_bbt: corrected error in BBT at "
+                                       "0x%012llx\n", from & ~mtd->writesize);
+                               ret = res;
+                       } else {
+                               pr_info("nand_bbt: error reading BBT\n");
                                return res;
                        }
-                       pr_warn("nand_bbt: ECC error while reading bad block table\n");
                }
 
                /* Analyse data */
@@ -219,8 +225,8 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
                                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);
+                                       pr_info("nand_read_bbt: reserved block at 0x%012llx\n",
+                                                (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
                                        this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
                                        mtd->ecc_stats.bbtblocks++;
                                        continue;
@@ -229,8 +235,8 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
                                 * Leave it for now, if it's matured we can
                                 * move this message to pr_debug.
                                 */
-                               pr_info("nand_read_bbt: Bad block at 0x%012llx\n",
-                                      (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+                               pr_info("nand_read_bbt: bad block at 0x%012llx\n",
+                                        (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
                                /* Factory marked bad or worn out? */
                                if (tmp == 0)
                                        this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
@@ -242,7 +248,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
                totlen -= len;
                from += len;
        }
-       return 0;
+       return ret;
 }
 
 /**
@@ -250,7 +256,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
  * @mtd: MTD device structure
  * @buf: temporary buffer
  * @td: descriptor for the bad block table
- * @chip: read the table for a specific chip, -1 read all chips; aplies only if
+ * @chip: read the table for a specific chip, -1 read all chips; applies only if
  *        NAND_BBT_PERCHIP option is set
  *
  * Read the bad block table for all chips starting at a given page. We assume
@@ -302,32 +308,19 @@ static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
        struct mtd_oob_ops ops;
        int res;
 
-       ops.mode = MTD_OOB_RAW;
+       ops.mode = MTD_OPS_RAW;
        ops.ooboffs = 0;
        ops.ooblen = mtd->oobsize;
 
-
        while (len > 0) {
-               if (len <= mtd->writesize) {
-                       ops.oobbuf = buf + len;
-                       ops.datbuf = buf;
-                       ops.len = len;
-                       res = mtd->read_oob(mtd, offs, &ops);
-
-                       /* Ignore ECC errors when checking for BBM */
-                       if (res != -EUCLEAN && res != -EBADMSG)
-                               return res;
-                       return 0;
-               } else {
-                       ops.oobbuf = buf + mtd->writesize;
-                       ops.datbuf = buf;
-                       ops.len = mtd->writesize;
-                       res = mtd->read_oob(mtd, offs, &ops);
+               ops.datbuf = buf;
+               ops.len = min(len, (size_t)mtd->writesize);
+               ops.oobbuf = buf + ops.len;
 
-                       /* Ignore ECC errors when checking for BBM */
-                       if (res && res != -EUCLEAN && res != -EBADMSG)
-                               return res;
-               }
+               res = mtd->read_oob(mtd, offs, &ops);
+
+               if (res)
+                       return res;
 
                buf += mtd->oobsize + mtd->writesize;
                len -= mtd->writesize;
@@ -350,7 +343,7 @@ static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
 {
        struct mtd_oob_ops ops;
 
-       ops.mode = MTD_OOB_PLACE;
+       ops.mode = MTD_OPS_PLACE_OOB;
        ops.ooboffs = 0;
        ops.ooblen = mtd->oobsize;
        ops.datbuf = buf;
@@ -390,7 +383,7 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
                              mtd->writesize, td);
                td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
                pr_info("Bad block table at page %d, version 0x%02X\n",
-                      td->pages[0], td->version[0]);
+                        td->pages[0], td->version[0]);
        }
 
        /* Read the mirror version, if available */
@@ -399,7 +392,7 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
                              mtd->writesize, td);
                md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
                pr_info("Bad block table at page %d, version 0x%02X\n",
-                      md->pages[0], md->version[0]);
+                        md->pages[0], md->version[0]);
        }
        return 1;
 }
@@ -412,7 +405,8 @@ static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
        int ret, j;
 
        ret = scan_read_raw_oob(mtd, buf, offs, readlen);
-       if (ret)
+       /* Ignore ECC errors when checking for BBM */
+       if (ret && !mtd_is_bitflip_or_eccerr(ret))
                return ret;
 
        for (j = 0; j < len; j++, buf += scanlen) {
@@ -433,7 +427,7 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
        ops.oobbuf = buf;
        ops.ooboffs = 0;
        ops.datbuf = NULL;
-       ops.mode = MTD_OOB_PLACE;
+       ops.mode = MTD_OPS_PLACE_OOB;
 
        for (j = 0; j < len; j++) {
                /*
@@ -442,7 +436,7 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
                 */
                ret = mtd->read_oob(mtd, offs, &ops);
                /* Ignore ECC errors when checking for BBM */
-               if (ret && ret != -EUCLEAN && ret != -EBADMSG)
+               if (ret && !mtd_is_bitflip_or_eccerr(ret))
                        return ret;
 
                if (check_short_pattern(buf, bd))
@@ -532,7 +526,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
                if (ret) {
                        this->bbt[i >> 3] |= 0x03 << (i & 0x6);
                        pr_warn("Bad eraseblock %d at 0x%012llx\n",
-                              i >> 1, (unsigned long long)from);
+                               i >> 1, (unsigned long long)from);
                        mtd->ecc_stats.badblocks++;
                }
 
@@ -616,8 +610,8 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
                if (td->pages[i] == -1)
                        pr_warn("Bad block table not found for chip %d\n", i);
                else
-                       pr_info("Bad block table found at page %d, version 0x%02X\n", td->pages[i],
-                              td->version[i]);
+                       pr_info("Bad block table found at page %d, version "
+                                "0x%02X\n", td->pages[i], td->version[i]);
        }
        return 0;
 }
@@ -672,7 +666,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
        ops.ooblen = mtd->oobsize;
        ops.ooboffs = 0;
        ops.datbuf = NULL;
-       ops.mode = MTD_OOB_PLACE;
+       ops.mode = MTD_OPS_PLACE_OOB;
 
        if (!rcode)
                rcode = 0xff;
@@ -755,24 +749,22 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
 
                bbtoffs = chip * (numblocks >> 2);
 
-               to = ((loff_t) page) << this->page_shift;
+               to = ((loff_t)page) << this->page_shift;
 
                /* Must we save the block contents? */
                if (td->options & NAND_BBT_SAVECONTENT) {
                        /* Make it block aligned */
-                       to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));
+                       to &= ~((loff_t)((1 << this->bbt_erase_shift) - 1));
                        len = 1 << this->bbt_erase_shift;
                        res = mtd->read(mtd, to, len, &retlen, buf);
                        if (res < 0) {
                                if (retlen != len) {
-                                       pr_info("nand_bbt: Error "
-                                              "reading block for writing "
-                                              "the bad block table\n");
+                                       pr_info("nand_bbt: error reading block "
+                                               "for writing the bad block table\n");
                                        return res;
                                }
-                               pr_warn("nand_bbt: ECC error "
-                                      "while reading block for writing "
-                                      "bad block table\n");
+                               pr_warn("nand_bbt: ECC error while reading "
+                                       "block for writing bad block table\n");
                        }
                        /* Read oob data */
                        ops.ooblen = (len >> this->page_shift) * mtd->oobsize;
@@ -785,7 +777,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                        pageoffs = page - (int)(to >> this->page_shift);
                        offs = pageoffs << this->page_shift;
                        /* Preset the bbt area with 0xff */
-                       memset(&buf[offs], 0xff, (size_t) (numblocks >> sft));
+                       memset(&buf[offs], 0xff, (size_t)(numblocks >> sft));
                        ooboffs = len + (pageoffs * mtd->oobsize);
 
                } else if (td->options & NAND_BBT_NO_OOB) {
@@ -795,7 +787,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                        if (td->options & NAND_BBT_VERSION)
                                offs++;
                        /* Calc length */
-                       len = (size_t) (numblocks >> sft);
+                       len = (size_t)(numblocks >> sft);
                        len += offs;
                        /* Make it page aligned! */
                        len = ALIGN(len, mtd->writesize);
@@ -805,7 +797,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                        memcpy(buf, td->pattern, td->len);
                } else {
                        /* Calc length */
-                       len = (size_t) (numblocks >> sft);
+                       len = (size_t)(numblocks >> sft);
                        /* Make it page aligned! */
                        len = ALIGN(len, mtd->writesize);
                        /* Preset the buffer with 0xff */
@@ -847,8 +839,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                if (res < 0)
                        goto outerr;
 
-               pr_info("Bad block table written to 0x%012llx, version "
-                      "0x%02X\n", (unsigned long long)to, td->version[chip]);
+               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;
@@ -856,8 +848,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
        return 0;
 
  outerr:
-       printk(KERN_WARNING
-              "nand_bbt: Error while writing bad block table %d\n", res);
+       pr_warn("nand_bbt: error while writing bad block table %d\n", res);
        return res;
 }
 
@@ -890,7 +881,7 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b
  */
 static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
 {
-       int i, chips, writeops, chipsel, res;
+       int i, chips, writeops, create, chipsel, res, res2;
        struct nand_chip *this = mtd->priv;
        struct nand_bbt_descr *td = this->bbt_td;
        struct nand_bbt_descr *md = this->bbt_md;
@@ -904,77 +895,89 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
 
        for (i = 0; i < chips; i++) {
                writeops = 0;
+               create = 0;
                rd = NULL;
                rd2 = NULL;
+               res = res2 = 0;
                /* Per chip or per device? */
                chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
                /* Mirrored table available? */
                if (md) {
                        if (td->pages[i] == -1 && md->pages[i] == -1) {
+                               create = 1;
                                writeops = 0x03;
-                               goto create;
-                       }
-
-                       if (td->pages[i] == -1) {
+                       } else if (td->pages[i] == -1) {
                                rd = md;
-                               td->version[i] = md->version[i];
-                               writeops = 1;
-                               goto writecheck;
-                       }
-
-                       if (md->pages[i] == -1) {
+                               writeops = 0x01;
+                       } else if (md->pages[i] == -1) {
                                rd = td;
-                               md->version[i] = td->version[i];
-                               writeops = 2;
-                               goto writecheck;
-                       }
-
-                       if (td->version[i] == md->version[i]) {
+                               writeops = 0x02;
+                       } else if (td->version[i] == md->version[i]) {
                                rd = td;
                                if (!(td->options & NAND_BBT_VERSION))
                                        rd2 = md;
-                               goto writecheck;
-                       }
-
-                       if (((int8_t) (td->version[i] - md->version[i])) > 0) {
+                       } else if (((int8_t)(td->version[i] - md->version[i])) > 0) {
                                rd = td;
-                               md->version[i] = td->version[i];
-                               writeops = 2;
+                               writeops = 0x02;
                        } else {
                                rd = md;
-                               td->version[i] = md->version[i];
-                               writeops = 1;
+                               writeops = 0x01;
                        }
-
-                       goto writecheck;
-
                } else {
                        if (td->pages[i] == -1) {
+                               create = 1;
                                writeops = 0x01;
-                               goto create;
+                       } else {
+                               rd = td;
                        }
-                       rd = td;
-                       goto writecheck;
                }
-       create:
-               /* Create the bad block table by scanning the device? */
-               if (!(td->options & NAND_BBT_CREATE))
-                       continue;
 
-               /* Create the table in memory by scanning the chip(s) */
-               if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY))
-                       create_bbt(mtd, buf, bd, chipsel);
+               if (create) {
+                       /* Create the bad block table by scanning the device? */
+                       if (!(td->options & NAND_BBT_CREATE))
+                               continue;
+
+                       /* Create the table in memory by scanning the chip(s) */
+                       if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY))
+                               create_bbt(mtd, buf, bd, chipsel);
+
+                       td->version[i] = 1;
+                       if (md)
+                               md->version[i] = 1;
+               }
 
-               td->version[i] = 1;
-               if (md)
-                       md->version[i] = 1;
-       writecheck:
                /* Read back first? */
-               if (rd)
-                       read_abs_bbt(mtd, buf, rd, chipsel);
+               if (rd) {
+                       res = read_abs_bbt(mtd, buf, rd, chipsel);
+                       if (mtd_is_eccerr(res)) {
+                               /* Mark table as invalid */
+                               rd->pages[i] = -1;
+                               rd->version[i] = 0;
+                               i--;
+                               continue;
+                       }
+               }
                /* If they weren't versioned, read both */
-               if (rd2)
-                       read_abs_bbt(mtd, buf, rd2, chipsel);
+               if (rd2) {
+                       res2 = read_abs_bbt(mtd, buf, rd2, chipsel);
+                       if (mtd_is_eccerr(res2)) {
+                               /* Mark table as invalid */
+                               rd2->pages[i] = -1;
+                               rd2->version[i] = 0;
+                               i--;
+                               continue;
+                       }
+               }
+
+               /* Scrub the flash table(s)? */
+               if (mtd_is_bitflip(res) || mtd_is_bitflip(res2))
+                       writeops = 0x03;
+
+               /* Update version numbers before writing */
+               if (md) {
+                       td->version[i] = max(td->version[i], md->version[i]);
+                       md->version[i] = td->version[i];
+               }
 
                /* Write the bad block table to the device? */
                if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
@@ -1137,7 +1140,7 @@ 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");
+                       pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
                        kfree(this->bbt);
                        this->bbt = NULL;
                }
@@ -1186,7 +1189,7 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
 int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
 {
        struct nand_chip *this = mtd->priv;
-       int len, res = 0, writeops = 0;
+       int len, res = 0;
        int chip, chipsel;
        uint8_t *buf;
        struct nand_bbt_descr *td = this->bbt_td;
@@ -1202,8 +1205,6 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
        if (!buf)
                return -ENOMEM;
 
-       writeops = md != NULL ? 0x03 : 0x01;
-
        /* Do we have a bbt per chip? */
        if (td->options & NAND_BBT_PERCHIP) {
                chip = (int)(offs >> this->chip_shift);
@@ -1218,13 +1219,13 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
                md->version[chip]++;
 
        /* Write the bad block table to the device? */
-       if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+       if (td->options & NAND_BBT_WRITE) {
                res = write_bbt(mtd, buf, td, md, chipsel);
                if (res < 0)
                        goto out;
        }
        /* Write the mirror bad block table to the device? */
-       if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
+       if (md && (md->options & NAND_BBT_WRITE)) {
                res = write_bbt(mtd, buf, md, td, chipsel);
        }
 
@@ -1292,26 +1293,27 @@ static struct nand_bbt_descr bbt_mirror_no_bbt_descr = {
        .pattern = mirror_pattern
 };
 
+#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB)
 /**
- * nand_create_default_bbt_descr - [INTERN] Creates a BBT descriptor structure
+ * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure
  * @this: 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
+ * 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_default_bbt_descr(struct nand_chip *this)
+static int nand_create_badblock_pattern(struct nand_chip *this)
 {
        struct nand_bbt_descr *bd;
        if (this->badblock_pattern) {
-               pr_warn("BBT descr already allocated; not replacing.\n");
+               pr_warn("Bad block pattern already allocated; not replacing\n");
                return -EINVAL;
        }
        bd = kzalloc(sizeof(*bd), GFP_KERNEL);
        if (!bd)
                return -ENOMEM;
-       bd->options = this->bbt_options;
+       bd->options = this->bbt_options & BADBLOCK_SCAN_MASK;
        bd->offs = this->badblockpos;
        bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1;
        bd->pattern = scan_ff_pattern;
@@ -1365,7 +1367,7 @@ int nand_default_bbt(struct mtd_info *mtd)
        }
 
        if (!this->badblock_pattern)
-               nand_create_default_bbt_descr(this);
+               nand_create_badblock_pattern(this);
 
        return nand_scan_bbt(mtd, this->badblock_pattern);
 }
@@ -1386,8 +1388,9 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
        block = (int)(offs >> (this->bbt_erase_shift - 1));
        res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
 
-       DEBUG(MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
-             (unsigned int)offs, block >> 1, res);
+       pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: "
+                       "(block %d) 0x%02x\n",
+                       (unsigned int)offs, block >> 1, res);
 
        switch ((int)res) {
        case 0x00: