ARM: at91: atmel_nand: add code to check the ONFI parameter ECC requirement
authorWu, Josh <Josh.wu@atmel.com>
Thu, 4 Jul 2013 07:36:23 +0000 (15:36 +0800)
committerAndreas Bießmann <andreas.devel@googlemail.com>
Thu, 22 Aug 2013 14:50:52 +0000 (16:50 +0200)
1. if CONFIG_SYS_NAND_ONFI_DETECTION is defined, driver will check NAND flash's
   ecc minimum requirement in ONFI parameter.

  a) if CONFIG_PMECC_CAP, CONFIG_PMECC_SECTOR_SIZE are defined. then use it.
     Driver will display a WARNING if the values are different from ONFI
     parameters.

  b) if CONFIG_PMECC_CAP, CONFIG_PMECC_SECTOR_SIZE are not defined, then use
      the value from ONFI parameters.
      * If ONFI ECC parameters are in ONFI extended parameter page, since we
        are not support it, so assume the minimum ecc requirement is 2 bits
        in 512 bytes.
      * For non-ONFI support nand flash, also assume the minimum ecc
        requirement is 2 bits in 512 bytes.

2. if CONFIG_SYS_NAND_ONFI_DETECTION is not defined, just use CONFIG_PMECC_CAP
   and CONFIG_PMECC_SECTOR_SIZE.

Signed-off-by: Josh Wu <josh.wu@atmel.com>
Acked-by: Scott Wood <scottwood@freescale.com>
Signed-off-by: Andreas Bießmann <andreas.devel@googlemail.com>
drivers/mtd/nand/atmel_nand.c

index 15fb4fb..96aca00 100644 (file)
@@ -665,6 +665,99 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd)
        pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE);
 }
 
        pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE);
 }
 
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+/*
+ * get_onfi_ecc_param - Get ECC requirement from ONFI parameters
+ * @ecc_bits: store the ONFI ECC correct bits capbility
+ * @sector_size: in how many bytes that ONFI require to correct @ecc_bits
+ *
+ * Returns -1 if ONFI parameters is not supported. In this case @ecc_bits,
+ * @sector_size are initialize to 0.
+ * Return 0 if success to get the ECC requirement.
+ */
+static int get_onfi_ecc_param(struct nand_chip *chip,
+               int *ecc_bits, int *sector_size)
+{
+       *ecc_bits = *sector_size = 0;
+
+       if (chip->onfi_params.ecc_bits == 0xff)
+               /* TODO: the sector_size and ecc_bits need to be find in
+                * extended ecc parameter, currently we don't support it.
+                */
+               return -1;
+
+       *ecc_bits = chip->onfi_params.ecc_bits;
+
+       /* The default sector size (ecc codeword size) is 512 */
+       *sector_size = 512;
+
+       return 0;
+}
+
+/*
+ * pmecc_choose_ecc - Get ecc requirement from ONFI parameters. If
+ *                    pmecc_corr_cap or pmecc_sector_size is 0, then set it as
+ *                    ONFI ECC parameters.
+ * @host: point to an atmel_nand_host structure.
+ *        if host->pmecc_corr_cap is 0 then set it as the ONFI ecc_bits.
+ *        if host->pmecc_sector_size is 0 then set it as the ONFI sector_size.
+ * @chip: point to an nand_chip structure.
+ * @cap: store the ONFI ECC correct bits capbility
+ * @sector_size: in how many bytes that ONFI require to correct @ecc_bits
+ *
+ * Return 0 if success. otherwise return the error code.
+ */
+static int pmecc_choose_ecc(struct atmel_nand_host *host,
+               struct nand_chip *chip,
+               int *cap, int *sector_size)
+{
+       /* Get ECC requirement from ONFI parameters */
+       *cap = *sector_size = 0;
+       if (chip->onfi_version) {
+               if (!get_onfi_ecc_param(chip, cap, sector_size)) {
+                       MTDDEBUG(MTD_DEBUG_LEVEL1, "ONFI params, minimum required ECC: %d bits in %d bytes\n",
+                               *cap, *sector_size);
+               } else {
+                       dev_info(host->dev, "NAND chip ECC reqirement is in Extended ONFI parameter, we don't support yet.\n");
+               }
+       } else {
+               dev_info(host->dev, "NAND chip is not ONFI compliant, assume ecc_bits is 2 in 512 bytes");
+       }
+       if (*cap == 0 && *sector_size == 0) {
+               /* Non-ONFI compliant or use extended ONFI parameters */
+               *cap = 2;
+               *sector_size = 512;
+       }
+
+       /* If head file doesn't specify then use the one in ONFI parameters */
+       if (host->pmecc_corr_cap == 0) {
+               /* use the most fitable ecc bits (the near bigger one ) */
+               if (*cap <= 2)
+                       host->pmecc_corr_cap = 2;
+               else if (*cap <= 4)
+                       host->pmecc_corr_cap = 4;
+               else if (*cap <= 8)
+                       host->pmecc_corr_cap = 8;
+               else if (*cap <= 12)
+                       host->pmecc_corr_cap = 12;
+               else if (*cap <= 24)
+                       host->pmecc_corr_cap = 24;
+               else
+                       return -EINVAL;
+       }
+       if (host->pmecc_sector_size == 0) {
+               /* use the most fitable sector size (the near smaller one ) */
+               if (*sector_size >= 1024)
+                       host->pmecc_sector_size = 1024;
+               else if (*sector_size >= 512)
+                       host->pmecc_sector_size = 512;
+               else
+                       return -EINVAL;
+       }
+       return 0;
+}
+#endif
+
 static int atmel_pmecc_nand_init_params(struct nand_chip *nand,
                struct mtd_info *mtd)
 {
 static int atmel_pmecc_nand_init_params(struct nand_chip *nand,
                struct mtd_info *mtd)
 {
@@ -678,8 +771,41 @@ static int atmel_pmecc_nand_init_params(struct nand_chip *nand,
        nand->ecc.correct = NULL;
        nand->ecc.hwctl = NULL;
 
        nand->ecc.correct = NULL;
        nand->ecc.hwctl = NULL;
 
-       cap = host->pmecc_corr_cap = CONFIG_PMECC_CAP;
-       sector_size = host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE;
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+       host->pmecc_corr_cap = host->pmecc_sector_size = 0;
+
+#ifdef CONFIG_PMECC_CAP
+       host->pmecc_corr_cap = CONFIG_PMECC_CAP;
+#endif
+#ifdef CONFIG_PMECC_SECTOR_SIZE
+       host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE;
+#endif
+       /* Get ECC requirement of ONFI parameters. And if CONFIG_PMECC_CAP or
+        * CONFIG_PMECC_SECTOR_SIZE not defined, then use ecc_bits, sector_size
+        * from ONFI.
+        */
+       if (pmecc_choose_ecc(host, nand, &cap, &sector_size)) {
+               dev_err(host->dev, "The NAND flash's ECC requirement(ecc_bits: %d, sector_size: %d) are not support!",
+                               cap, sector_size);
+               return -EINVAL;
+       }
+
+       if (cap > host->pmecc_corr_cap)
+               dev_info(host->dev, "WARNING: Using different ecc correct bits(%d bit) from Nand ONFI ECC reqirement (%d bit).\n",
+                               host->pmecc_corr_cap, cap);
+       if (sector_size < host->pmecc_sector_size)
+               dev_info(host->dev, "WARNING: Using different ecc correct sector size (%d bytes) from Nand ONFI ECC reqirement (%d bytes).\n",
+                               host->pmecc_sector_size, sector_size);
+#else  /* CONFIG_SYS_NAND_ONFI_DETECTION */
+       host->pmecc_corr_cap = CONFIG_PMECC_CAP;
+       host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE;
+#endif
+
+       cap = host->pmecc_corr_cap;
+       sector_size = host->pmecc_sector_size;
+
+       /* TODO: need check whether cap & sector_size is validate */
+
        if (host->pmecc_sector_size == 512)
                host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_512;
        else
        if (host->pmecc_sector_size == 512)
                host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_512;
        else