Merge tag 'for-4.9' of github.com:linux-nand/linux
[cascardo/linux.git] / drivers / mtd / nand / nand_bbt.c
index 2fbb523..7695efe 100644 (file)
@@ -604,6 +604,100 @@ 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;
+}
+
+/**
+ * mark_bbt_block_bad - Mark one of the block reserved for BBT bad
+ * @this: the NAND 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 nand_chip *this,
+                              struct nand_bbt_descr *td,
+                              int chip, int block)
+{
+       struct mtd_info *mtd = nand_to_mtd(this);
+       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
@@ -621,7 +715,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
        struct nand_chip *this = mtd_to_nand(mtd);
        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;
@@ -652,46 +746,21 @@ 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;
+       while (chip < nrchips) {
+               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;
@@ -787,20 +856,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(this, 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(this, 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;