]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
[PATCH] OneNAND: Simple Bad Block handling support
authorKyungmin Park <kyungmin.park@samsung.com>
Sat, 3 Sep 2005 06:15:48 +0000 (07:15 +0100)
committerThomas Gleixner <tglx@mtd.linutronix.de>
Sun, 6 Nov 2005 20:20:53 +0000 (21:20 +0100)
Based on NAND memory bad block table code

Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
drivers/mtd/onenand/Makefile
drivers/mtd/onenand/onenand_base.c
include/linux/mtd/onenand.h

index f4e75864d8b5c0bc60768c51c16bf4acaaa2a889..243c7592a1119945fd9905d9a8ad139b3d81cb4d 100644 (file)
@@ -3,7 +3,9 @@
 #
 
 # Core functionality.
-obj-$(CONFIG_MTD_ONENAND)              += onenand_base.o
+obj-$(CONFIG_MTD_ONENAND)              += onenand.o
 
 # Board specific.
 obj-$(CONFIG_MTD_ONENAND_OMAP)         += omap-onenand.o
+
+onenand-objs = onenand_base.o onenand_bbt.o
index e87489505772621e275650232c81b8a0af0a674e..bdeac01e659f19cda75e544f687d42023fba9353 100644 (file)
@@ -311,19 +311,21 @@ static int onenand_wait(struct mtd_info *mtd, int state)
        ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
 
        if (ctrl & ONENAND_CTRL_ERROR) {
-               DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x", ctrl);
-               return -EIO;
+               /* It maybe occur at initial bad block */
+               DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl);
+               /* Clear other interrupt bits for preventing ECC error */
+               interrupt &= ONENAND_INT_MASTER;
        }
 
        if (ctrl & ONENAND_CTRL_LOCK) {
-               DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x", ctrl);
-               return -EIO;
+               DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x\n", ctrl);
+               return -EACCES;
        }
 
        if (interrupt & ONENAND_INT_READ) {
                ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
                if (ecc & ONENAND_ECC_2BIT_ALL) {
-                       DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x", ecc);
+                       DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc);
                        return -EBADMSG;
                }
        }
@@ -1059,6 +1061,25 @@ static int onenand_writev(struct mtd_info *mtd, const struct kvec *vecs,
        return onenand_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL);
 }
 
+/**
+ * onenand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * @param mtd          MTD device structure
+ * @param ofs          offset from device start
+ * @param getchip      0, if the chip is already selected
+ * @param allowbbt     1, if its allowed to access the bbt area
+ *
+ * Check, if the block is bad. Either by reading the bad block table or
+ * calling of the scan function.
+ */
+static int onenand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct bbm_info *bbm = this->bbm;
+
+       /* Return info from the table */
+       return bbm->isbad_bbt(mtd, ofs, allowbbt);
+}
+
 /**
  * onenand_erase - [MTD Interface] erase block(s)
  * @param mtd          MTD device structure
@@ -1109,7 +1130,12 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
 
        while (len) {
 
-               /* TODO Check badblock */
+               /* Check if we have a bad block, we do not erase bad blocks */
+               if (onenand_block_checkbad(mtd, addr, 0, 0)) {
+                       printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%08x\n", (unsigned int) addr);
+                       instr->state = MTD_ERASE_FAILED;
+                       goto erase_exit;
+               }
 
                this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
 
@@ -1161,34 +1187,70 @@ static void onenand_sync(struct mtd_info *mtd)
        onenand_release_device(mtd);
 }
 
+
 /**
  * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
  * @param mtd          MTD device structure
  * @param ofs          offset relative to mtd start
+ *
+ * Check whether the block is bad
  */
 static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
 {
-       /*
-        * TODO 
-        * 1. Bad block table (BBT)
-        *   -> using NAND BBT to support JFFS2
-        * 2. Bad block management (BBM)
-        *   -> bad block replace scheme
-        *
-        * Currently we do nothing
-        */
-       return 0;
+       /* Check for invalid offset */
+       if (ofs > mtd->size)
+               return -EINVAL;
+
+       return onenand_block_checkbad(mtd, ofs, 1, 0);
+}
+
+/**
+ * onenand_default_block_markbad - [DEFAULT] mark a block bad
+ * @param mtd          MTD device structure
+ * @param ofs          offset from device start
+ *
+ * This is the default implementation, which can be overridden by
+ * a hardware specific driver.
+ */
+static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct bbm_info *bbm = this->bbm;
+       u_char buf[2] = {0, 0};
+       size_t retlen;
+       int block;
+
+       /* Get block number */
+       block = ((int) ofs) >> bbm->bbt_erase_shift;
+        if (bbm->bbt)
+                bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+        /* We write two bytes, so we dont have to mess with 16 bit access */
+        ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
+        return mtd->write_oob(mtd, ofs , 2, &retlen, buf);
 }
 
 /**
  * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
  * @param mtd          MTD device structure
  * @param ofs          offset relative to mtd start
+ *
+ * Mark the block as bad
  */
 static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
 {
-       /* see above */
-       return 0;
+       struct onenand_chip *this = mtd->priv;
+       int ret;
+
+       ret = onenand_block_isbad(mtd, ofs);
+       if (ret) {
+               /* If it was bad already, return success and do nothing */
+               if (ret > 0)
+                       return 0;
+               return ret;
+       }
+
+       return this->block_markbad(mtd, ofs);
 }
 
 /**
@@ -1411,6 +1473,11 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        if (!this->write_bufferram)
                this->write_bufferram = onenand_write_bufferram;
 
+       if (!this->block_markbad)
+               this->block_markbad = onenand_default_block_markbad;
+       if (!this->scan_bbt)
+               this->scan_bbt = onenand_default_bbt;
+
        if (onenand_probe(mtd))
                return -ENXIO;
 
@@ -1472,7 +1539,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        /* Unlock whole block */
        mtd->unlock(mtd, 0x0, this->chipsize);
 
-       return 0;
+       return this->scan_bbt(mtd);
 }
 
 /**
index c557caa24a6c80b18c379da5e8309fdcafad177a..89aaffbc95768a5220a4adcfa551ef6f75bdc159 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <linux/spinlock.h>
 #include <linux/mtd/onenand_regs.h>
+#include <linux/mtd/bbm.h>
 
 #define MAX_BUFFERRAM          2
 
@@ -67,10 +68,14 @@ struct onenand_bufferram {
  * @param wait         [REPLACEABLE] hardware specific function for wait on ready
  * @param read_bufferram       [REPLACEABLE] hardware specific function for BufferRAM Area
  * @param write_bufferram      [REPLACEABLE] hardware specific function for BufferRAM Area
+ * @param read_word    [REPLACEABLE] hardware specific function for read register of OneNAND
+ * @param write_word   [REPLACEABLE] hardware specific function for write register of OneNAND
+ * @param scan_bbt     [REPLACEALBE] hardware specific function for scaning Bad block Table
  * @param chip_lock    [INTERN] spinlock used to protect access to this structure and the chip
  * @param wq           [INTERN] wait queue to sleep on if a OneNAND operation is in progress
  * @param state                [INTERN] the current state of the OneNAND device
  * @param autooob      [REPLACEABLE] the default (auto)placement scheme
+ * @param bbm          [REPLACEABLE] pointer to Bad Block Management 
  * @param priv         [OPTIONAL] pointer to private chip date
  */
 struct onenand_chip {
@@ -96,6 +101,8 @@ struct onenand_chip {
        unsigned short (*read_word)(void __iomem *addr);
        void (*write_word)(unsigned short value, void __iomem *addr);
        void (*mmcontrol)(struct mtd_info *mtd, int sync_read);
+       int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
+       int (*scan_bbt)(struct mtd_info *mtd);
 
        spinlock_t              chip_lock;
        wait_queue_head_t       wq;
@@ -103,6 +110,8 @@ struct onenand_chip {
 
        struct nand_oobinfo     *autooob;
 
+       void                    *bbm;
+
        void                    *priv;
 };