Signed-off-by: Eric Bénard <e...@eukrea.com>
---
this patch sync flash detection functions with linux 3.9's code
 drivers/mtd/nand/nand_base.c | 377 ++++++++++++++++++++++++++++++-------------
 include/linux/mtd/bbm.h      |   2 +
 2 files changed, 265 insertions(+), 114 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index e8103cf..9bfddf9 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1077,7 +1077,6 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, 
struct nand_chip *chip,
                chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
                return 0;
 
-       pr_info("ONFI flash detected ... ");
        chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
        for (i = 0; i < 3; i++) {
                chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
@@ -1128,10 +1127,252 @@ static int nand_flash_detect_onfi(struct mtd_info 
*mtd, struct nand_chip *chip,
        chip->options &= ~NAND_CHIPOPTIONS_MSK;
        chip->options |= NAND_NO_READRDY & NAND_CHIPOPTIONS_MSK;
 
+       pr_info("ONFI flash detected ... ");
+       return 1;
+}
+
+/*
+ * nand_id_has_period - Check if an ID string has a given wraparound period
+ * @id_data: the ID string
+ * @arrlen: the length of the @id_data array
+ * @period: the period of repitition
+ *
+ * Check if an ID string is repeated within a given sequence of bytes at
+ * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a
+ * period of 3). This is a helper function for nand_id_len(). Returns non-zero
+ * if the repetition has a period of @period; otherwise, returns zero.
+ */
+static int nand_id_has_period(u8 *id_data, int arrlen, int period)
+{
+       int i, j;
+       for (i = 0; i < period; i++)
+               for (j = i + period; j < arrlen; j += period)
+                       if (id_data[i] != id_data[j])
+                               return 0;
        return 1;
 }
 
 /*
+ * nand_id_len - Get the length of an ID string returned by CMD_READID
+ * @id_data: the ID string
+ * @arrlen: the length of the @id_data array
+
+ * Returns the length of the ID string, according to known wraparound/trailing
+ * zero patterns. If no pattern exists, returns the length of the array.
+ */
+static int nand_id_len(u8 *id_data, int arrlen)
+{
+       int last_nonzero, period;
+
+       /* Find last non-zero byte */
+       for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--)
+               if (id_data[last_nonzero])
+                       break;
+
+       /* All zeros */
+       if (last_nonzero < 0)
+               return 0;
+
+       /* Calculate wraparound period */
+       for (period = 1; period < arrlen; period++)
+               if (nand_id_has_period(id_data, arrlen, period))
+                       break;
+
+       /* There's a repeated pattern */
+       if (period < arrlen)
+               return period;
+
+       /* There are trailing zeros */
+       if (last_nonzero < arrlen - 1)
+               return last_nonzero + 1;
+
+       /* No pattern detected */
+       return arrlen;
+}
+
+/*
+ * Many new NAND share similar device ID codes, which represent the size of the
+ * chip. The rest of the parameters must be decoded according to generic or
+ * manufacturer-specific "extended ID" decoding patterns.
+ */
+static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
+                               u8 id_data[8], int *busw)
+{
+       int extid, id_len;
+       /* The 3rd id byte holds MLC / multichip data */
+       chip->cellinfo = id_data[2];
+       /* The 4th id byte is the important one */
+       extid = id_data[3];
+
+       id_len = nand_id_len(id_data, 8);
+
+       /*
+        * Field definitions are in the following datasheets:
+        * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
+        * New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44)
+        * Hynix MLC   (6 byte ID): Hynix H27UBG8T2B (p.22)
+        *
+        * Check for ID length, non-zero 6th byte, cell type, and Hynix/Samsung
+        * ID to decide what to do.
+        */
+       if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG &&
+                       (chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
+                       id_data[5] != 0x00) {
+               /* Calc pagesize */
+               mtd->writesize = 2048 << (extid & 0x03);
+               extid >>= 2;
+               /* Calc oobsize */
+               switch (((extid >> 2) & 0x04) | (extid & 0x03)) {
+               case 1:
+                       mtd->oobsize = 128;
+                       break;
+               case 2:
+                       mtd->oobsize = 218;
+                       break;
+               case 3:
+                       mtd->oobsize = 400;
+                       break;
+               case 4:
+                       mtd->oobsize = 436;
+                       break;
+               case 5:
+                       mtd->oobsize = 512;
+                       break;
+               case 6:
+               default: /* Other cases are "reserved" (unknown) */
+                       mtd->oobsize = 640;
+                       break;
+               }
+               extid >>= 2;
+               /* Calc blocksize */
+               mtd->erasesize = (128 * 1024) <<
+                       (((extid >> 1) & 0x04) | (extid & 0x03));
+               *busw = 0;
+       } else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX &&
+                       (chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
+               unsigned int tmp;
+
+               /* Calc pagesize */
+               mtd->writesize = 2048 << (extid & 0x03);
+               extid >>= 2;
+               /* Calc oobsize */
+               switch (((extid >> 2) & 0x04) | (extid & 0x03)) {
+               case 0:
+                       mtd->oobsize = 128;
+                       break;
+               case 1:
+                       mtd->oobsize = 224;
+                       break;
+               case 2:
+                       mtd->oobsize = 448;
+                       break;
+               case 3:
+                       mtd->oobsize = 64;
+                       break;
+               case 4:
+                       mtd->oobsize = 32;
+                       break;
+               case 5:
+                       mtd->oobsize = 16;
+                       break;
+               default:
+                       mtd->oobsize = 640;
+                       break;
+               }
+               extid >>= 2;
+               /* Calc blocksize */
+               tmp = ((extid >> 1) & 0x04) | (extid & 0x03);
+               if (tmp < 0x03)
+                       mtd->erasesize = (128 * 1024) << tmp;
+               else if (tmp == 0x03)
+                       mtd->erasesize = 768 * 1024;
+               else
+                       mtd->erasesize = (64 * 1024) << tmp;
+               *busw = 0;
+       } else {
+               /* Calc pagesize */
+               mtd->writesize = 1024 << (extid & 0x03);
+               extid >>= 2;
+               /* Calc oobsize */
+               mtd->oobsize = (8 << (extid & 0x01)) *
+                       (mtd->writesize >> 9);
+               extid >>= 2;
+               /* Calc blocksize. Blocksize is multiples of 64KiB */
+               mtd->erasesize = (64 * 1024) << (extid & 0x03);
+               extid >>= 2;
+               /* Get buswidth information */
+               *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+       }
+}
+
+/*
+ * Old devices have chip data hardcoded in the device ID table. nand_decode_id
+ * decodes a matching ID table entry and assigns the MTD size parameters for
+ * the chip.
+ */
+static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip,
+                               struct nand_flash_dev *type, u8 id_data[8],
+                               int *busw)
+{
+       int maf_id = id_data[0];
+
+       mtd->erasesize = type->erasesize;
+       mtd->writesize = type->pagesize;
+       mtd->oobsize = mtd->writesize / 32;
+       *busw = type->options & NAND_BUSWIDTH_16;
+
+       /*
+        * Check for Spansion/AMD ID + repeating 5th, 6th byte since
+        * some Spansion chips have erasesize that conflicts with size
+        * listed in nand_ids table.
+        * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
+        */
+       if (maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00
+                       && id_data[6] == 0x00 && id_data[7] == 0x00
+                       && mtd->writesize == 512) {
+               mtd->erasesize = 128 * 1024;
+               mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
+       }
+}
+
+/*
+ * Set the bad block marker/indicator (BBM/BBI) patterns according to some
+ * heuristic patterns using various detected parameters (e.g., manufacturer,
+ * page size, cell-type information).
+ */
+static void nand_decode_bbm_options(struct mtd_info *mtd,
+                                   struct nand_chip *chip, u8 id_data[8])
+{
+       int maf_id = id_data[0];
+
+       /* Set the bad block position */
+       if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16))
+               chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
+       else
+               chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
+
+       /*
+        * Bad block marker is stored in the last page of each block on Samsung
+        * and Hynix MLC devices; stored in first two pages of each block on
+        * Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba,
+        * AMD/Spansion, and Macronix.  All others scan only the first page.
+        */
+       if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
+                       (maf_id == NAND_MFR_SAMSUNG ||
+                        maf_id == NAND_MFR_HYNIX))
+               chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
+       else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
+                               (maf_id == NAND_MFR_SAMSUNG ||
+                                maf_id == NAND_MFR_HYNIX ||
+                                maf_id == NAND_MFR_TOSHIBA ||
+                                maf_id == NAND_MFR_AMD ||
+                                maf_id == NAND_MFR_MACRONIX)) ||
+                       (mtd->writesize == 2048 &&
+                        maf_id == NAND_MFR_MICRON))
+               chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
+}
+
+/*
  * Get the flash and manufacturer id and lookup if the type is supported
  */
 static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
@@ -1140,7 +1381,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct 
mtd_info *mtd,
 {
        struct nand_flash_dev *type = NULL;
        int i, dev_id, maf_idx;
-       int id_data[8];
+       u8 id_data[8];
        int ret;
 
        /* Select the device */
@@ -1167,10 +1408,9 @@ static struct nand_flash_dev *nand_get_flash_type(struct 
mtd_info *mtd,
 
        chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
 
-       /* Read manufacturer and device IDs */
-
-       id_data[0] = chip->read_byte(mtd);
-       id_data[1] = chip->read_byte(mtd);
+       /* Read entire ID string */
+       for (i = 0; i < 8; i++)
+               id_data[i] = chip->read_byte(mtd);
 
        if (id_data[0] != *maf_id || id_data[1] != dev_id) {
                pr_err("%s: second ID read did not match "
@@ -1190,20 +1430,11 @@ static struct nand_flash_dev 
*nand_get_flash_type(struct mtd_info *mtd,
        if (!type->name || !type->pagesize) {
                /* Check is chip is ONFI compliant */
                ret = nand_flash_detect_onfi(mtd, chip, &busw);
-               if (ret)
                        goto ident_done;
-               else {
-                       pr_err("NAND type unknown: %02x,%02x\n", *maf_id, 
dev_id);
-                       return ERR_PTR(-ENODEV);
-               }
        }
 
-       chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
-
-       /* Read entire ID string */
-
-       for (i = 0; i < 8; i++)
-               id_data[i] = chip->read_byte(mtd);
+       if (!type->name)
+               return ERR_PTR(-ENODEV);
 
        if (!mtd->name)
                mtd->name = type->name;
@@ -1211,102 +1442,20 @@ static struct nand_flash_dev 
*nand_get_flash_type(struct mtd_info *mtd,
        chip->chipsize = type->chipsize << 20;
 
        if (!type->pagesize) {
-               int extid;
-               /* The 3rd id byte holds MLC / multichip data */
-               chip->cellinfo = id_data[2];
-               /* The 4th id byte is the important one */
-               extid = id_data[3];
-
-               /*
-                * Field definitions are in the following datasheets:
-                * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
-                * New style   (6 byte ID): Samsung K9GBG08U0M (p.40)
-                *
-                * Check for wraparound + Samsung ID + nonzero 6th byte
-                * to decide what to do.
-                */
-               if (id_data[0] == id_data[6] && id_data[1] == id_data[7] &&
-                               id_data[0] == NAND_MFR_SAMSUNG &&
-                               (chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
-                               id_data[5] != 0x00) {
-                       /* Calc pagesize */
-                       mtd->writesize = 2048 << (extid & 0x03);
-                       extid >>= 2;
-                       /* Calc oobsize */
-                       switch (extid & 0x03) {
-                       case 1:
-                               mtd->oobsize = 128;
-                               break;
-                       case 2:
-                               mtd->oobsize = 218;
-                               break;
-                       case 3:
-                               mtd->oobsize = 400;
-                               break;
-                       default:
-                               mtd->oobsize = 436;
-                               break;
-                       }
-                       extid >>= 2;
-                       /* Calc blocksize */
-                       mtd->erasesize = (128 * 1024) <<
-                               (((extid >> 1) & 0x04) | (extid & 0x03));
-                       busw = 0;
-               } else {
-                       /* Calc pagesize */
-                       mtd->writesize = 1024 << (extid & 0x03);
-                       extid >>= 2;
-                       /* Calc oobsize */
-                       mtd->oobsize = (8 << (extid & 0x01)) *
-                               (mtd->writesize >> 9);
-                       extid >>= 2;
-                       /* Calc blocksize. Blocksize is multiples of 64KiB */
-                       mtd->erasesize = (64 * 1024) << (extid & 0x03);
-                       extid >>= 2;
-                       /* Get buswidth information */
-                       busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
-               }
-
-
+               /* Decode parameters from extended ID */
+               nand_decode_ext_id(mtd, chip, id_data, &busw);
        } else {
-               /*
-                * Old devices have chip data hardcoded in the device id table
-                */
-               mtd->erasesize = type->erasesize;
-               mtd->writesize = type->pagesize;
-               mtd->oobsize = mtd->writesize / 32;
-               busw = type->options & NAND_BUSWIDTH_16;
-
-               /*
-                * Check for Spansion/AMD ID + repeating 5th, 6th byte since
-                * some Spansion chips have erasesize that conflicts with size
-                * listed in nand_ids table
-                * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
-                */
-               if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 &&
-                               id_data[5] == 0x00 && id_data[6] == 0x00 &&
-                               id_data[7] == 0x00 && mtd->writesize == 512) {
-                       mtd->erasesize = 128 * 1024;
-                       mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
-               }
+               nand_decode_id(mtd, chip, type, id_data, &busw);
        }
+       /* Get chip options */
+       chip->options |= type->options;
 
-       /* Try to identify manufacturer */
-       for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
-               if (nand_manuf_ids[maf_idx].id == *maf_id)
-                       break;
-       }
-
-       /* Get chip options, preserve non chip based options */
-       chip->options &= ~NAND_CHIPOPTIONS_MSK;
-       chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
-
-       /* Check if chip is a not a samsung device. Do not clear the
-        * options for chips which are not having an extended id.
+       /*
+        * Check if chip is not a Samsung device. Do not clear the
+        * options for chips which do not have an extended id.
         */
        if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
                chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
-
 ident_done:
        /*
         * Set chip as a default. Board drivers can override it, if necessary
@@ -1324,13 +1473,11 @@ ident_done:
                nand_set_defaults(chip, busw);
                if (chip->set_buswidth)
                        chip->set_buswidth(mtd, chip, busw);
-       }
-
-       /*
-        * Check, if buswidth is correct. Hardware drivers should set
-        * chip correct !
-        */
-       if (busw != (chip->options & NAND_BUSWIDTH_16)) {
+       } else  if (busw != (chip->options & NAND_BUSWIDTH_16)) {
+               /*
+                * Check, if buswidth is correct. Hardware drivers should set
+                * chip correct !
+                */
                pr_info("NAND device: Manufacturer ID:"
                        " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
                        dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
@@ -1340,6 +1487,8 @@ ident_done:
                return ERR_PTR(-EINVAL);
        }
 
+       nand_decode_bbm_options(mtd, chip, id_data);
+
        /* Calculate the address shift from the page size */
        chip->page_shift = ffs(mtd->writesize) - 1;
        /* Convert chipsize to number of pages per chip -1. */
diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h
index d546f40..50bc4d9 100644
--- a/include/linux/mtd/bbm.h
+++ b/include/linux/mtd/bbm.h
@@ -79,6 +79,8 @@ struct nand_bbt_descr {
 #define NAND_BBT_SAVECONTENT   0x00002000
 /* Search good / bad pattern on the first and the second page */
 #define NAND_BBT_SCAN2NDPAGE   0x00004000
+/* Search good / bad pattern on the last page of the eraseblock */
+#define NAND_BBT_SCANLASTPAGE  0x00010000
 
 /*
  * Use a flash based bad block table. By default, OOB identifier is saved in
-- 
1.8.1.4


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

Reply via email to