]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
Xilinx: ARM: Added ClearNAND ECC support
authorNaveen Mamindlapalli <naveenm@xhd-epdswlin32re1.(none)>
Fri, 3 Jun 2011 13:11:05 +0000 (18:41 +0530)
committerBrian Hill <brian.hill@xilinx.com>
Thu, 23 Jun 2011 19:22:51 +0000 (13:22 -0600)
board/xilinx/dfe/xilinx_nandpss.c
drivers/mtd/nand/nand_base.c
include/linux/mtd/nand.h
include/linux/string.h
lib/string.c

index c085691cb794ed5ff1a3a8223d38b67be5f27ddc..5d349abb38bcc21111d7d2321ec57f68d9611bc0 100644 (file)
@@ -204,6 +204,7 @@ static struct xnandpss_command_format xnandpss_commands[] __devinitdata = {
        {NAND_CMD_RNDIN, NAND_CMD_NONE, 2, NAND_CMD_NONE},
        {NAND_CMD_ERASE1, NAND_CMD_ERASE2, 3, XNANDPSS_CMD_PHASE},
        {NAND_CMD_RESET, NAND_CMD_NONE, 0, NAND_CMD_NONE},
+       {NAND_CMD_PARAM, NAND_CMD_NONE, 1, NAND_CMD_NONE},
        {NAND_CMD_GET_FEATURES, NAND_CMD_NONE, 1, NAND_CMD_NONE},
        {NAND_CMD_SET_FEATURES, NAND_CMD_NONE, 1, NAND_CMD_NONE},
        {NAND_CMD_NONE, NAND_CMD_NONE, 0, 0},
@@ -220,10 +221,10 @@ static struct xnandpss_command_format xnandpss_commands[] __devinitdata = {
 /* Define default oob placement schemes for large and small page devices */
 static struct nand_ecclayout nand_oob_16 = {
        .eccbytes = 3,
-       .eccpos = {0, 1, 2},
+       .eccpos = {13, 14, 15},
        .oobfree = {
-               {.offset = 8,
-                . length = 8} }
+               {.offset = 0,
+                . length = 12} }
 };
 
 static struct nand_ecclayout nand_oob_64 = {
@@ -1006,6 +1007,7 @@ int board_nand_init(struct nand_chip *nand_chip)
        u8 set_feature[4] = {0x08, 0x00, 0x00, 0x00};
        unsigned long ecc_cfg;
        int ondie_ecc_enabled = 0;
+       int ez_nand_supported = 0;
 
 #ifdef LINUX_ONLY_NOT_UBOOT
        xnand = kzalloc(sizeof(struct xnandpss_info), GFP_KERNEL);
@@ -1143,9 +1145,13 @@ int board_nand_init(struct nand_chip *nand_chip)
 
                if (get_feature[0] & 0x08)
                        ondie_ecc_enabled = 1;
+       } else if ((nand_chip->onfi_version == 23) &&
+                               (nand_chip->onfi_params.features & (1 << 9))) {
+               printk(KERN_INFO "\nClear NAND flash detected\n");
+               ez_nand_supported = 1;
        }
 
-       if (ondie_ecc_enabled) {
+       if (ondie_ecc_enabled || ez_nand_supported) {
                /* bypass the controller ECC block */
                ecc_cfg = xnandpss_read32(xnand->smc_regs +
                        XSMCPSS_ECC_MEMCFG_OFFSET(XSMCPSS_ECC_IF1_OFFSET));
@@ -1166,11 +1172,13 @@ int board_nand_init(struct nand_chip *nand_chip)
                nand_chip->ecc.size = mtd->writesize;
                nand_chip->ecc.bytes = 0;
 
-               nand_chip->ecc.layout = &ondie_nand_oob_64;
-
-               /* Use the BBT pattern descriptors */
-               nand_chip->bbt_td = &bbt_main_descr;
-               nand_chip->bbt_md = &bbt_mirror_descr;
+               /* On-Die ECC spare bytes offset 8 is used for ECC codes */
+               if (ondie_ecc_enabled) {
+                       nand_chip->ecc.layout = &ondie_nand_oob_64;
+                       /* Use the BBT pattern descriptors */
+                       nand_chip->bbt_td = &bbt_main_descr;
+                       nand_chip->bbt_md = &bbt_mirror_descr;
+               }
        } else {
                /* Hardware ECC generates 3 bytes ECC code for each 512 bytes */
                nand_chip->ecc.mode = NAND_ECC_HW;
index 96bbbcafcfa44a061c89aeee4582687824061f48..b1687b3ab12c9ce980f9919cf03e287011eae8a1 100644 (file)
@@ -2602,6 +2602,104 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
 
 }
 
+/*
+ * sanitize ONFI strings so we can safely print them
+ */
+static void sanitize_string(uint8_t *s, size_t len)
+{
+       ssize_t i;
+
+       /* null terminate */
+       s[len - 1] = 0;
+
+       /* remove non printable chars */
+       for (i = 0; i < len - 1; i++) {
+               if (s[i] < ' ' || s[i] > 127)
+                       s[i] = '?';
+       }
+
+       /* remove trailing spaces */
+       strim(s);
+}
+
+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;
+
+       /* try ONFI for unknow chip or LP */
+       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;
+
+       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)) {
+                       break;
+               }
+       }
+
+       if (i == 3) {
+               return 0;
+       }
+
+       /* check version */
+       val = le16_to_cpu(p->revision);
+       if (val == 1 || val > (1 << 5)) {
+               printk(KERN_INFO "%s: unsupported ONFI version: %d\n",
+                                                               __func__, val);
+               return 0;
+       }
+
+       if (val & (1 << 5))
+               chip->onfi_version = 23;
+       else 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;
+
+       sanitize_string(p->manufacturer, sizeof(p->manufacturer));
+       sanitize_string(p->model, sizeof(p->model));
+       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;
+
+       chip->options &= ~NAND_CHIPOPTIONS_MSK;
+       chip->options |= (NAND_NO_READRDY |
+                       NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK;
+
+       return 1;
+}
+
 /*
  * Get the flash and manufacturer id and lookup if the type is supported
  */
@@ -2612,6 +2710,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 tmp_id, tmp_manf;
+       int ret;
 
        /* Select the device */
        chip->select_chip(mtd, 0);
@@ -2649,22 +2748,24 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                return ERR_PTR(-ENODEV);
        }
 
+       type = nand_flash_ids;
        /* 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];
+       for (; type->name != NULL; type++)
+               if (dev_id == type->id)
                        break;
-               }
-       }
 
-       if (!type) {
-               /* supress warning if there is no nand */
-               if (*maf_id != 0x00 && *maf_id != 0xff &&
-                   dev_id  != 0x00 && dev_id  != 0xff)
+       chip->onfi_version = 0;
+       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 {
                        printk(KERN_INFO "%s: unknown NAND device: "
                                "Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
                                __func__, *maf_id, dev_id);
-               return ERR_PTR(-ENODEV);
+                       return ERR_PTR(-ENODEV);
+               }
        }
 
        if (!mtd->name)
@@ -2701,6 +2802,17 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                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;
+
+       /* Check if chip is a not a samsung device. Do not clear the
+        * options for chips which are not having an extended id.
+        */
+       if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
+               chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
+
+ident_done:
        /* 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)
@@ -2737,21 +2849,11 @@ static 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;
 
-       /* 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.
-        */
-       if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
-               chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
-
        /* Check for AND chips with 4 page planes */
        if (chip->options & NAND_4PAGE_ARRAY)
                chip->erase_cmd = multi_erase_cmd;
index 94ad0c0e36aba8d4e7c3d1f283ece0c429cd0c04..0295f87de889a20dd2be864c95122933ffee247f 100644 (file)
@@ -85,6 +85,7 @@ extern void nand_wait_ready(struct mtd_info *mtd);
 #define NAND_CMD_RNDIN         0x85
 #define NAND_CMD_READID                0x90
 #define NAND_CMD_ERASE2                0xd0
+#define NAND_CMD_PARAM         0xec
 #define NAND_CMD_RESET         0xff
 
 /* Extended commands for large page devices */
@@ -214,6 +215,70 @@ typedef enum {
 /* 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
@@ -404,6 +469,9 @@ struct nand_chip {
        uint8_t         cellinfo;
        int             badblockpos;
 
+       int onfi_version;
+       struct nand_onfi_params onfi_params;
+
        int             state;
 
        uint8_t         *oob_poi;
index 62390399b0b1b7e160001abd437fd2020bfee808..249f61362a40991ebf374c1cb66dda606bf99dc4 100644 (file)
@@ -50,6 +50,7 @@ extern char * strrchr(const char *,int);
 #ifndef __HAVE_ARCH_STRSTR
 extern char * strstr(const char *,const char *);
 #endif
+extern char *strim(char *s);
 #ifndef __HAVE_ARCH_STRLEN
 extern __kernel_size_t strlen(const char *);
 #endif
index b375b8124a9a1879a831d79b8f956bf8ae8a9537..78df4cc4f8084d8f88e85c40af649bf563a84a78 100644 (file)
@@ -214,6 +214,45 @@ char * strrchr(const char * s, int c)
 }
 #endif
 
+/**
+ * skip_spaces - Removes leading whitespace from @str.
+ * @str: The string to be stripped.
+ *
+ * Returns a pointer to the first non-whitespace character in @str.
+ */
+char *skip_spaces(const char *str)
+{
+       while (isspace(*str))
+               ++str;
+       return (char *)str;
+}
+
+/**
+ * strim - Removes leading and trailing whitespace from @s.
+ * @s: The string to be stripped.
+ *
+ * Note that the first trailing whitespace is replaced with a %NUL-terminator
+ * in the given string @s. Returns a pointer to the first non-whitespace
+ * character in @s.
+ */
+char *strim(char *s)
+{
+       size_t size;
+       char *end;
+
+       s = skip_spaces(s);
+       size = strlen(s);
+       if (!size)
+               return s;
+
+       end = s + size - 1;
+       while (end >= s && isspace(*end))
+               end--;
+       *(end + 1) = '\0';
+
+       return s;
+}
+
 #ifndef __HAVE_ARCH_STRLEN
 /**
  * strlen - Find the length of a string