From patchwork Tue Sep 3 08:33:48 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: pekon gupta X-Patchwork-Id: 272171 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 2504C2C007A for ; Tue, 3 Sep 2013 18:34:17 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 640E74A068; Tue, 3 Sep 2013 10:34:15 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id yCFv7ofPlT8A; Tue, 3 Sep 2013 10:34:15 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 3C6414A062; Tue, 3 Sep 2013 10:34:13 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 277734A018 for ; Tue, 3 Sep 2013 10:34:10 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 8zZukNTwmrQ7 for ; Tue, 3 Sep 2013 10:34:04 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from bear.ext.ti.com (bear.ext.ti.com [192.94.94.41]) by theia.denx.de (Postfix) with ESMTPS id 8809F4A05E for ; Tue, 3 Sep 2013 10:34:01 +0200 (CEST) Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id r838XwlJ024129; Tue, 3 Sep 2013 03:33:58 -0500 Received: from DFLE72.ent.ti.com (dfle72.ent.ti.com [128.247.5.109]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id r838XwKF000848; Tue, 3 Sep 2013 03:33:58 -0500 Received: from dflp33.itg.ti.com (10.64.6.16) by DFLE72.ent.ti.com (128.247.5.109) with Microsoft SMTP Server id 14.2.342.3; Tue, 3 Sep 2013 03:33:57 -0500 Received: from psplinux064.india.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by dflp33.itg.ti.com (8.14.3/8.13.8) with ESMTP id r838XpmK005289; Tue, 3 Sep 2013 03:33:56 -0500 From: Pekon Gupta To: , Date: Tue, 3 Sep 2013 14:03:48 +0530 Message-ID: <1378197229-21596-3-git-send-email-pekon@ti.com> X-Mailer: git-send-email 1.8.1 In-Reply-To: <1378197229-21596-1-git-send-email-pekon@ti.com> References: <1378197229-21596-1-git-send-email-pekon@ti.com> MIME-Version: 1.0 Cc: u-boot@lists.denx.de, balbi@ti.com Subject: [U-Boot] [PATCH v1 2/3] mtd: nand: omap: add support for BCH16_ECC - NAND driver updates X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de With increase in NAND flash densities occurence of bit-flips has increased. Thus stronger ECC schemes are required for detecting and correcting multiple simultaneous bit-flips in same NAND page. But stronger ECC schemes have large ECC syndrome which require more space in OOB/Spare. This patch add support for BCH16_ECC: (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 --- arch/arm/include/asm/arch-am33xx/cpu.h | 15 +++- arch/arm/include/asm/arch-am33xx/omap_gpmc.h | 4 +- drivers/mtd/nand/omap_gpmc.c | 103 ++++++++++++++++++++------- include/mtd/mtd-abi.h | 3 +- 4 files changed, 96 insertions(+), 29 deletions(-) diff --git a/arch/arm/include/asm/arch-am33xx/cpu.h b/arch/arm/include/asm/arch-am33xx/cpu.h index 10b56e0..1de92e6 100644 --- a/arch/arm/include/asm/arch-am33xx/cpu.h +++ b/arch/arm/include/asm/arch-am33xx/cpu.h @@ -63,7 +63,16 @@ struct gpmc_cs { }; struct bch_res_0_3 { - u32 bch_result_x[4]; + u32 bch_result0; + u32 bch_result1; + u32 bch_result2; + u32 bch_result3; +}; + +struct bch_res_4_6 { + u32 bch_result4; + u32 bch_result5; + u32 bch_result6; }; struct gpmc { @@ -95,7 +104,9 @@ struct gpmc { u8 res7[12]; /* 0x224 */ u32 testmomde_ctrl; /* 0x230 */ u8 res8[12]; /* 0x234 */ - struct bch_res_0_3 bch_result_0_3[2]; /* 0x240 */ + struct bch_res_0_3 bch_result_0_3[8]; /* 0x240 - 0x2BF */ + u8 res9[16 * 4]; /* 0x2C0 - 0x2FF */ + struct bch_res_4_6 bch_result_4_6[8]; /* 0x300 - 0x37F */ }; /* Used for board specific gpmc initialization */ diff --git a/arch/arm/include/asm/arch-am33xx/omap_gpmc.h b/arch/arm/include/asm/arch-am33xx/omap_gpmc.h index 69982c1..ade0ca4 100644 --- a/arch/arm/include/asm/arch-am33xx/omap_gpmc.h +++ b/arch/arm/include/asm/arch-am33xx/omap_gpmc.h @@ -16,7 +16,9 @@ enum omap_ecc { /* 8-bit ECC calculation by GPMC, Error detection by Software */ OMAP_ECC_BCH8_CODE_HW_DETECTION_SW, /* 8-bit ECC calculation by GPMC, Error detection by ELM */ - OMAP_ECC_BCH8_CODE_HW + OMAP_ECC_BCH8_CODE_HW, + /* 16-bit ECC calculation by GPMC, Error detection by ELM */ + OMAP_ECC_BCH16_CODE_HW, }; #endif /* __ASM_ARCH_OMAP_GPMC_H */ diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 08d59cc..3501374 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -155,21 +155,6 @@ struct nand_bch_priv { enum omap_ecc ecc_scheme; }; -/* bch types */ -#define ECC_BCH4 0 -#define ECC_BCH8 1 -#define ECC_BCH16 2 - -/* GPMC ecc engine settings */ -#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */ -#define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */ - -/* BCH nibbles for diff bch levels */ -#define NAND_ECC_HW_BCH ((uint8_t)(NAND_ECC_HW_OOB_FIRST) + 1) -#define ECC_BCH4_NIBBLES 13 -#define ECC_BCH8_NIBBLES 26 -#define ECC_BCH16_NIBBLES 52 - /* * This can be a single instance cause all current users have only one NAND * with nearly the same setup (BCH8, some with ELM and others with sw BCH @@ -177,9 +162,7 @@ struct nand_bch_priv { * When some users with other BCH strength will exists this have to change! */ static __maybe_unused struct nand_bch_priv bch_priv = { - .mode = NAND_ECC_HW_BCH, .type = ECC_BCH8, - .nibbles = ECC_BCH8_NIBBLES, .control = NULL }; @@ -223,6 +206,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: printf("nand: error: invalid driver configuration\n"); return; @@ -276,19 +272,19 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, break; #ifdef CONFIG_BCH case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: - val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result_x[3]); + val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result3); *ecc_code++ = 0xef ^ (val & 0xFF); - val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result_x[2]); + val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result2); *ecc_code++ = 0x51 ^ ((val >> 24) & 0xFF); *ecc_code++ = 0x2e ^ ((val >> 16) & 0xFF); *ecc_code++ = 0x09 ^ ((val >> 8) & 0xFF); *ecc_code++ = 0xed ^ ((val >> 0) & 0xFF); - val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result_x[1]); + val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result1); *ecc_code++ = 0x93 ^ ((val >> 24) & 0xFF); *ecc_code++ = 0x9a ^ ((val >> 16) & 0xFF); *ecc_code++ = 0xc2 ^ ((val >> 8) & 0xFF); *ecc_code++ = 0x97 ^ ((val >> 0) & 0xFF); - val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result_x[0]); + val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result0); *ecc_code++ = 0x79 ^ ((val >> 24) & 0xFF); *ecc_code++ = 0xe5 ^ ((val >> 16) & 0xFF); *ecc_code++ = 0x24 ^ ((val >> 8) & 0xFF); @@ -296,7 +292,7 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, break; #endif case OMAP_ECC_BCH8_CODE_HW: - ptr = &gpmc_cfg->bch_result_0_3[0].bch_result_x[3]; + ptr = &gpmc_cfg->bch_result_0_3[0].bch_result3; val = readl(ptr); ecc_code[i++] = (val >> 0) & 0xFF; ptr--; @@ -309,6 +305,27 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, ptr--; } break; + case OMAP_ECC_BCH16_CODE_HW: + ptr = &gpmc_cfg->bch_result_4_6[0].bch_result6; + ecc_code[i++] = (readl(ptr) >> 8) & 0xFF; + ecc_code[i++] = (readl(ptr) >> 0) & 0xFF; + ptr--; + for (j = 0; j < 2; j++) { + ecc_code[i++] = (readl(ptr) >> 24) & 0xFF; + ecc_code[i++] = (readl(ptr) >> 16) & 0xFF; + ecc_code[i++] = (readl(ptr) >> 8) & 0xFF; + ecc_code[i++] = (readl(ptr) >> 0) & 0xFF; + ptr--; + } + ptr = &gpmc_cfg->bch_result_0_3[0].bch_result3; + for (j = 0; j < 4; j++) { + ecc_code[i++] = (readl(ptr) >> 24) & 0xFF; + ecc_code[i++] = (readl(ptr) >> 16) & 0xFF; + ecc_code[i++] = (readl(ptr) >> 8) & 0xFF; + ecc_code[i++] = (readl(ptr) >> 0) & 0xFF; + ptr--; + } + break; default: printf("nand: error: invalid driver configuration\n"); return -EINVAL; @@ -358,7 +375,7 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat, return 0; elm_reset(); - elm_config((enum bch_level)(bch->type)); + elm_config(bch->type); /* * while reading ECC result we read it in big endian. @@ -368,8 +385,8 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat, syndrome[i] = calc_ecc[j]; /* use elm module to check for errors */ - if (elm_check_error(syndrome, bch->nibbles, &error_count, - error_loc) != 0) { + if (elm_check_error(syndrome, bch->type, + &error_count, error_loc) != 0) { printf("nand: error: uncorrectable ECC errors\n"); return -EINVAL; } @@ -386,6 +403,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 + (eccbytes - 1); break; + case ECC_BCH16: + error_max = SECTOR_BYTES + eccbytes; + break; default: return -EINVAL; } @@ -541,6 +561,7 @@ static int omap_select_ecc_scheme(struct nand_chip *nand, int ecc_scheme, printf("nand: error: could not init_bch()\n"); return -ENODEV; } + bch_priv.type = ECC_BCH8; /* define ecc-layout */ ecclayout->eccbytes = nand->ecc.bytes * (pagesize / SECTOR_BYTES); @@ -564,6 +585,7 @@ static int omap_select_ecc_scheme(struct nand_chip *nand, int ecc_scheme, nand->ecc.calculate = omap_calculate_ecc; /* ELM is used for ECC error detection */ elm_init(); + bch_priv.type = ECC_BCH8; /* define ecc-layout */ ecclayout->eccbytes = nand->ecc.bytes * (pagesize / SECTOR_BYTES); @@ -576,10 +598,41 @@ static int omap_select_ecc_scheme(struct nand_chip *nand, int ecc_scheme, BADBLOCK_MARKER_LENGTH; bch->ecc_scheme = OMAP_ECC_BCH8_CODE_HW; break; + case OMAP_ECC_BCH16_CODE_HW: + debug("nand: using OMAP_ECC_BCH16_CODE_HW\n"); + 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; + /* ELM is used for ECC error detection */ + elm_init(); + bch_priv.type = ECC_BCH16; + /* define ecc-layout */ + ecclayout->eccbytes = nand->ecc.bytes * + (pagesize / SECTOR_BYTES); + 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; + bch->ecc_scheme = OMAP_ECC_BCH16_CODE_HW; + break; default: debug("nand: error: ecc scheme not enabled or supported\n"); return -EINVAL; } + + /* check if NAND spare/OOB has enough bytes to accomodate ecclayout */ + if ((ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH) > oobsize) { + printf("nand: error: insufficient OOB bytes. require=%d\n", + (ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH)); + return -EINVAL; + } return 0; } diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index d51c1ab..f7040be 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -156,13 +156,14 @@ struct nand_oobfree { }; #define MTD_MAX_OOBFREE_ENTRIES 8 +#define MTD_MAX_ECCPOS_ENTRIES 256 /* * ECC layout control structure. Exported to userspace for * diagnosis and to allow creation of raw images */ struct nand_ecclayout { uint32_t eccbytes; - uint32_t eccpos[128]; + uint32_t eccpos[MTD_MAX_ECCPOS_ENTRIES]; uint32_t oobavail; struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; };