]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - drivers/mtd/nand/nand_bbt.c
Merge branch 'master' of git://git.denx.de/u-boot-arc
[karo-tx-uboot.git] / drivers / mtd / nand / nand_bbt.c
index 2fe68abe1df8c05255dc5589fc35cc6e6998bbd9..c8f28c792b2a3e21c919be0a725aca19fe794a28 100644 (file)
@@ -4,7 +4,7 @@
  *  Overview:
  *   Bad block table support for the NAND driver
  *
- *  Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
+ *  Copyright © 2004 Thomas Gleixner (tglx@linutronix.de)
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * Description:
  *
  * When nand_scan_bbt is called, then it tries to find the bad block table
- * depending on the options in the bbt descriptor(s). If a bbt is found
- * then the contents are read and the memory based bbt is created. If a
- * mirrored bbt is selected then the mirror is searched too and the
- * versions are compared. If the mirror has a greater version number
- * than the mirror bbt is used to build the memory based bbt.
+ * depending on the options in the BBT descriptor(s). If no flash based BBT
+ * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory
+ * marked good / bad blocks. This information is used to create a memory BBT.
+ * Once a new bad block is discovered then the "factory" information is updated
+ * on the device.
+ * If a flash based BBT is specified then the function first tries to find the
+ * BBT on flash. If a BBT is found then the contents are read and the memory
+ * based BBT is created. If a mirrored BBT is selected then the mirror is
+ * searched too and the versions are compared. If the mirror has a greater
+ * version number, then the mirror BBT is used to build the memory based BBT.
  * If the tables are not versioned, then we "or" the bad block information.
- * If one of the bbt's is out of date or does not exist it is (re)created.
- * If no bbt exists at all then the device is scanned for factory marked
+ * If one of the BBTs is out of date or does not exist it is (re)created.
+ * If no BBT exists at all then the device is scanned for factory marked
  * good / bad blocks and the bad block tables are created.
  *
- * For manufacturer created bbts like the one found on M-SYS DOC devices
- * the bbt is searched and read but never created
+ * For manufacturer created BBTs like the one found on M-SYS DOC devices
+ * the BBT is searched and read but never created
  *
- * The autogenerated bad block table is located in the last good blocks
+ * The auto generated bad block table is located in the last good blocks
  * of the device. The table is mirrored, so it can be updated eventually.
- * The table is marked in the oob area with an ident pattern and a version
- * number which indicates which of both tables is more up to date.
+ * The table is marked in the OOB area with an ident pattern and a version
+ * number which indicates which of both tables is more up to date. If the NAND
+ * controller needs the complete OOB area for the ECC information then the
+ * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of
+ * course): it moves the ident pattern and the version byte into the data area
+ * and the OOB area will remain untouched.
  *
  * The table uses 2 bits per block
- * 11b:        block is good
- * 00b:        block is factory marked bad
+ * 11b:                block is good
+ * 00b:                block is factory marked bad
  * 01b, 10b:   block is marked bad due to wear
  *
  * The memory bad block table uses the following scheme:
  *
  */
 
-#include <common.h>
-#include <malloc.h>
-#include <linux/mtd/compat.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
-
-#include <asm/errno.h>
-
-/* XXX U-BOOT XXX */
-#if 0
+#define __UBOOT__
+#ifndef __UBOOT__
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/mtd/mtd.h>
+#include <linux/mtd/bbm.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/nand_ecc.h>
-#include <linux/mtd/compatmac.h>
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/vmalloc.h>
+#include <linux/export.h>
+#include <linux/string.h>
+#else
+#include <common.h>
+#include <malloc.h>
+#include <linux/compat.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/bbm.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/nand_ecc.h>
+ #include <linux/bitops.h>
+ #include <linux/string.h>
 #endif
 
+#define BBT_BLOCK_GOOD         0x00
+#define BBT_BLOCK_WORN         0x01
+#define BBT_BLOCK_RESERVED     0x02
+#define BBT_BLOCK_FACTORY_BAD  0x03
+
+#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];
+       entry >>= (block & BBT_ENTRY_MASK) * 2;
+       return entry & BBT_ENTRY_MASK;
+}
+
+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;
+}
+
+static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
+{
+       if (memcmp(buf, td->pattern, td->len))
+               return -1;
+       return 0;
+}
+
 /**
  * check_pattern - [GENERIC] check if a pattern is in the buffer
- * @buf:       the buffer to search
- * @len:       the length of buffer to search
- * @paglen:    the pagelength
- * @td:                search pattern descriptor
+ * @buf: the buffer to search
+ * @len: the length of buffer to search
+ * @paglen: the pagelength
+ * @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
- *
-*/
+ * Check for a pattern at the given place. Used to search bad block tables and
+ * good / bad block identifiers.
+ */
 static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
 {
-       int i, end = 0;
-       uint8_t *p = buf;
-
-       end = paglen + td->offs;
-       if (td->options & NAND_BBT_SCANEMPTY) {
-               for (i = 0; i < end; i++) {
-                       if (p[i] != 0xff)
-                               return -1;
-               }
-       }
-       p += end;
+       if (td->options & NAND_BBT_NO_OOB)
+               return check_pattern_no_oob(buf, td);
 
        /* Compare the pattern */
-       for (i = 0; i < td->len; i++) {
-               if (p[i] != td->pattern[i])
-                       return -1;
-       }
+       if (memcmp(buf + paglen + td->offs, td->pattern, td->len))
+               return -1;
 
-       if (td->options & NAND_BBT_SCANEMPTY) {
-               p += td->len;
-               end += td->len;
-               for (i = end; i < len; i++) {
-                       if (*p++ != 0xff)
-                               return -1;
-               }
-       }
        return 0;
 }
 
 /**
  * check_short_pattern - [GENERIC] check if a pattern is in the buffer
- * @buf:       the buffer to search
- * @td:                search pattern descriptor
- *
- * Check for a pattern at the given place. Used to search bad block
- * tables and good / bad block identifiers. Same as check_pattern, but
- * no optional empty check
+ * @buf: the buffer to search
+ * @td:        search pattern descriptor
  *
-*/
+ * Check for a pattern at the given place. Used to search bad block tables and
+ * good / bad block identifiers. Same as check_pattern, but no optional empty
+ * check.
+ */
 static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
 {
-       int i;
-       uint8_t *p = buf;
-
        /* Compare the pattern */
-       for (i = 0; i < td->len; i++) {
-               if (p[td->offs + i] != td->pattern[i])
-                       return -1;
-       }
+       if (memcmp(buf + td->offs, td->pattern, td->len))
+               return -1;
        return 0;
 }
 
+/**
+ * add_marker_len - compute the length of the marker in data area
+ * @td: BBT descriptor used for computation
+ *
+ * The length will be 0 if the marker is located in OOB area.
+ */
+static u32 add_marker_len(struct nand_bbt_descr *td)
+{
+       u32 len;
+
+       if (!(td->options & NAND_BBT_NO_OOB))
+               return 0;
+
+       len = td->len;
+       if (td->options & NAND_BBT_VERSION)
+               len++;
+       return len;
+}
+
 /**
  * read_bbt - [GENERIC] Read the bad block table starting from page
- * @mtd:       MTD device structure
- * @buf:       temporary buffer
- * @page:      the starting page
- * @num:       the number of bbt descriptors to read
- * @bits:      number of bits per block
- * @offs:      offset in the memory table
- * @reserved_block_code:       Pattern to identify reserved blocks
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @page: the starting page
+ * @num: the number of bbt descriptors to read
+ * @td: the bbt describtion table
+ * @offs: block number offset in the table
  *
  * Read the bad block table starting from page.
- *
  */
 static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
-                   int bits, int offs, int reserved_block_code)
+               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;
-       uint8_t msk = (uint8_t) ((1 << bits) - 1);
+       int bits = td->options & NAND_BBT_NRBITS_MSK;
+       uint8_t msk = (uint8_t)((1 << bits) - 1);
+       u32 marker_len;
+       int reserved_block_code = td->reserved_block_code;
 
        totlen = (num * bits) >> 3;
-       from = ((loff_t) page) << this->page_shift;
+       marker_len = add_marker_len(td);
+       from = ((loff_t)page) << this->page_shift;
 
        while (totlen) {
-               len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
-               res = mtd->read(mtd, from, len, &retlen, buf);
+               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
+                        * will be just in the first page.
+                        */
+                       len -= marker_len;
+                       from += marker_len;
+                       marker_len = 0;
+               }
+               res = mtd_read(mtd, from, len, &retlen, buf);
                if (res < 0) {
-                       if (retlen != len) {
-                               printk(KERN_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;
                        }
-                       printk(KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
                }
 
                /* 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)) {
-                                       printk(KERN_DEBUG "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);
+                                       pr_info("nand_read_bbt: reserved block at 0x%012llx\n",
+                                                (loff_t)(offs + act) <<
+                                                this->bbt_erase_shift);
+                                       bbt_mark_entry(this, offs + act,
+                                                       BBT_BLOCK_RESERVED);
                                        mtd->ecc_stats.bbtblocks++;
                                        continue;
                                }
-                               /* Leave it for now, if its matured we can move this
-                                * message to MTD_DEBUG_LEVEL0 */
-                               printk(KERN_DEBUG "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 ? */
+                               /*
+                                * 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 + act) <<
+                                        this->bbt_erase_shift);
+                               /* Factory marked bad or worn out? */
                                if (tmp == 0)
-                                       this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
+                                       bbt_mark_entry(this, offs + act,
+                                                       BBT_BLOCK_FACTORY_BAD);
                                else
-                                       this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
+                                       bbt_mark_entry(this, offs + act,
+                                                       BBT_BLOCK_WORN);
                                mtd->ecc_stats.badblocks++;
                        }
                }
                totlen -= len;
                from += len;
        }
-       return 0;
+       return ret;
 }
 
 /**
  * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
- * @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.
- *             Applies only if NAND_BBT_PERCHIP option is set
+ * @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; applies only if
+ *        NAND_BBT_PERCHIP option is set
  *
- * Read the bad block table for all chips starting at a given page
- * We assume that the bbt bits are in consecutive order.
-*/
+ * Read the bad block table for all chips starting at a given page. We assume
+ * that the bbt bits are in consecutive order.
+ */
 static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
 {
        struct nand_chip *this = mtd->priv;
        int res = 0, i;
-       int bits;
 
-       bits = td->options & NAND_BBT_NRBITS_MSK;
        if (td->options & NAND_BBT_PERCHIP) {
                int offs = 0;
                for (i = 0; i < this->numchips; i++) {
                        if (chip == -1 || chip == i)
-                               res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code);
+                               res = read_bbt(mtd, buf, td->pages[i],
+                                       this->chipsize >> this->bbt_erase_shift,
+                                       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], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code);
+               res = read_bbt(mtd, buf, td->pages[0],
+                               mtd->size >> this->bbt_erase_shift, td, 0);
                if (res)
                        return res;
        }
        return 0;
 }
 
-/*
- * Scan read raw data from flash
+/* BBT marker is in the first page, no OOB */
+static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+                        struct nand_bbt_descr *td)
+{
+       size_t retlen;
+       size_t len;
+
+       len = td->len;
+       if (td->options & NAND_BBT_VERSION)
+               len++;
+
+       return mtd_read(mtd, offs, len, &retlen, buf);
+}
+
+/**
+ * scan_read_oob - [GENERIC] Scan data+OOB region to buffer
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @offs: offset at which to scan
+ * @len: length of data region to read
+ *
+ * Scan read data from data+OOB. May traverse multiple pages, interleaving
+ * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest"
+ * ECC condition (error or bitflip). May quit on the first (non-ECC) error.
  */
-static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
                         size_t len)
 {
        struct mtd_oob_ops ops;
+       int res, ret = 0;
 
-       ops.mode = MTD_OOB_RAW;
+       ops.mode = MTD_OPS_PLACE_OOB;
        ops.ooboffs = 0;
        ops.ooblen = mtd->oobsize;
-       ops.oobbuf = buf;
-       ops.datbuf = buf;
-       ops.len = len;
 
-       return mtd->read_oob(mtd, offs, &ops);
+       while (len > 0) {
+               ops.datbuf = buf;
+               ops.len = min(len, (size_t)mtd->writesize);
+               ops.oobbuf = buf + ops.len;
+
+               res = mtd_read_oob(mtd, offs, &ops);
+               if (res) {
+                       if (!mtd_is_bitflip_or_eccerr(res))
+                               return res;
+                       else if (mtd_is_eccerr(res) || !ret)
+                               ret = res;
+               }
+
+               buf += mtd->oobsize + mtd->writesize;
+               len -= mtd->writesize;
+               offs += mtd->writesize;
+       }
+       return ret;
 }
 
-/*
- * Scan write data with oob to flash
- */
+static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+                        size_t len, struct nand_bbt_descr *td)
+{
+       if (td->options & NAND_BBT_NO_OOB)
+               return scan_read_data(mtd, buf, offs, td);
+       else
+               return scan_read_oob(mtd, buf, offs, len);
+}
+
+/* Scan write data with oob to flash */
 static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
                          uint8_t *buf, uint8_t *oob)
 {
        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;
        ops.oobbuf = oob;
        ops.len = len;
 
-       return mtd->write_oob(mtd, offs, &ops);
+       return mtd_write_oob(mtd, offs, &ops);
+}
+
+static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
+{
+       u32 ver_offs = td->veroffs;
+
+       if (!(td->options & NAND_BBT_NO_OOB))
+               ver_offs += mtd->writesize;
+       return ver_offs;
 }
 
 /**
  * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
- * @mtd:       MTD device structure
- * @buf:       temporary buffer
- * @td:                descriptor for the bad block table
- * @md:                descriptor for the bad block table mirror
- *
- * Read the bad block table(s) for all chips starting at a given page
- * We assume that the bbt bits are in consecutive order.
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md:        descriptor for the bad block table mirror
  *
-*/
-static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
-                        struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+ * Read the bad block table(s) for all chips starting at a given page. We
+ * assume that the bbt bits are in consecutive order.
+ */
+static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
+                         struct nand_bbt_descr *td, struct nand_bbt_descr *md)
 {
        struct nand_chip *this = mtd->priv;
 
        /* Read the primary version, if available */
        if (td->options & NAND_BBT_VERSION) {
-               scan_read_raw(mtd, buf, (loff_t)td->pages[0] <<
-                               this->page_shift, mtd->writesize);
-               td->version[0] = buf[mtd->writesize + td->veroffs];
-               printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
-                      td->pages[0], td->version[0]);
+               scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
+                             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]);
        }
 
        /* Read the mirror version, if available */
        if (md && (md->options & NAND_BBT_VERSION)) {
-               scan_read_raw(mtd, buf, (loff_t)md->pages[0] <<
-                               this->page_shift, mtd->writesize);
-               md->version[0] = buf[mtd->writesize + md->veroffs];
-               printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
-                      md->pages[0], md->version[0]);
-       }
-       return 1;
-}
-
-/*
- * 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 len)
-{
-       int ret, j;
-
-       ret = scan_read_raw(mtd, buf, offs, readlen);
-       if (ret)
-               return ret;
-
-       for (j = 0; j < len; j++, buf += scanlen) {
-               if (check_pattern(buf, scanlen, mtd->writesize, bd))
-                       return 1;
+               scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
+                             mtd->writesize, md);
+               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]);
        }
-       return 0;
 }
 
-/*
- * Scan a given block partially
- */
+/* 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 len)
+                          loff_t offs, uint8_t *buf, int numpages)
 {
        struct mtd_oob_ops ops;
        int j, ret;
@@ -349,16 +437,16 @@ 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++) {
+       for (j = 0; j < numpages; j++) {
                /*
-                * Read the full oob until read_oob is fixed to
-                * handle single byte reads for 16 bit
-                * buswidth
+                * 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);
-               if (ret)
+               ret = mtd_read_oob(mtd, offs, &ops);
+               /* Ignore ECC errors when checking for BBM */
+               if (ret && !mtd_is_bitflip_or_eccerr(ret))
                        return ret;
 
                if (check_short_pattern(buf, bd))
@@ -371,84 +459,65 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
 
 /**
  * create_bbt - [GENERIC] Create a bad block table by scanning the device
- * @mtd:       MTD device structure
- * @buf:       temporary buffer
- * @bd:                descriptor for the good/bad block search pattern
- * @chip:      create the table for a specific chip, -1 read all chips.
- *             Applies only if NAND_BBT_PERCHIP option is set
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @bd: descriptor for the good/bad block search pattern
+ * @chip: create the table for a specific chip, -1 read all chips; applies only
+ *        if NAND_BBT_PERCHIP option is set
  *
- * Create a bad block table by scanning the device
- * for the given good/bad block identify pattern
+ * Create a bad block table by scanning the device for the given good/bad block
+ * identify pattern.
  */
 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, len, scanlen;
+       int i, numblocks, numpages;
        int startblock;
        loff_t from;
-       size_t readlen;
 
-       MTDDEBUG (MTD_DEBUG_LEVEL0, "Scanning device for bad blocks\n");
+       pr_info("Scanning device for bad blocks\n");
 
-       if (bd->options & NAND_BBT_SCANALLPAGES)
-               len = 1 << (this->bbt_erase_shift - this->page_shift);
-       else {
-               if (bd->options & NAND_BBT_SCAN2NDPAGE)
-                       len = 2;
-               else
-                       len = 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 = len * mtd->writesize;
-       }
+       if (bd->options & NAND_BBT_SCAN2NDPAGE)
+               numpages = 2;
+       else
+               numpages = 1;
 
        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 {
                if (chip >= this->numchips) {
-                       printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
+                       pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n",
                               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;
        }
 
-       for (i = startblock; i < numblocks;) {
+       if (this->bbt_options & NAND_BBT_SCANLASTPAGE)
+               from += mtd->erasesize - (mtd->writesize * numpages);
+
+       for (i = startblock; i < numblocks; i++) {
                int ret;
 
-               if (bd->options & NAND_BBT_SCANALLPAGES)
-                       ret = scan_block_full(mtd, bd, from, buf, readlen,
-                                             scanlen, len);
-               else
-                       ret = scan_block_fast(mtd, bd, from, buf, len);
+               BUG_ON(bd->options & NAND_BBT_NO_OOB);
 
+               ret = scan_block_fast(mtd, bd, from, buf, numpages);
                if (ret < 0)
                        return ret;
 
                if (ret) {
-                       this->bbt[i >> 3] |= 0x03 << (i & 0x6);
-                       MTDDEBUG (MTD_DEBUG_LEVEL0,
-                                 "Bad eraseblock %d at 0x%012llx\n",
-                                 i >> 1, (unsigned long long)from);
+                       bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD);
+                       pr_warn("Bad eraseblock %d at 0x%012llx\n",
+                               i, (unsigned long long)from);
                        mtd->ecc_stats.badblocks++;
                }
 
-               i += 2;
                from += (1 << this->bbt_erase_shift);
        }
        return 0;
@@ -456,31 +525,33 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
 
 /**
  * search_bbt - [GENERIC] scan the device for a specific bad block table
- * @mtd:       MTD device structure
- * @buf:       temporary buffer
- * @td:                descriptor for the bad block table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
  *
- * Read the bad block table by searching for a given ident pattern.
- * Search is preformed either from the beginning up or from the end of
- * the device downwards. The search starts always at the start of a
- * block.
- * If the option NAND_BBT_PERCHIP is given, each chip is searched
- * for a bbt, which contains the bad block information of this chip.
- * This is necessary to provide support for certain DOC devices.
+ * Read the bad block table by searching for a given ident pattern. Search is
+ * preformed either from the beginning up or from the end of the device
+ * downwards. The search starts always at the start of a block. If the option
+ * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains
+ * the bad block information of this chip. This is necessary to provide support
+ * for certain DOC devices.
  *
- * The bbt ident pattern resides in the oob area of the first page
- * in a block.
+ * The bbt ident pattern resides in the oob area of the first page in a block.
  */
 static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
 {
        struct nand_chip *this = mtd->priv;
        int i, chips;
+#ifndef __UBOOT__
        int bits, startblock, block, dir;
+#else
+       int startblock, block, dir;
+#endif
        int scanlen = mtd->writesize + mtd->oobsize;
        int bbtblocks;
        int blocktopage = this->bbt_erase_shift - this->page_shift;
 
-       /* Search direction top -> down ? */
+       /* Search direction top -> down? */
        if (td->options & NAND_BBT_LASTBLOCK) {
                startblock = (mtd->size >> this->bbt_erase_shift) - 1;
                dir = -1;
@@ -489,7 +560,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
                dir = 1;
        }
 
-       /* Do we have a bbt per chip ? */
+       /* Do we have a bbt per chip? */
        if (td->options & NAND_BBT_PERCHIP) {
                chips = this->numchips;
                bbtblocks = this->chipsize >> this->bbt_erase_shift;
@@ -499,8 +570,10 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
                bbtblocks = mtd->size >> this->bbt_erase_shift;
        }
 
+#ifndef __UBOOT__
        /* Number of bits for each erase block in the bbt */
        bits = td->options & NAND_BBT_NRBITS_MSK;
+#endif
 
        for (i = 0; i < chips; i++) {
                /* Reset version information */
@@ -513,11 +586,12 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
                        loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
 
                        /* Read first page */
-                       scan_read_raw(mtd, buf, offs, mtd->writesize);
+                       scan_read(mtd, buf, offs, mtd->writesize, td);
                        if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
                                td->pages[i] = actblock << blocktopage;
                                if (td->options & NAND_BBT_VERSION) {
-                                       td->version[i] = buf[mtd->writesize + td->veroffs];
+                                       offs = bbt_get_ver_offs(mtd, td);
+                                       td->version[i] = buf[offs];
                                }
                                break;
                        }
@@ -527,24 +601,26 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
        /* Check, if we found a bbt for each requested chip */
        for (i = 0; i < chips; i++) {
                if (td->pages[i] == -1)
-                       printk(KERN_WARNING "Bad block table not found for chip %d\n", i);
+                       pr_warn("Bad block table not found for chip %d\n", i);
                else
-                       printk(KERN_DEBUG "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;
 }
 
 /**
  * search_read_bbts - [GENERIC] scan the device for bad block table(s)
- * @mtd:       MTD device structure
- * @buf:       temporary buffer
- * @td:                descriptor for the bad block table
- * @md:                descriptor for the bad block table mirror
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
  *
- * Search and read the bad block table(s)
-*/
-static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+ * Search and read the bad block table(s).
+ */
+static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf,
+                            struct nand_bbt_descr *td,
+                            struct nand_bbt_descr *md)
 {
        /* Search the primary table */
        search_bbt(mtd, buf, td);
@@ -552,32 +628,27 @@ static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt
        /* Search the mirror table */
        if (md)
                search_bbt(mtd, buf, md);
-
-       /* Force result check */
-       return 1;
 }
 
 /**
  * write_bbt - [GENERIC] (Re)write the bad block table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
+ * @chipsel: selector for a specific chip, -1 for all
  *
- * @mtd:       MTD device structure
- * @buf:       temporary buffer
- * @td:                descriptor for the bad block table
- * @md:                descriptor for the bad block table mirror
- * @chipsel:   selector for a specific chip, -1 for all
- *
- * (Re)write the bad block table
- *
-*/
+ * (Re)write the bad block table.
+ */
 static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                     struct nand_bbt_descr *td, struct nand_bbt_descr *md,
                     int chipsel)
 {
        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;
@@ -587,14 +658,14 @@ 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;
-       /* Write bad block table per chip rather than per device ? */
+       /* Write bad block table per chip rather than per device? */
        if (td->options & NAND_BBT_PERCHIP) {
                numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
-               /* Full device write or specific chip ? */
+               /* Full device write or specific chip? */
                if (chipsel == -1) {
                        nrchips = this->numchips;
                } else {
@@ -608,8 +679,8 @@ 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
+               /*
+                * 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.
                 */
@@ -618,8 +689,10 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                        goto write;
                }
 
-               /* Automatic placement of the bad block table */
-               /* Search direction top -> down ? */
+               /*
+                * Automatic placement of the bad block table. Search direction
+                * top -> down?
+                */
                if (td->options & NAND_BBT_LASTBLOCK) {
                        startblock = numblocks * (chip + 1) - 1;
                        dir = -1;
@@ -631,10 +704,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                for (i = 0; i < td->maxblocks; i++) {
                        int block = startblock + dir * i;
                        /* Check, if the block is bad */
-                       switch ((this->bbt[block >> 2] >>
-                                (2 * (block & 0x03))) & 0x03) {
-                       case 0x01:
-                       case 0x03:
+                       switch (bbt_get_entry(this, block)) {
+                       case BBT_BLOCK_WORN:
+                       case BBT_BLOCK_FACTORY_BAD:
                                continue;
                        }
                        page = block <<
@@ -643,7 +715,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                        if (!md || md->pages[chip] != page)
                                goto write;
                }
-               printk(KERN_ERR "No space left to write bad block table\n");
+               pr_err("No space left to write bad block table\n");
                return -ENOSPC;
        write:
 
@@ -666,31 +738,27 @@ 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;
 
-               to = ((loff_t) page) << this->page_shift;
-
-               /* Must we save the block contents ? */
+               /* 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);
+                       res = mtd_read(mtd, to, len, &retlen, buf);
                        if (res < 0) {
                                if (retlen != len) {
-                                       printk(KERN_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;
                                }
-                               printk(KERN_WARNING "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;
                        ops.oobbuf = &buf[len];
-                       res = mtd->read_oob(mtd, to + mtd->writesize, &ops);
+                       res = mtd_read_oob(mtd, to + mtd->writesize, &ops);
                        if (res < 0 || ops.oobretlen != ops.ooblen)
                                goto outerr;
 
@@ -698,15 +766,29 @@ 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) {
+                       ooboffs = 0;
+                       offs = td->len;
+                       /* The version byte */
+                       if (td->options & NAND_BBT_VERSION)
+                               offs++;
+                       /* Calc length */
+                       len = (size_t)(numblocks >> sft);
+                       len += offs;
+                       /* Make it page aligned! */
+                       len = ALIGN(len, mtd->writesize);
+                       /* Preset the buffer with 0xff */
+                       memset(buf, 0xff, len);
+                       /* Pattern is located at the begin of first page */
+                       memcpy(buf, td->pattern, td->len);
                } else {
                        /* Calc length */
-                       len = (size_t) (numblocks >> sft);
-                       /* Make it page aligned ! */
-                       len = (len + (mtd->writesize - 1)) &
-                               ~(mtd->writesize - 1);
+                       len = (size_t)(numblocks >> sft);
+                       /* Make it page aligned! */
+                       len = ALIGN(len, mtd->writesize);
                        /* Preset the buffer with 0xff */
                        memset(buf, 0xff, len +
                               (len >> this->page_shift)* mtd->oobsize);
@@ -719,17 +801,13 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                if (td->options & NAND_BBT_VERSION)
                        buf[ooboffs + td->veroffs] = td->version[chip];
 
-               /* walk through the memory table */
-               for (i = 0; i < numblocks;) {
+               /* Walk through the memory table */
+               for (i = 0; i < numblocks; i++) {
                        uint8_t dat;
-                       dat = this->bbt[bbtoffs + (i >> 2)];
-                       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));
@@ -740,13 +818,14 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                if (res < 0)
                        goto outerr;
 
-               res = scan_write_bbt(mtd, to, len, buf, &buf[len]);
+               res = scan_write_bbt(mtd, to, len, buf,
+                               td->options & NAND_BBT_NO_OOB ? NULL :
+                               &buf[len]);
                if (res < 0)
                        goto outerr;
 
-               printk(KERN_DEBUG "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;
@@ -754,48 +833,45 @@ 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;
 }
 
 /**
  * nand_memory_bbt - [GENERIC] create a memory based bad block table
- * @mtd:       MTD device structure
- * @bd:                descriptor for the good/bad block search pattern
+ * @mtd: MTD device structure
+ * @bd: descriptor for the good/bad block search pattern
  *
- * The function creates a memory based bbt by scanning the device
- * for manufacturer / software marked good / bad blocks
-*/
+ * The function creates a memory based bbt by scanning the device for
+ * manufacturer / software marked good / bad blocks.
+ */
 static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
 {
        struct nand_chip *this = mtd->priv;
 
-       bd->options &= ~NAND_BBT_SCANEMPTY;
        return create_bbt(mtd, this->buffers->databuf, bd, -1);
 }
 
 /**
  * check_create - [GENERIC] create and write bbt(s) if necessary
- * @mtd:       MTD device structure
- * @buf:       temporary buffer
- * @bd:                descriptor for the good/bad block search pattern
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @bd: descriptor for the good/bad block search pattern
  *
- * The function checks the results of the previous call to read_bbt
- * and creates / updates the bbt(s) if necessary
- * Creation is necessary if no bbt was found for the chip/device
- * Update is necessary if one of the tables is missing or the
- * version nr. of one table is less than the other
-*/
+ * The function checks the results of the previous call to read_bbt and creates
+ * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found
+ * for the chip/device. Update is necessary if one of the tables is missing or
+ * the version nr. of one table is less than the other.
+ */
 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;
        struct nand_bbt_descr *rd, *rd2;
 
-       /* Do we have a bbt per chip ? */
+       /* Do we have a bbt per chip? */
        if (td->options & NAND_BBT_PERCHIP)
                chips = this->numchips;
        else
@@ -803,85 +879,98 @@ 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;
-               /* Per chip or per device ? */
+               res = res2 = 0;
+               /* Per chip or per device? */
                chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
-               /* Mirrored table avilable ? */
+               /* 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) */
-               create_bbt(mtd, buf, bd, chipsel);
-
-               td->version[i] = 1;
-               if (md)
-                       md->version[i] = 1;
-       writecheck:
-               /* read back first ? */
-               if (rd)
-                       read_abs_bbt(mtd, buf, rd, chipsel);
-               /* If they weren't versioned, read both. */
-               if (rd2)
-                       read_abs_bbt(mtd, buf, rd2, chipsel);
-
-               /* Write the bad block table to the device ? */
+               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;
+               }
+
+               /* Read back first? */
+               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) {
+                       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)) {
                        res = write_bbt(mtd, buf, td, md, chipsel);
                        if (res < 0)
                                return res;
                }
 
-               /* Write the mirror bad block table to the device ? */
+               /* Write the mirror bad block table to the device? */
                if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
                        res = write_bbt(mtd, buf, md, td, chipsel);
                        if (res < 0)
@@ -893,20 +982,19 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
 
 /**
  * mark_bbt_regions - [GENERIC] mark the bad block table regions
- * @mtd:       MTD device structure
- * @td:                bad block table descriptor
+ * @mtd: MTD device structure
+ * @td: bad block table descriptor
  *
- * The bad block table regions are marked as "bad" to prevent
- * accidental erasures / writes. The regions are identified by
- * the mark 0x02.
-*/
+ * The bad block table regions are marked as "bad" to prevent accidental
+ * erasures / writes. The regions are identified by the mark 0x02.
+ */
 static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
 {
        struct nand_chip *this = mtd->priv;
        int i, j, chips, block, nrblocks, update;
-       uint8_t oldval, newval;
+       uint8_t oldval;
 
-       /* Do we have a bbt per chip ? */
+       /* Do we have a bbt per chip? */
        if (td->options & NAND_BBT_PERCHIP) {
                chips = this->numchips;
                nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
@@ -921,13 +1009,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 = this->bbt[(block >> 3)];
-                       newval = oldval | (0x2 << (block & 0x06));
-                       this->bbt[(block >> 3)] = newval;
-                       if ((oldval != newval) && td->reserved_block_code)
+                       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));
+                                               this->bbt_erase_shift);
                        continue;
                }
                update = 0;
@@ -935,38 +1022,84 @@ 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 = this->bbt[(block >> 3)];
-                       newval = oldval | (0x2 << (block & 0x06));
-                       this->bbt[(block >> 3)] = newval;
-                       if (oldval != newval)
+                       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
-                  new ones have been marked, then we need to update the stored
-                  bbts.  This should only happen once. */
+               /*
+                * If we want reserved blocks to be recorded to flash, and some
+                * new ones have been marked, then we need to update the stored
+                * 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);
        }
 }
 
 /**
- * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
- * @mtd:       MTD device structure
- * @bd:                descriptor for the good/bad block search pattern
+ * verify_bbt_descr - verify the bad block description
+ * @mtd: MTD device structure
+ * @bd: the table to verify
  *
- * The function checks, if a bad block table(s) is/are already
- * available. If not it scans the device for manufacturer
- * marked good / bad blocks and writes the bad block table(s) to
- * the selected place.
+ * This functions performs a few sanity checks on the bad block description
+ * table.
+ */
+static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+       struct nand_chip *this = mtd->priv;
+       u32 pattern_len;
+       u32 bits;
+       u32 table_size;
+
+       if (!bd)
+               return;
+
+       pattern_len = bd->len;
+       bits = bd->options & NAND_BBT_NRBITS_MSK;
+
+       BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) &&
+                       !(this->bbt_options & NAND_BBT_USE_FLASH));
+       BUG_ON(!bits);
+
+       if (bd->options & NAND_BBT_VERSION)
+               pattern_len++;
+
+       if (bd->options & NAND_BBT_NO_OOB) {
+               BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH));
+               BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB));
+               BUG_ON(bd->offs);
+               if (bd->options & NAND_BBT_VERSION)
+                       BUG_ON(bd->veroffs != bd->len);
+               BUG_ON(bd->options & NAND_BBT_SAVECONTENT);
+       }
+
+       if (bd->options & NAND_BBT_PERCHIP)
+               table_size = this->chipsize >> this->bbt_erase_shift;
+       else
+               table_size = mtd->size >> this->bbt_erase_shift;
+       table_size >>= 3;
+       table_size *= bits;
+       if (bd->options & NAND_BBT_NO_OOB)
+               table_size += pattern_len;
+       BUG_ON(table_size > (1 << this->bbt_erase_shift));
+}
+
+/**
+ * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
+ * @mtd: MTD device structure
+ * @bd: descriptor for the good/bad block search pattern
  *
- * The bad block table memory is allocated here. It must be freed
- * by calling the nand_free_bbt function.
+ * The function checks, if a bad block table(s) is/are already available. If
+ * not it scans the device for manufacturer marked good / bad blocks and writes
+ * the bad block table(s) to the selected place.
  *
-*/
+ * 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)
 {
        struct nand_chip *this = mtd->priv;
@@ -976,46 +1109,48 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
        struct nand_bbt_descr *md = this->bbt_md;
 
        len = mtd->size >> (this->bbt_erase_shift + 2);
-       /* Allocate memory (2bit per block) and clear the memory bad block table */
+       /*
+        * Allocate memory (2bit per block) and clear the memory bad block
+        * table.
+        */
        this->bbt = kzalloc(len, GFP_KERNEL);
-       if (!this->bbt) {
-               printk(KERN_ERR "nand_scan_bbt: Out of memory\n");
+       if (!this->bbt)
                return -ENOMEM;
-       }
 
-       /* If no primary table decriptor is given, scan the device
-        * to build a memory based bad block table
+       /*
+        * If no primary table decriptor is given, scan the device to build a
+        * memory based bad block table.
         */
        if (!td) {
                if ((res = nand_memory_bbt(mtd, bd))) {
-                       printk(KERN_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;
                }
                return res;
        }
+       verify_bbt_descr(mtd, td);
+       verify_bbt_descr(mtd, md);
 
        /* Allocate a temporary buffer for one eraseblock incl. oob */
        len = (1 << this->bbt_erase_shift);
        len += (len >> this->page_shift) * mtd->oobsize;
        buf = vmalloc(len);
        if (!buf) {
-               printk(KERN_ERR "nand_bbt: Out of memory\n");
                kfree(this->bbt);
                this->bbt = NULL;
                return -ENOMEM;
        }
 
-       /* Is the bbt at a given page ? */
+       /* Is the bbt at a given page? */
        if (td->options & NAND_BBT_ABSPAGE) {
-               res = read_abs_bbts(mtd, buf, td, md);
+               read_abs_bbts(mtd, buf, td, md);
        } else {
                /* Search the bad block table using a pattern in oob */
-               res = search_read_bbts(mtd, buf, td, md);
+               search_read_bbts(mtd, buf, td, md);
        }
 
-       if (res)
-               res = check_create(mtd, buf, bd);
+       res = check_create(mtd, buf, bd);
 
        /* Prevent the bbt regions from erasing / writing */
        mark_bbt_region(mtd, td);
@@ -1027,16 +1162,16 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
 }
 
 /**
- * nand_update_bbt - [NAND Interface] update bad block table(s)
- * @mtd:       MTD device structure
- * @offs:      the offset of the newly marked block
+ * nand_update_bbt - update bad block table(s)
+ * @mtd: MTD device structure
+ * @offs: the offset of the newly marked block
  *
- * The function updates the bad block table(s)
-*/
-int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
+ * The function updates the bad block table(s).
+ */
+static 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;
@@ -1049,14 +1184,10 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
        len = (1 << this->bbt_erase_shift);
        len += (len >> this->page_shift) * mtd->oobsize;
        buf = kmalloc(len, GFP_KERNEL);
-       if (!buf) {
-               printk(KERN_ERR "nand_update_bbt: Out of memory\n");
+       if (!buf)
                return -ENOMEM;
-       }
 
-       writeops = md != NULL ? 0x03 : 0x01;
-
-       /* Do we have a bbt per chip ? */
+       /* Do we have a bbt per chip? */
        if (td->options & NAND_BBT_PERCHIP) {
                chip = (int)(offs >> this->chip_shift);
                chipsel = chip;
@@ -1069,14 +1200,14 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
        if (md)
                md->version[chip]++;
 
-       /* Write the bad block table to the device ? */
-       if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+       /* Write the bad block table to the device? */
+       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)) {
+       /* Write the mirror bad block table to the device? */
+       if (md && (md->options & NAND_BBT_WRITE)) {
                res = write_bbt(mtd, buf, md, td, chipsel);
        }
 
@@ -1085,49 +1216,13 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
        return res;
 }
 
-/* Define some generic bad / good block scan pattern which are used
- * while scanning a device for factory marked good / bad blocks. */
+/*
+ * Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks.
+ */
 static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
 
-static struct nand_bbt_descr smallpage_memorybased = {
-       .options = NAND_BBT_SCAN2NDPAGE,
-       .offs = 5,
-       .len = 1,
-       .pattern = scan_ff_pattern
-};
-
-static struct nand_bbt_descr largepage_memorybased = {
-       .options = 0,
-       .offs = 0,
-       .len = 2,
-       .pattern = scan_ff_pattern
-};
-
-static struct nand_bbt_descr smallpage_flashbased = {
-       .options = NAND_BBT_SCAN2NDPAGE,
-       .offs = 5,
-       .len = 1,
-       .pattern = scan_ff_pattern
-};
-
-static struct nand_bbt_descr largepage_flashbased = {
-       .options = NAND_BBT_SCAN2NDPAGE,
-       .offs = 0,
-       .len = 2,
-       .pattern = scan_ff_pattern
-};
-
-static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 };
-
-static struct nand_bbt_descr agand_flashbased = {
-       .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
-       .offs = 0x20,
-       .len = 6,
-       .pattern = scan_agand_pattern
-};
-
-/* Generic flash bbt decriptors
-*/
+/* Generic flash bbt descriptors */
 static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
 static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
 
@@ -1137,7 +1232,7 @@ static struct nand_bbt_descr bbt_main_descr = {
        .offs = 8,
        .len = 4,
        .veroffs = 12,
-       .maxblocks = 4,
+       .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
        .pattern = bbt_pattern
 };
 
@@ -1147,93 +1242,142 @@ static struct nand_bbt_descr bbt_mirror_descr = {
        .offs = 8,
        .len = 4,
        .veroffs = 12,
-       .maxblocks = 4,
+       .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
        .pattern = mirror_pattern
 };
 
+static struct nand_bbt_descr bbt_main_no_oob_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+               | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
+               | NAND_BBT_NO_OOB,
+       .len = 4,
+       .veroffs = 4,
+       .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
+       .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_no_oob_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+               | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
+               | NAND_BBT_NO_OOB,
+       .len = 4,
+       .veroffs = 4,
+       .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
+       .pattern = mirror_pattern
+};
+
+#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB)
 /**
- * nand_default_bbt - [NAND Interface] Select a default bad block table for the device
- * @mtd:       MTD device structure
+ * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure
+ * @this: NAND chip to create descriptor for
  *
- * This function selects the default bad block table
- * support for the device and calls the nand_scan_bbt function
+ * This function allocates and initializes a nand_bbt_descr for BBM detection
+ * based on the properties of @this. The new descriptor is stored in
+ * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when
+ * passed to this function.
+ */
+static int nand_create_badblock_pattern(struct nand_chip *this)
+{
+       struct nand_bbt_descr *bd;
+       if (this->badblock_pattern) {
+               pr_warn("Bad block pattern already allocated; not replacing\n");
+               return -EINVAL;
+       }
+       bd = kzalloc(sizeof(*bd), GFP_KERNEL);
+       if (!bd)
+               return -ENOMEM;
+       bd->options = this->bbt_options & BADBLOCK_SCAN_MASK;
+       bd->offs = this->badblockpos;
+       bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1;
+       bd->pattern = scan_ff_pattern;
+       bd->options |= NAND_BBT_DYNAMICSTRUCT;
+       this->badblock_pattern = bd;
+       return 0;
+}
+
+/**
+ * nand_default_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)
 {
        struct nand_chip *this = mtd->priv;
 
-       /* Default for AG-AND. We must use a flash based
-        * bad block table as the devices have factory marked
-        * _good_ blocks. Erasing those blocks leads to loss
-        * of the good / bad information, so we _must_ store
-        * this information in a good / bad table during
-        * startup
-        */
-       if (this->options & NAND_IS_AND) {
+       /* Is a flash based bad block table requested? */
+       if (this->bbt_options & NAND_BBT_USE_FLASH) {
                /* Use the default pattern descriptors */
                if (!this->bbt_td) {
-                       this->bbt_td = &bbt_main_descr;
-                       this->bbt_md = &bbt_mirror_descr;
-               }
-               this->options |= NAND_USE_FLASH_BBT;
-               return nand_scan_bbt(mtd, &agand_flashbased);
-       }
-
-       /* Is a flash based bad block table requested ? */
-       if (this->options & NAND_USE_FLASH_BBT) {
-               /* Use the default pattern descriptors */
-               if (!this->bbt_td) {
-                       this->bbt_td = &bbt_main_descr;
-                       this->bbt_md = &bbt_mirror_descr;
-               }
-               if (!this->badblock_pattern) {
-                       this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;
+                       if (this->bbt_options & NAND_BBT_NO_OOB) {
+                               this->bbt_td = &bbt_main_no_oob_descr;
+                               this->bbt_md = &bbt_mirror_no_oob_descr;
+                       } else {
+                               this->bbt_td = &bbt_main_descr;
+                               this->bbt_md = &bbt_mirror_descr;
+                       }
                }
        } else {
                this->bbt_td = NULL;
                this->bbt_md = NULL;
-               if (!this->badblock_pattern) {
-                       this->badblock_pattern = (mtd->writesize > 512) ?
-                           &largepage_memorybased : &smallpage_memorybased;
-               }
        }
+
+       if (!this->badblock_pattern)
+               nand_create_badblock_pattern(this);
+
        return nand_scan_bbt(mtd, this->badblock_pattern);
 }
 
 /**
  * nand_isbad_bbt - [NAND Interface] Check if a block is bad
- * @mtd:       MTD device structure
- * @offs:      offset in the device
- * @allowbbt:  allow access to bad block table region
- *
-*/
+ * @mtd: MTD device structure
+ * @offs: offset in the device
+ * @allowbbt: allow access to bad block table region
+ */
 int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
 {
        struct nand_chip *this = mtd->priv;
-       int block;
-       uint8_t res;
+       int block, res;
 
-       /* Get block number * 2 */
-       block = (int)(offs >> (this->bbt_erase_shift - 1));
-       res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
+       block = (int)(offs >> this->bbt_erase_shift);
+       res = bbt_get_entry(this, block);
 
-       MTDDEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: "
-                 "(block %d) 0x%02x\n", (unsigned int)offs, res, block >> 1);
+       pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: "
+                       "(block %d) 0x%02x\n",
+                       (unsigned int)offs, block, res);
 
-       switch ((int)res) {
-       case 0x00:
+       switch (res) {
+       case BBT_BLOCK_GOOD:
                return 0;
-       case 0x01:
+       case BBT_BLOCK_WORN:
                return 1;
-       case 0x02:
+       case BBT_BLOCK_RESERVED:
                return allowbbt ? 0 : 1;
        }
        return 1;
 }
 
-/* XXX U-BOOT XXX */
-#if 0
+/**
+ * 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)
+{
+       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, BBT_BLOCK_WORN);
+
+       /* Update flash-based bad block table */
+       if (this->bbt_options & NAND_BBT_USE_FLASH)
+               ret = nand_update_bbt(mtd, offs);
+
+       return ret;
+}
+
 EXPORT_SYMBOL(nand_scan_bbt);
-EXPORT_SYMBOL(nand_default_bbt);
-#endif