[U-Boot] [PATCH v1 2/3] mtd: nand: omap: add support for BCH16_ECC - NAND driver updates
Pekon Gupta
pekon at ti.com
Tue Sep 3 10:33:48 CEST 2013
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 <pekon at ti.com>
---
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];
};
--
1.8.1
More information about the U-Boot
mailing list