[MTD] OneNAND: Add missing files
authorKyungmin Park <kyungmin.park@samsung.com>
Tue, 27 Sep 2005 10:26:39 +0000 (11:26 +0100)
committerThomas Gleixner <tglx@mtd.linutronix.de>
Sun, 6 Nov 2005 21:39:23 +0000 (22:39 +0100)
Simple bad block table source and header files

Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
drivers/mtd/onenand/onenand_bbt.c [new file with mode: 0644]
include/linux/mtd/bbm.h [new file with mode: 0644]

diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
new file mode 100644 (file)
index 0000000..f40190f
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ *  linux/drivers/mtd/onenand/onenand_bbt.c
+ *
+ *  Bad Block Table support for the OneNAND driver
+ *
+ *  Copyright(c) 2005 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ *  Derived from nand_bbt.c
+ *
+ *  TODO:
+ *    Split BBT core and chip specific BBT.
+ */
+
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+#include <linux/mtd/compatmac.h>
+
+/**
+ * check_short_pattern - [GENERIC] check if a pattern is in the buffer
+ * @param buf          the buffer to search
+ * @param len          the length of buffer to search
+ * @param paglen       the pagelength
+ * @param 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 and the pattern is expected to start
+ * at offset 0.
+ *
+ */
+static int check_short_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
+{
+       int i;
+       uint8_t *p = buf;
+
+       /* Compare the pattern */
+       for (i = 0; i < td->len; i++) {
+               if (p[i] != td->pattern[i])
+                       return -1;
+       }
+        return 0;
+}
+
+/**
+ * create_bbt - [GENERIC] Create a bad block table by scanning the device
+ * @param mtd          MTD device structure
+ * @param buf          temporary buffer
+ * @param bd           descriptor for the good/bad block search pattern
+ * @param 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
+ */
+static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct bbm_info *bbm = this->bbm;
+       int i, j, numblocks, len, scanlen;
+       int startblock;
+       loff_t from;
+       size_t readlen, ooblen;
+
+       printk(KERN_INFO "Scanning device for bad blocks\n");
+
+       len = 1;
+
+       /* We need only read few bytes from the OOB area */
+       scanlen = ooblen = 0;
+       readlen = bd->len;
+
+       /* chip == -1 case only */
+       /* Note that numblocks is 2 * (real numblocks) here;
+        * see i += 2 below as it makses shifting and masking less painful
+        */
+       numblocks = mtd->size >> (bbm->bbt_erase_shift - 1);
+       startblock = 0;
+       from = 0;
+
+       for (i = startblock; i < numblocks; ) {
+               int ret;
+
+               for (j = 0; j < len; j++) {
+                       size_t retlen;
+
+                       /* No need to read pages fully,
+                        * just read required OOB bytes */
+                       ret = mtd->read_oob(mtd, from + j * mtd->oobblock + bd->offs,
+                                               readlen, &retlen, &buf[0]);
+
+                       if (ret)
+                               return ret;
+
+                       if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
+                               bbm->bbt[i >> 3] |= 0x03 << (i & 0x6);
+                               printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
+                                       i >> 1, (unsigned int) from);
+                               break;
+                       }
+               }
+               i += 2;
+               from += (1 << bbm->bbt_erase_shift);
+       }
+
+       return 0;
+}
+
+
+/**
+ * onenand_memory_bbt - [GENERIC] create a memory based bad block table
+ * @param mtd          MTD device structure
+ * @param 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
+ */
+static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+       unsigned char data_buf[MAX_ONENAND_PAGESIZE];
+
+        bd->options &= ~NAND_BBT_SCANEMPTY;
+        return create_bbt(mtd, data_buf, bd, -1);
+}
+
+/**
+ * onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad
+ * @param mtd          MTD device structure
+ * @param offs         offset in the device
+ * @param allowbbt     allow access to bad block table region
+ */
+static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct bbm_info *bbm = this->bbm;
+       int block;
+       uint8_t res;
+
+       /* Get block number * 2 */
+       block = (int) (offs >> (bbm->bbt_erase_shift - 1));
+       res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
+
+       DEBUG(MTD_DEBUG_LEVEL2, "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
+               (unsigned int) offs, block >> 1, res);
+
+       switch ((int) res) {
+       case 0x00:      return 0;
+       case 0x01:      return 1;
+       case 0x02:      return allowbbt ? 0 : 1;
+       }
+
+       return 1;
+}
+
+/**
+ * onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s)
+ * @param mtd          MTD device structure
+ * @param bd           descriptor for the good/bad block search pattern
+ *
+ * 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 onenand_free_bbt function.
+ *
+ */
+int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct bbm_info *bbm = this->bbm;
+       int len, ret = 0;
+
+       len = mtd->size >> (this->erase_shift + 2);
+       /* Allocate memory (2bit per block) */
+       bbm->bbt = kmalloc(len, GFP_KERNEL);
+       if (!bbm->bbt) {
+               printk(KERN_ERR "onenand_scan_bbt: Out of memory\n");
+               return -ENOMEM;
+       }
+       /* Clear the memory bad block table */
+       memset(bbm->bbt, 0x00, len);
+
+       /* Set the bad block position */
+       bbm->badblockpos = ONENAND_BADBLOCK_POS;
+
+       /* Set erase shift */
+       bbm->bbt_erase_shift = this->erase_shift;
+
+       if (!bbm->isbad_bbt)
+               bbm->isbad_bbt = onenand_isbad_bbt;
+
+       /* Scan the device to build a memory based bad block table */
+       if ((ret = onenand_memory_bbt(mtd, bd))) {
+               printk(KERN_ERR "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n");
+               kfree(bbm->bbt);
+               bbm->bbt = NULL;
+       }
+
+       return ret;
+}
+
+/*
+ * 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 largepage_memorybased = {
+       .options = 0,
+       .offs = 0,
+       .len = 2,
+       .pattern = scan_ff_pattern,
+};
+
+/**
+ * onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device
+ * @param mtd          MTD device structure
+ *
+ * This function selects the default bad block table
+ * support for the device and calls the onenand_scan_bbt function
+ */
+int onenand_default_bbt(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct bbm_info *bbm;
+
+       this->bbm = kmalloc(sizeof(struct bbm_info), GFP_KERNEL);
+       if (!this->bbm)
+               return -ENOMEM;
+
+       bbm = this->bbm;
+
+       memset(bbm, 0, sizeof(struct bbm_info));
+
+       /* 1KB page has same configuration as 2KB page */
+       if (!bbm->badblock_pattern)
+               bbm->badblock_pattern = &largepage_memorybased;
+
+       return onenand_scan_bbt(mtd, bbm->badblock_pattern);
+}
+
+EXPORT_SYMBOL(onenand_scan_bbt);
+EXPORT_SYMBOL(onenand_default_bbt);
diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h
new file mode 100644 (file)
index 0000000..92b42cb
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ *  linux/include/linux/mtd/bbm.h
+ *
+ *  NAND family Bad Block Management (BBM) header file
+ *    - Bad Block Table (BBT) implementation
+ *
+ *  Copyright (c) 2005 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ *  Copyright (c) 2000-2005
+ *  Thomas Gleixner <tglx@linuxtronix.de>
+ *
+ */
+#ifndef __LINUX_MTD_BBM_H
+#define __LINUX_MTD_BBM_H
+
+/* The maximum number of NAND chips in an array */
+#define NAND_MAX_CHIPS         8
+
+/**
+ * struct nand_bbt_descr - bad block table descriptor
+ * @param options      options for this descriptor
+ * @param pages                the page(s) where we find the bbt, used with
+ *                     option BBT_ABSPAGE when bbt is searched,
+ *                     then we store the found bbts pages here.
+ *                     Its an array and supports up to 8 chips now
+ * @param offs         offset of the pattern in the oob area of the page
+ * @param veroffs      offset of the bbt version counter in the oob are of the page
+ * @param version      version read from the bbt page during scan
+ * @param len          length of the pattern, if 0 no pattern check is performed
+ * @param maxblocks    maximum number of blocks to search for a bbt. This number of
+ *                     blocks is reserved at the end of the device 
+ *                     where the tables are written.
+ * @param reserved_block_code  if non-0, this pattern denotes a reserved
+ *                     (rather than bad) block in the stored bbt
+ * @param pattern      pattern to identify bad block table or factory marked
+ *                     good / bad blocks, can be NULL, if len = 0
+ *
+ * Descriptor for the bad block table marker and the descriptor for the
+ * pattern which identifies good and bad blocks. The assumption is made
+ * that the pattern and the version count are always located in the oob area
+ * of the first block.
+ */
+struct nand_bbt_descr {
+       int options;
+       int pages[NAND_MAX_CHIPS];
+       int offs;
+       int veroffs;
+       uint8_t version[NAND_MAX_CHIPS];
+       int len;
+       int maxblocks;
+       int reserved_block_code;
+       uint8_t *pattern;
+};
+
+/* Options for the bad block table descriptors */
+
+/* The number of bits used per block in the bbt on the device */
+#define NAND_BBT_NRBITS_MSK    0x0000000F
+#define NAND_BBT_1BIT          0x00000001
+#define NAND_BBT_2BIT          0x00000002
+#define NAND_BBT_4BIT          0x00000004
+#define NAND_BBT_8BIT          0x00000008
+/* The bad block table is in the last good block of the device */
+#define NAND_BBT_LASTBLOCK     0x00000010
+/* The bbt is at the given page, else we must scan for the bbt */
+#define NAND_BBT_ABSPAGE       0x00000020
+/* The bbt is at the given page, else we must scan for the bbt */
+#define NAND_BBT_SEARCH                0x00000040
+/* bbt is stored per chip on multichip devices */
+#define NAND_BBT_PERCHIP       0x00000080
+/* bbt has a version counter at offset veroffs */
+#define NAND_BBT_VERSION       0x00000100
+/* Create a bbt if none axists */
+#define NAND_BBT_CREATE                0x00000200
+/* Search good / bad pattern through all pages of a block */
+#define NAND_BBT_SCANALLPAGES  0x00000400
+/* Scan block empty during good / bad block scan */
+#define NAND_BBT_SCANEMPTY     0x00000800
+/* Write bbt if neccecary */
+#define NAND_BBT_WRITE         0x00001000
+/* Read and write back block contents when writing bbt */
+#define NAND_BBT_SAVECONTENT   0x00002000
+/* Search good / bad pattern on the first and the second page */
+#define NAND_BBT_SCAN2NDPAGE   0x00004000
+
+/* The maximum number of blocks to scan for a bbt */
+#define NAND_BBT_SCAN_MAXBLOCKS        4
+
+/*
+ * Constants for oob configuration
+ */
+#define ONENAND_BADBLOCK_POS   0
+
+/**
+ * struct bbt_info - [GENERIC] Bad Block Table data structure
+ * @param bbt_erase_shift      [INTERN] number of address bits in a bbt entry
+ * @param badblockpos          [INTERN] position of the bad block marker in the oob area
+ * @param bbt                  [INTERN] bad block table pointer
+ * @param badblock_pattern     [REPLACEABLE] bad block scan pattern used for initial bad block scan
+ * @param priv                 [OPTIONAL] pointer to private bbm date
+ */
+struct bbm_info {
+       int bbt_erase_shift;
+       int badblockpos;
+       int options;
+
+       uint8_t *bbt;
+
+       int (*isbad_bbt)(struct mtd_info *mtd, loff_t ofs, int allowbbt);
+
+       /* TODO Add more NAND specific fileds */
+       struct nand_bbt_descr *badblock_pattern;
+
+       void *priv;
+};
+
+/* OneNAND BBT interface */
+extern int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
+extern int onenand_default_bbt(struct mtd_info *mtd);
+
+#endif /* __LINUX_MTD_BBM_H */