]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - drivers/mtd/nand/nand_base.c
mtd: nand: refactor BB marker detection
[net-next-2.6.git] / drivers / mtd / nand / nand_base.c
index 51dfea1b3ce6fb52a1c64b4a879ff4c49dce7b44..bd697909db53a3ae4c76759e0a83298a68362954 100644 (file)
@@ -347,6 +347,9 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
        struct nand_chip *chip = mtd->priv;
        u16 bad;
 
+       if (chip->options & NAND_BBT_SCANLASTPAGE)
+               ofs += mtd->erasesize - mtd->writesize;
+
        page = (int)(ofs >> chip->page_shift) & chip->pagemask;
 
        if (getchip) {
@@ -364,14 +367,18 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
                bad = cpu_to_le16(chip->read_word(mtd));
                if (chip->badblockpos & 0x1)
                        bad >>= 8;
-               if ((bad & 0xFF) != 0xff)
-                       res = 1;
+               else
+                       bad &= 0xFF;
        } else {
                chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);
-               if (chip->read_byte(mtd) != 0xff)
-                       res = 1;
+               bad = chip->read_byte(mtd);
        }
 
+       if (likely(chip->badblockbits == 8))
+               res = bad != 0xFF;
+       else
+               res = hweight8(bad) < chip->badblockbits;
+
        if (getchip)
                nand_release_device(mtd);
 
@@ -392,6 +399,9 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
        uint8_t buf[2] = { 0, 0 };
        int block, ret;
 
+       if (chip->options & NAND_BBT_SCANLASTPAGE)
+               ofs += mtd->erasesize - mtd->writesize;
+
        /* Get block number */
        block = (int)(ofs >> chip->bbt_erase_shift);
        if (chip->bbt)
@@ -430,6 +440,11 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
 static int nand_check_wp(struct mtd_info *mtd)
 {
        struct nand_chip *chip = mtd->priv;
+
+       /* broken xD cards report WP despite being writable */
+       if (chip->options & NAND_BROKEN_XD)
+               return 0;
+
        /* Check the WP bit */
        chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
        return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
@@ -2762,11 +2777,11 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
  */
 static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                                                  struct nand_chip *chip,
-                                                 int busw, int *maf_id)
+                                                 int busw, int *maf_id,
+                                                 struct nand_flash_dev *type)
 {
-       struct nand_flash_dev *type = NULL;
        int i, dev_id, maf_idx;
-       int tmp_id, tmp_manf;
+       u8 id_data[8];
 
        /* Select the device */
        chip->select_chip(mtd, 0);
@@ -2792,27 +2807,26 @@ 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 */
+       /* Read entire ID string */
 
-       tmp_manf = chip->read_byte(mtd);
-       tmp_id = chip->read_byte(mtd);
+       for (i = 0; i < 8; i++)
+               id_data[i] = chip->read_byte(mtd);
 
-       if (tmp_manf != *maf_id || tmp_id != dev_id) {
+       if (id_data[0] != *maf_id || id_data[1] != dev_id) {
                printk(KERN_INFO "%s: second ID read did not match "
                       "%02x,%02x against %02x,%02x\n", __func__,
-                      *maf_id, dev_id, tmp_manf, tmp_id);
+                      *maf_id, dev_id, id_data[0], id_data[1]);
                return ERR_PTR(-ENODEV);
        }
 
-       /* Lookup the flash id */
-       for (i = 0; nand_flash_ids[i].name != NULL; i++) {
-               if (dev_id == nand_flash_ids[i].id) {
-                       type =  &nand_flash_ids[i];
-                       break;
-               }
-       }
-
        if (!type)
+               type = nand_flash_ids;
+
+       for (; type->name != NULL; type++)
+               if (dev_id == type->id)
+                        break;
+
+       if (!type->name)
                return ERR_PTR(-ENODEV);
 
        if (!mtd->name)
@@ -2824,21 +2838,45 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
        if (!type->pagesize) {
                int extid;
                /* The 3rd id byte holds MLC / multichip data */
-               chip->cellinfo = chip->read_byte(mtd);
+               chip->cellinfo = id_data[2];
                /* The 4th id byte is the important one */
-               extid = chip->read_byte(mtd);
-               /* Calc pagesize */
-               mtd->writesize = 1024 << (extid & 0x3);
-               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;
+               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 K9GAG08U0D (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 &&
+                               id_data[5] != 0x00) {
+                       /* Calc pagesize */
+                       mtd->writesize = 2048 << (extid & 0x03);
+                       extid >>= 2;
+                       /* Calc oobsize */
+                       mtd->oobsize = (extid & 0x03) == 0x01 ? 128 : 218;
+                       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;
+               }
        } else {
                /*
                 * Old devices have chip data hardcoded in the device id table
@@ -2882,8 +2920,14 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 32 - 1;
 
        /* Set the bad block position */
-       chip->badblockpos = mtd->writesize > 512 ?
-               NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
+       if (!(busw & NAND_BUSWIDTH_16) && (*maf_id == NAND_MFR_STMICRO ||
+                               (*maf_id == NAND_MFR_SAMSUNG &&
+                                mtd->writesize == 512) ||
+                               *maf_id == NAND_MFR_AMD))
+               chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
+       else
+               chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
+
 
        /* Get chip options, preserve non chip based options */
        chip->options &= ~NAND_CHIPOPTIONS_MSK;
@@ -2900,6 +2944,26 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
        if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
                chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
 
+       /*
+        * 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, and AMD/Spansion. 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->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_AMD)) ||
+                       (mtd->writesize == 2048 &&
+                        *maf_id == NAND_MFR_MICRON))
+               chip->options |= NAND_BBT_SCAN2NDPAGE;
+
+
        /* Check for AND chips with 4 page planes */
        if (chip->options & NAND_4PAGE_ARRAY)
                chip->erase_cmd = multi_erase_cmd;
@@ -2921,13 +2985,15 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
  * nand_scan_ident - [NAND Interface] Scan for the NAND device
  * @mtd:            MTD device structure
  * @maxchips:       Number of chips to scan for
+ * @table:          Alternative NAND ID table
  *
  * This is the first phase of the normal nand_scan() function. It
  * reads the flash ID and sets up MTD fields accordingly.
  *
  * The mtd->owner field must be set to the module of the caller.
  */
-int nand_scan_ident(struct mtd_info *mtd, int maxchips)
+int nand_scan_ident(struct mtd_info *mtd, int maxchips,
+                   struct nand_flash_dev *table)
 {
        int i, busw, nand_maf_id;
        struct nand_chip *chip = mtd->priv;
@@ -2939,7 +3005,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
        nand_set_defaults(chip, busw);
 
        /* Read the flash type */
-       type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);
+       type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, table);
 
        if (IS_ERR(type)) {
                if (!(chip->options & NAND_SCAN_SILENT_NODEV))
@@ -3169,7 +3235,8 @@ int nand_scan_tail(struct mtd_info *mtd)
 
        /* Fill in remaining MTD driver data */
        mtd->type = MTD_NANDFLASH;
-       mtd->flags = MTD_CAP_NANDFLASH;
+       mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
+                                               MTD_CAP_NANDFLASH;
        mtd->erase = nand_erase;
        mtd->point = NULL;
        mtd->unpoint = NULL;
@@ -3230,7 +3297,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
                BUG();
        }
 
-       ret = nand_scan_ident(mtd, maxchips);
+       ret = nand_scan_ident(mtd, maxchips, NULL);
        if (!ret)
                ret = nand_scan_tail(mtd);
        return ret;