]> git.ipfire.org Git - people/ms/u-boot.git/commitdiff
NAND: add support for reading ONFI page table
authorFlorian Fainelli <florian@openwrt.org>
Fri, 25 Feb 2011 00:01:34 +0000 (00:01 +0000)
committerScott Wood <scottwood@freescale.com>
Fri, 1 Apr 2011 19:49:08 +0000 (14:49 -0500)
This patch adds support for reading an ONFI page parameter from a NAND
device supporting it. If this is the case, struct nand_chip onfi_version
member contains the supported ONFI version, 0 otherwise.

This allows NAND drivers past nand_scan_ident to set the best timings for the
NAND chip.

Signed-off-by: Florian Fainelli <florian@openwrt.org>
Signed-off-by: Scott Wood <scottwood@freescale.com>
drivers/mtd/nand/nand_base.c
include/linux/mtd/nand.h

index b9bd3942f27bf815522598460ef3d843a695a875..fa286a8d8101dee5d50f57637172459c27984169 100644 (file)
@@ -2415,15 +2415,134 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
                chip->controller = &chip->hwcontrol;
 }
 
                chip->controller = &chip->hwcontrol;
 }
 
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
+{
+       int i;
+
+       while (len--) {
+               crc ^= *p++ << 8;
+               for (i = 0; i < 8; i++)
+                       crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
+       }
+
+       return crc;
+}
+
+/*
+ * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise
+ */
+static int nand_flash_detect_onfi(struct mtd_info *mtd,
+                                       struct nand_chip *chip,
+                                       int *busw)
+{
+       struct nand_onfi_params *p = &chip->onfi_params;
+       int i;
+       int val;
+
+       chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
+       if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
+               chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
+               return 0;
+
+       printk(KERN_INFO "ONFI flash detected\n");
+       chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
+       for (i = 0; i < 3; i++) {
+               chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
+               if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+                                               le16_to_cpu(p->crc)) {
+                       printk(KERN_INFO "ONFI param page %d valid\n", i);
+                       break;
+               }
+       }
+
+       if (i == 3)
+               return 0;
+
+       /* check version */
+       val = le16_to_cpu(p->revision);
+       if (val == 1 || val > (1 << 4)) {
+               printk(KERN_INFO "%s: unsupported ONFI "
+                                       "version: %d\n", __func__, val);
+               return 0;
+       }
+
+       if (val & (1 << 4))
+               chip->onfi_version = 22;
+       else if (val & (1 << 3))
+               chip->onfi_version = 21;
+       else if (val & (1 << 2))
+               chip->onfi_version = 20;
+       else
+               chip->onfi_version = 10;
+
+       if (!mtd->name)
+               mtd->name = p->model;
+
+       mtd->writesize = le32_to_cpu(p->byte_per_page);
+       mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
+       mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+       chip->chipsize = le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
+       *busw = 0;
+       if (le16_to_cpu(p->features) & 1)
+               *busw = NAND_BUSWIDTH_16;
+
+       return 1;
+}
+#else
+static inline int nand_flash_detect_onfi(struct mtd_info *mtd,
+                                       struct nand_chip *chip,
+                                       int *busw)
+{
+       return 0;
+}
+#endif
+
+static void nand_flash_detect_non_onfi(struct mtd_info *mtd,
+                                       struct nand_chip *chip,
+                                       const struct nand_flash_dev *type,
+                                       int *busw)
+{
+       /* Newer devices have all the information in additional id bytes */
+       if (!type->pagesize) {
+               int extid;
+               /* The 3rd id byte holds MLC / multichip data */
+               chip->cellinfo = chip->read_byte(mtd);
+               /* 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;
+
+       } 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;
+       }
+}
+
 /*
  * Get the flash and manufacturer id and lookup if the type is supported
  */
 static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                                                  struct nand_chip *chip,
 /*
  * Get the flash and manufacturer id and lookup if the type is supported
  */
 static const 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, int *dev_id,
                                                  const struct nand_flash_dev *type)
 {
                                                  const struct nand_flash_dev *type)
 {
-       int dev_id, maf_idx;
+       int ret, maf_idx;
        int tmp_id, tmp_manf;
 
        /* Select the device */
        int tmp_id, tmp_manf;
 
        /* Select the device */
@@ -2440,7 +2559,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 
        /* Read manufacturer and device IDs */
        *maf_id = chip->read_byte(mtd);
 
        /* Read manufacturer and device IDs */
        *maf_id = chip->read_byte(mtd);
-       dev_id = chip->read_byte(mtd);
+       *dev_id = chip->read_byte(mtd);
 
        /* Try again to make sure, as some systems the bus-hold or other
         * interface concerns can cause random data which looks like a
 
        /* Try again to make sure, as some systems the bus-hold or other
         * interface concerns can cause random data which looks like a
@@ -2455,10 +2574,10 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
        tmp_manf = chip->read_byte(mtd);
        tmp_id = chip->read_byte(mtd);
 
        tmp_manf = chip->read_byte(mtd);
        tmp_id = chip->read_byte(mtd);
 
-       if (tmp_manf != *maf_id || tmp_id != dev_id) {
+       if (tmp_manf != *maf_id || tmp_id != *dev_id) {
                printk(KERN_INFO "%s: second ID read did not match "
                       "%02x,%02x against %02x,%02x\n", __func__,
                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, tmp_manf, tmp_id);
                return ERR_PTR(-ENODEV);
        }
 
                return ERR_PTR(-ENODEV);
        }
 
@@ -2466,16 +2585,16 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                type = nand_flash_ids;
 
        for (; type->name != NULL; type++)
                type = nand_flash_ids;
 
        for (; type->name != NULL; type++)
-               if (dev_id == type->id)
+               if (*dev_id == type->id)
                        break;
 
        if (!type->name) {
                /* supress warning if there is no nand */
                if (*maf_id != 0x00 && *maf_id != 0xff &&
                        break;
 
        if (!type->name) {
                /* supress warning if there is no nand */
                if (*maf_id != 0x00 && *maf_id != 0xff &&
-                   dev_id  != 0x00 && dev_id  != 0xff)
+                   *dev_id  != 0x00 && *dev_id  != 0xff)
                        printk(KERN_INFO "%s: unknown NAND device: "
                                "Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
                        printk(KERN_INFO "%s: unknown NAND device: "
                                "Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
-                               __func__, *maf_id, dev_id);
+                               __func__, *maf_id, *dev_id);
                return ERR_PTR(-ENODEV);
        }
 
                return ERR_PTR(-ENODEV);
        }
 
@@ -2483,35 +2602,20 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                mtd->name = type->name;
 
        chip->chipsize = (uint64_t)type->chipsize << 20;
                mtd->name = type->name;
 
        chip->chipsize = (uint64_t)type->chipsize << 20;
+       chip->onfi_version = 0;
 
 
-       /* Newer devices have all the information in additional id bytes */
-       if (!type->pagesize) {
-               int extid;
-               /* The 3rd id byte holds MLC / multichip data */
-               chip->cellinfo = chip->read_byte(mtd);
-               /* 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;
+       ret = nand_flash_detect_onfi(mtd, chip, &busw);
+       if (!ret)
+               nand_flash_detect_non_onfi(mtd, chip, type, &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;
-       }
+       /* Get chip options, preserve non chip based options */
+       chip->options &= ~NAND_CHIPOPTIONS_MSK;
+       chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
+
+       /*
+        * Set chip as a default. Board drivers can override it, if necessary
+        */
+       chip->options |= NAND_NO_AUTOINCR;
 
        /* Try to identify manufacturer */
        for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
 
        /* Try to identify manufacturer */
        for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
@@ -2526,7 +2630,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
        if (busw != (chip->options & NAND_BUSWIDTH_16)) {
                printk(KERN_INFO "NAND device: Manufacturer ID:"
                       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
        if (busw != (chip->options & NAND_BUSWIDTH_16)) {
                printk(KERN_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);
+                      *dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
                printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
                       (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
                       busw ? 16 : 8);
                printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
                       (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
                       busw ? 16 : 8);
@@ -2549,15 +2653,6 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
        chip->badblockpos = mtd->writesize > 512 ?
                NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
 
        chip->badblockpos = mtd->writesize > 512 ?
                NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
 
-       /* Get chip options, preserve non chip based options */
-       chip->options &= ~NAND_CHIPOPTIONS_MSK;
-       chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
-
-       /*
-        * Set chip as a default. Board drivers can override it, if necessary
-        */
-       chip->options |= NAND_NO_AUTOINCR;
-
        /* 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 a not a samsung device. Do not clear the
         * options for chips which are not having an extended id.
         */
@@ -2575,7 +2670,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                chip->cmdfunc = nand_command_lp;
 
        MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:"
                chip->cmdfunc = nand_command_lp;
 
        MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:"
-                 " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,
+                 " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
                  nand_manuf_ids[maf_idx].name, type->name);
 
        return type;
                  nand_manuf_ids[maf_idx].name, type->name);
 
        return type;
@@ -2595,7 +2690,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 int nand_scan_ident(struct mtd_info *mtd, int maxchips,
                    const struct nand_flash_dev *table)
 {
 int nand_scan_ident(struct mtd_info *mtd, int maxchips,
                    const struct nand_flash_dev *table)
 {
-       int i, busw, nand_maf_id;
+       int i, busw, nand_maf_id, nand_dev_id;
        struct nand_chip *chip = mtd->priv;
        const struct nand_flash_dev *type;
 
        struct nand_chip *chip = mtd->priv;
        const struct nand_flash_dev *type;
 
@@ -2605,7 +2700,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
        nand_set_defaults(chip, busw);
 
        /* Read the flash type */
        nand_set_defaults(chip, busw);
 
        /* Read the flash type */
-       type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, table);
+       type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, &nand_dev_id, table);
 
        if (IS_ERR(type)) {
 #ifndef CONFIG_SYS_NAND_QUIET_TEST
 
        if (IS_ERR(type)) {
 #ifndef CONFIG_SYS_NAND_QUIET_TEST
@@ -2624,7 +2719,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
                chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
                /* Read manufacturer and device IDs */
                if (nand_maf_id != chip->read_byte(mtd) ||
                chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
                /* Read manufacturer and device IDs */
                if (nand_maf_id != chip->read_byte(mtd) ||
-                   type->id != chip->read_byte(mtd))
+                   nand_dev_id != chip->read_byte(mtd))
                        break;
        }
 #ifdef DEBUG
                        break;
        }
 #ifdef DEBUG
index d299929ab8058bfc81233117e97f1452da5fd2c4..987a2ec85d1a36e40559f300cf153077a9ef3fa9 100644 (file)
@@ -217,6 +217,71 @@ typedef enum {
 /* Keep gcc happy */
 struct nand_chip;
 
 /* Keep gcc happy */
 struct nand_chip;
 
+struct nand_onfi_params {
+       /* rev info and features block */
+       /* 'O' 'N' 'F' 'I'  */
+       u8 sig[4];
+       __le16 revision;
+       __le16 features;
+       __le16 opt_cmd;
+       u8 reserved[22];
+
+       /* manufacturer information block */
+       char manufacturer[12];
+       char model[20];
+       u8 jedec_id;
+       __le16 date_code;
+       u8 reserved2[13];
+
+       /* memory organization block */
+       __le32 byte_per_page;
+       __le16 spare_bytes_per_page;
+       __le32 data_bytes_per_ppage;
+       __le16 spare_bytes_per_ppage;
+       __le32 pages_per_block;
+       __le32 blocks_per_lun;
+       u8 lun_count;
+       u8 addr_cycles;
+       u8 bits_per_cell;
+       __le16 bb_per_lun;
+       __le16 block_endurance;
+       u8 guaranteed_good_blocks;
+       __le16 guaranteed_block_endurance;
+       u8 programs_per_page;
+       u8 ppage_attr;
+       u8 ecc_bits;
+       u8 interleaved_bits;
+       u8 interleaved_ops;
+       u8 reserved3[13];
+
+       /* electrical parameter block */
+       u8 io_pin_capacitance_max;
+       __le16 async_timing_mode;
+       __le16 program_cache_timing_mode;
+       __le16 t_prog;
+       __le16 t_bers;
+       __le16 t_r;
+       __le16 t_ccs;
+       __le16 src_sync_timing_mode;
+       __le16 src_ssync_features;
+       __le16 clk_pin_capacitance_typ;
+       __le16 io_pin_capacitance_typ;
+       __le16 input_pin_capacitance_typ;
+       u8 input_pin_capacitance_max;
+       u8 driver_strenght_support;
+       __le16 t_int_r;
+       __le16 t_ald;
+       u8 reserved4[7];
+
+       /* vendor */
+       u8 reserved5[90];
+
+       __le16 crc;
+} __attribute__((packed));
+
+#define ONFI_CRC_BASE  0x4F4E
+
+
 /**
  * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
  * @lock:               protection lock
 /**
  * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
  * @lock:               protection lock
@@ -406,6 +471,10 @@ struct nand_chip {
        int             subpagesize;
        uint8_t         cellinfo;
        int             badblockpos;
        int             subpagesize;
        uint8_t         cellinfo;
        int             badblockpos;
+       int             onfi_version;
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+       struct nand_onfi_params onfi_params;
+#endif
 
        int             state;
 
 
        int             state;