MLK-17153-1 nand: Update SPL MXS NAND mini driver
authorYe Li <ye.li@nxp.com>
Tue, 12 Dec 2017 05:49:53 +0000 (23:49 -0600)
committerYe Li <ye.li@nxp.com>
Wed, 13 Dec 2017 04:54:04 +0000 (22:54 -0600)
Update the mini driver to add support for getting ecc info from ONFI and
support read image data from page unaligned NAND address.

Signed-off-by: Ye Li <ye.li@nxp.com>
drivers/mtd/nand/mxs_nand_spl.c

index b6c9208..dd7aa07 100644 (file)
@@ -37,6 +37,12 @@ static void mxs_nand_command(struct mtd_info *mtd, unsigned int command,
        if (command == NAND_CMD_READ0) {
                chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_CLE);
                chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0);
+       } else if (command == NAND_CMD_RNDOUT) {
+               /* No ready / busy check necessary */
+               chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
+                              NAND_NCE | NAND_CLE);
+               chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+                              NAND_NCE);
        }
 
        /* wait for nand ready */
@@ -49,10 +55,96 @@ static void mxs_nand_command(struct mtd_info *mtd, unsigned int command,
        }
 }
 
+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;
+}
+
+/* Parse the Extended Parameter Page. */
+static int nand_flash_detect_ext_param_page(struct mtd_info *mtd,
+               struct nand_chip *chip, struct nand_onfi_params *p)
+{
+       struct onfi_ext_param_page *ep;
+       struct onfi_ext_section *s;
+       struct onfi_ext_ecc_info *ecc;
+       uint8_t *cursor;
+       int ret = -EINVAL;
+       int len;
+       int i;
+
+       len = le16_to_cpu(p->ext_param_page_length) * 16;
+       ep = malloc(len);
+       if (!ep) {
+               printf("can't malloc memory 0x%x\n", len);
+               return -ENOMEM;
+       }
+
+       /* Send our own NAND_CMD_PARAM. */
+       chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
+
+       /* Use the Change Read Column command to skip the ONFI param pages. */
+       chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+                       sizeof(*p) * p->num_of_param_pages , -1);
+
+       /* Read out the Extended Parameter Page. */
+       chip->read_buf(mtd, (uint8_t *)ep, len);
+       if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2)
+               != le16_to_cpu(ep->crc))) {
+               printf("fail in the CRC.\n");
+               goto ext_out;
+       }
+
+       /*
+        * Check the signature.
+        * Do not strictly follow the ONFI spec, maybe changed in future.
+        */
+       if (strncmp((char *)ep->sig, "EPPS", 4)) {
+               printf("The signature is invalid.\n");
+               goto ext_out;
+       }
+
+       /* find the ECC section. */
+       cursor = (uint8_t *)(ep + 1);
+       for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) {
+               s = ep->sections + i;
+               if (s->type == ONFI_SECTION_TYPE_2)
+                       break;
+               cursor += s->length * 16;
+       }
+       if (i == ONFI_EXT_SECTION_MAX) {
+               printf("We can not find the ECC section.\n");
+               goto ext_out;
+       }
+
+       /* get the info we want. */
+       ecc = (struct onfi_ext_ecc_info *)cursor;
+
+       if (!ecc->codeword_size) {
+               printf("Invalid codeword size\n");
+               goto ext_out;
+       }
+
+       chip->ecc_strength_ds = ecc->ecc_bits;
+       chip->ecc_step_ds = 1 << ecc->codeword_size;
+       ret = 0;
+
+ext_out:
+       free(ep);
+       return ret;
+}
+
+
 static int mxs_flash_ident(struct mtd_info *mtd)
 {
        register struct nand_chip *chip = mtd_to_nand(mtd);
-       int i;
+       int i, val;
        u8 mfg_id, dev_id;
        u8 id_data[8];
        struct nand_onfi_params *p = &chip->onfi_params;
@@ -101,6 +193,37 @@ static int mxs_flash_ident(struct mtd_info *mtd)
        chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
        chip->badblockbits = 8;
 
+       /* Check version */
+       val = le16_to_cpu(p->revision);
+       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 if (val & (1 << 1))
+               chip->onfi_version = 10;
+
+       if (!chip->onfi_version) {
+               printf("unsupported ONFI version: %d\n", val);
+               return 0;
+       }
+
+       if (p->ecc_bits != 0xff) {
+               chip->ecc_strength_ds = p->ecc_bits;
+               chip->ecc_step_ds = 512;
+       } else if (chip->onfi_version >= 21 &&
+               (onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) {
+
+               if (nand_flash_detect_ext_param_page(mtd, chip, p))
+                       printf("Failed to detect ONFI extended param page\n");
+       } else {
+               printf("Could not retrieve ONFI ECC requirements\n");
+       }
+
+       debug("ecc_strength_ds %u, ecc_step_ds %u\n", chip->ecc_strength_ds, chip->ecc_step_ds);
        debug("erasesize=%d (>>%d)\n", mtd->erasesize, chip->phys_erase_shift);
        debug("writesize=%d (>>%d)\n", mtd->writesize, chip->page_shift);
        debug("oobsize=%d\n", mtd->oobsize);
@@ -177,23 +300,40 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *buf)
        unsigned int page;
        unsigned int nand_page_per_block;
        unsigned int sz = 0;
+       uint8_t *page_buf = NULL;
+       uint32_t page_off;
 
        if (mxs_nand_init())
                return -ENODEV;
+
        chip = mtd_to_nand(mtd);
+
+       page_buf = malloc(mtd->writesize);
+       if (!page_buf)
+               return -ENOMEM;
+
        page = offs >> chip->page_shift;
+       page_off = offs & (mtd->writesize - 1);
        nand_page_per_block = mtd->erasesize / mtd->writesize;
 
        debug("%s offset:0x%08x len:%d page:%d\n", __func__, offs, size, page);
 
-       size = roundup(size, mtd->writesize);
-       while (sz < size) {
-               if (mxs_read_page_ecc(mtd, buf, page) < 0)
+       while (size) {
+               if (mxs_read_page_ecc(mtd, page_buf, page) < 0)
                        return -1;
-               sz += mtd->writesize;
+
+               if (size > (mtd->writesize - page_off))
+                       sz = (mtd->writesize - page_off);
+               else
+                       sz = size;
+
+               memcpy(buf, page_buf + page_off, sz);
+
                offs += mtd->writesize;
                page++;
-               buf += mtd->writesize;
+               buf += (mtd->writesize - page_off);
+               page_off = 0;
+               size -= sz;
 
                /*
                 * Check if we have crossed a block boundary, and if so
@@ -207,12 +347,16 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *buf)
                        while (is_badblock(mtd, offs, 1)) {
                                page = page + nand_page_per_block;
                                /* Check i we've reached the end of flash. */
-                               if (page >= mtd->size >> chip->page_shift)
+                               if (page >= mtd->size >> chip->page_shift) {
+                                       free(page_buf);
                                        return -ENOMEM;
+                               }
                        }
                }
        }
 
+       free(page_buf);
+
        return 0;
 }