This patch add support for BCH16_ECC to omap_gpmc driver.

*need to BCH16 ECC scheme*
With newer SLC Flash technologies and MLC NAND, and large densities, pagesizes
Flash devices have become more suspectible to bit-flips. Thus stronger
ECC schemes are required for protecting the data.
But stronger ECC schemes have come with larger-sized ECC syndromes which require
more space in OOB/Spare. This puts constrains like;
(a) BCH16_ECC can correct 16 bit-flips per 512Bytes of data.
(b) BCH16_ECC generates 26-bytes of ECC syndrome / 512B.
Due to (b) this scheme can only be used with NAND devices which have enough
OOB to satisfy following equation:
OOBsize per page >= 26 * (page-size / 512)

Signed-off-by: Pekon Gupta <pe...@ti.com>
---
 drivers/mtd/nand/omap_gpmc.c  | 78 ++++++++++++++++++++++++++++++++++++++++++-
 include/linux/mtd/omap_gpmc.h |  8 +++++
 2 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c
index ba7ac2b..5bd5d91 100644
--- a/drivers/mtd/nand/omap_gpmc.c
+++ b/drivers/mtd/nand/omap_gpmc.c
@@ -224,6 +224,19 @@ static void omap_enable_hwecc(struct mtd_info *mtd, 
int32_t mode)
                        eccsize1 = 2;  /* non-ECC bits in nibbles per sector */
                }
                break;
+       case OMAP_ECC_BCH16_CODE_HW:
+               ecc_algo = 0x1;
+               bch_type = 0x2;
+               if (mode == NAND_ECC_WRITE) {
+                       bch_wrapmode = 0x01;
+                       eccsize0 = 0;  /* extra bits in nibbles per sector */
+                       eccsize1 = 52; /* OOB bits in nibbles per sector */
+               } else {
+                       bch_wrapmode = 0x01;
+                       eccsize0 = 52; /* ECC bits in nibbles per sector */
+                       eccsize1 = 0;  /* non-ECC bits in nibbles per sector */
+               }
+               break;
        default:
                return;
        }
@@ -290,6 +303,29 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const 
uint8_t *dat,
                        ptr--;
                }
                break;
+       case OMAP_ECC_BCH16_CODE_HW:
+               val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[2]);
+               ecc_code[i++] = (val >>  8) & 0xFF;
+               ecc_code[i++] = (val >>  0) & 0xFF;
+               val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[1]);
+               ecc_code[i++] = (val >> 24) & 0xFF;
+               ecc_code[i++] = (val >> 16) & 0xFF;
+               ecc_code[i++] = (val >>  8) & 0xFF;
+               ecc_code[i++] = (val >>  0) & 0xFF;
+               val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[0]);
+               ecc_code[i++] = (val >> 24) & 0xFF;
+               ecc_code[i++] = (val >> 16) & 0xFF;
+               ecc_code[i++] = (val >>  8) & 0xFF;
+               ecc_code[i++] = (val >>  0) & 0xFF;
+               for (j = 3; j >= 0; j--) {
+                       val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result_x[j]
+                                                                       );
+                       ecc_code[i++] = (val >> 24) & 0xFF;
+                       ecc_code[i++] = (val >> 16) & 0xFF;
+                       ecc_code[i++] = (val >>  8) & 0xFF;
+                       ecc_code[i++] = (val >>  0) & 0xFF;
+               }
+               break;
        default:
                return -EINVAL;
        }
@@ -308,6 +344,8 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const 
uint8_t *dat,
        case OMAP_ECC_BCH8_CODE_HW:
                ecc_code[chip->ecc.bytes - 1] = 0x00;
                break;
+       case OMAP_ECC_BCH16_CODE_HW:
+               break;
        default:
                return -EINVAL;
        }
@@ -333,7 +371,7 @@ static int omap_correct_data_bch(struct mtd_info *mtd, 
uint8_t *dat,
        struct omap_nand_info *info = chip->priv;
        struct nand_ecc_ctrl *ecc = &chip->ecc;
        uint32_t error_count = 0, error_max;
-       uint32_t error_loc[8];
+       uint32_t error_loc[ELM_MAX_ERROR_COUNT];
        enum bch_level bch_type;
        uint32_t i, ecc_flag = 0;
        uint8_t count, err = 0;
@@ -365,6 +403,9 @@ static int omap_correct_data_bch(struct mtd_info *mtd, 
uint8_t *dat,
                bch_type = BCH_8_BIT;
                omap_reverse_list(calc_ecc, ecc->bytes - 1);
                break;
+       case OMAP_ECC_BCH16_CODE_HW:
+               omap_reverse_list(calc_ecc, ecc->bytes);
+               break;
        default:
                return -EINVAL;
        }
@@ -381,6 +422,9 @@ static int omap_correct_data_bch(struct mtd_info *mtd, 
uint8_t *dat,
                        /* 14th byte in ECC is reserved to match ROM layout */
                        error_max = SECTOR_BYTES + (ecc->bytes - 1);
                        break;
+               case OMAP_ECC_BCH16_CODE_HW:
+                       error_max = SECTOR_BYTES + ecc->bytes;
+                       break;
                default:
                        return -EINVAL;
                }
@@ -666,6 +710,38 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
                return -EINVAL;
 #endif
 
+       case OMAP_ECC_BCH16_CODE_HW:
+#ifdef CONFIG_NAND_OMAP_ELM
+               debug("nand: using OMAP_ECC_BCH16_CODE_HW\n");
+               /* check ecc-scheme requirements before updating ecc info */
+               if ((26 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) {
+                       printf("nand: error: insufficient OOB: require=%d\n", (
+                               (26 * eccsteps) + BADBLOCK_MARKER_LENGTH));
+                       return -EINVAL;
+               }
+               /* intialize ELM for ECC error detection */
+               elm_init();
+               /* populate ecc specific fields */
+               nand->ecc.mode          = NAND_ECC_HW;
+               nand->ecc.size          = SECTOR_BYTES;
+               nand->ecc.bytes         = 26;
+               nand->ecc.strength      = 16;
+               nand->ecc.hwctl         = omap_enable_hwecc;
+               nand->ecc.correct       = omap_correct_data_bch;
+               nand->ecc.calculate     = omap_calculate_ecc;
+               nand->ecc.read_page     = omap_read_page_bch;
+               /* define ecc-layout */
+               ecclayout->eccbytes     = nand->ecc.bytes * eccsteps;
+               for (i = 0; i < ecclayout->eccbytes; i++)
+                       ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH;
+               ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
+               ecclayout->oobfree[0].length = oobsize - nand->ecc.bytes -
+                                               BADBLOCK_MARKER_LENGTH;
+               break;
+#else
+               printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n");
+               return -EINVAL;
+#endif
        default:
                debug("nand: error: ecc scheme not enabled or supported\n");
                return -EINVAL;
diff --git a/include/linux/mtd/omap_gpmc.h b/include/linux/mtd/omap_gpmc.h
index d55fe32..9a86582 100644
--- a/include/linux/mtd/omap_gpmc.h
+++ b/include/linux/mtd/omap_gpmc.h
@@ -27,6 +27,8 @@ enum omap_ecc {
        OMAP_ECC_BCH8_CODE_HW_DETECTION_SW,
        /* 8-bit  ECC calculation by GPMC, Error detection by ELM */
        OMAP_ECC_BCH8_CODE_HW,
+       /* 16-bit  ECC calculation by GPMC, Error detection by ELM */
+       OMAP_ECC_BCH16_CODE_HW,
 };
 
 struct gpmc_cs {
@@ -47,6 +49,10 @@ struct bch_res_0_3 {
        u32 bch_result_x[4];
 };
 
+struct bch_res_4_6 {
+       u32 bch_result_x[3];
+};
+
 struct gpmc {
        u8 res1[0x10];
        u32 sysconfig;          /* 0x10 */
@@ -77,6 +83,8 @@ struct gpmc {
        u32 testmomde_ctrl;     /* 0x230 */
        u8 res8[12];            /* 0x234 */
        struct bch_res_0_3 bch_result_0_3[GPMC_MAX_SECTORS]; /* 0x240,0x250, */
+       u8 res9[16 * 4];        /* 0x2C0 - 0x2FF */
+       struct bch_res_4_6 bch_result_4_6[GPMC_MAX_SECTORS]; /* 0x300,0x310, */
 };
 
 /* Used for board specific gpmc initialization */
-- 
1.8.5.1.163.gd7aced9

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to