[U-Boot] [RFC/PATCH 0/4] BCH8 support for OMAP3

This RFC series implements BCH8 for OMAP3 as provided by linux kernel in commit 0e618ef0a6a33cf7ef96c2c824402088dd8ef48c. This series is heavily influenced by Ilyas series 'NAND support for AM33XX' thus could share some code.
I have managed to load kernel from an ubifs written by the kernel driver, but is far away from tested thoroughly.
Cause my NAND device 'NAND device: Manufacturer ID: 0x2c, Chip ID: 0xbc (Micron NAND 512MiB 1,8V 16-bit)' does support 1bit ECC for first sector if erase is less than 1000, the rest requires 4bit ECC. Therefore the SPL needs to support BCH, the impact is about 9k for the SPL.
Andreas Bießmann (4): omap3/cpu.h: add BCH support omap3/omap_gpmc.h: add ooblayout for BCH8 as in kernel omap_gpmc: add support for hw assisted BCH8 tricorder: enable hw assisted BCH8 in SPL and u-boot
arch/arm/cpu/armv7/omap3/board.c | 8 +- arch/arm/include/asm/arch-omap3/cpu.h | 6 + arch/arm/include/asm/arch-omap3/omap_gpmc.h | 12 ++ drivers/mtd/nand/omap_gpmc.c | 216 ++++++++++++++++++++++++++- include/configs/tricorder.h | 10 +- lib/Makefile | 3 +- 6 files changed, 248 insertions(+), 7 deletions(-)

This patch adds the BCH result registers to register mapping for OMAP3 gpmc.
Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com --- arch/arm/include/asm/arch-omap3/cpu.h | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/arm/include/asm/arch-omap3/cpu.h b/arch/arm/include/asm/arch-omap3/cpu.h index 5683e16..c601873 100644 --- a/arch/arm/include/asm/arch-omap3/cpu.h +++ b/arch/arm/include/asm/arch-omap3/cpu.h @@ -109,6 +109,10 @@ struct gpmc_cs { u8 res[8]; /* blow up to 0x30 byte */ };
+struct bch_res_0_3 { + u32 bch_result_x[4]; +}; + struct gpmc { u8 res1[0x10]; u32 sysconfig; /* 0x10 */ @@ -135,6 +139,8 @@ struct gpmc { u32 ecc7_result; /* 0x218 */ u32 ecc8_result; /* 0x21C */ u32 ecc9_result; /* 0x220 */ + u8 res7[0x1C]; /* fill up to 0x240 */ + struct bch_res_0_3 bch_result_0_3[7]; /* 0x240 */ };
/* Used for board specific gpmc initialization */

This patch adds BCH8 ooblayout for NAND as provided by 0e618ef0a6a33cf7ef96c2c824402088dd8ef48c in linux kernel. This Layout is currently only provided for 64 byte OOB.
Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com --- arch/arm/include/asm/arch-omap3/omap_gpmc.h | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/arch/arm/include/asm/arch-omap3/omap_gpmc.h b/arch/arm/include/asm/arch-omap3/omap_gpmc.h index 800e4ee..3a3abdf 100644 --- a/arch/arm/include/asm/arch-omap3/omap_gpmc.h +++ b/arch/arm/include/asm/arch-omap3/omap_gpmc.h @@ -80,6 +80,18 @@ } #endif
+/* use the same layout here as he kernel does (ecc bytes at oob tail) */ +#define GPMC_NAND_HW_BCH8_ECC_LAYOUT {\ + .eccbytes = 56,\ + .eccpos = {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,\ + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,\ + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,\ + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63},\ + .oobfree = {\ + {.offset = 2,\ + .length = 10 } } \ +} + /* GPMC CS configuration for an SMSC LAN9221 ethernet controller */ #define NET_LAN9221_GPMC_CONFIG1 0x00001000 #define NET_LAN9221_GPMC_CONFIG2 0x00060700

The BCH for OMAP3 is implemented as the linux kernel in 0e618ef0a6a33cf7ef96c2c824402088dd8ef48c does.
The kernel states:
---8<--- The OMAP3 GPMC hardware BCH engine computes remainder polynomials, it does not provide automatic error location and correction: this step is implemented using the BCH library. --->8---
And we do so in u-boot.
This implementation uses the same layout for BCH8 but it is fix. The current provided layout does only work with 64 Byte OOB.
Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com --- This patch has some debug stuff in which should be cleaned up.
arch/arm/cpu/armv7/omap3/board.c | 8 +- drivers/mtd/nand/omap_gpmc.c | 216 +++++++++++++++++++++++++++++++++++++- lib/Makefile | 3 +- 3 files changed, 223 insertions(+), 4 deletions(-)
diff --git a/arch/arm/cpu/armv7/omap3/board.c b/arch/arm/cpu/armv7/omap3/board.c index f3cd81a..22286c0 100644 --- a/arch/arm/cpu/armv7/omap3/board.c +++ b/arch/arm/cpu/armv7/omap3/board.c @@ -330,8 +330,10 @@ static int do_switch_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char * const arg { if (argc != 2) goto usage; - if (strncmp(argv[1], "hw", 2) == 0) + if (strncmp(argv[1], "hw1", 3) == 0) omap_nand_switch_ecc(1); + else if (strncmp(argv[1], "hw2", 3) == 0) + omap_nand_switch_ecc(2); else if (strncmp(argv[1], "sw", 2) == 0) omap_nand_switch_ecc(0); else @@ -347,7 +349,9 @@ usage: U_BOOT_CMD( nandecc, 2, 1, do_switch_ecc, "switch OMAP3 NAND ECC calculation algorithm", - "[hw/sw] - Switch between NAND hardware (hw) or software (sw) ecc algorithm" + "[hw1/hw2/sw] - Switch between NAND hardware (hw1 - 1bit hamming),\n" + "\tNAND hardware assisted BCH8 (hw2 - 8bit BCH)\n" + "\tor software (sw) ecc algorithm" );
#endif /* CONFIG_NAND_OMAP_GPMC & !CONFIG_SPL_BUILD */ diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index f1469d1..a5ab046 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -27,6 +27,7 @@ #include <asm/arch/mem.h> #include <asm/arch/omap_gpmc.h> #include <linux/mtd/nand_ecc.h> +#include <linux/bch.h> #include <linux/compiler.h> #include <nand.h>
@@ -234,12 +235,194 @@ static void __maybe_unused omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) } }
+/* + * basic BCH8 support + */ +#ifdef CONFIG_NAND_OMAP_BCH8 +static struct nand_ecclayout hw_bch8_nand_oob = GPMC_NAND_HW_BCH8_ECC_LAYOUT; + +/* + * omap_init_hwecc_bch - Initialize the BCH Hardware ECC for NAND flash in + * GPMC controller + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +static void omap_init_hwecc_bch(struct nand_chip *chip, int32_t mode) +{ + uint32_t val, dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1; + + /* Clear the ecc result registers, select ecc reg as 1 */ + writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); + + /* + * When using BCH, sector size is hardcoded to 512 bytes. + * Here we are using wrapping mode 6 both for reading and writing, with: + * size0 = 0 (no additional protected byte in spare area) + * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) + */ + writel((32 << 22) | (0 << 12), &gpmc_cfg->ecc_size_config); + + /* BCH configuration */ + val = ((1 << 16) | /* enable BCH */ + (1 << 12) | /* set fix to BCH8 */ + (0x06 << 8) | /* wrap mode = 6 */ + (dev_width << 7) | /* bus width */ + (0 << 4) | /* number of sectors (we use 1 sector)*/ + (cs << 1) | /* ECC CS */ + (0x1)); /* enable ECC */ +// debug("set ECC_CONFIG = 0x%08x\n", val); + writel(val, &gpmc_cfg->ecc_config); +} + +/** + * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +static void omap_enable_hwecc_bch(struct mtd_info *mtd, int32_t mode) +{ + struct nand_chip *chip = mtd->priv; + + omap_init_hwecc_bch(chip, mode); + /* enable ecc */ + writel((readl(&gpmc_cfg->ecc_config) | 0x1), &gpmc_cfg->ecc_config); +} + +/* + * omap_ecc_disable - Disable H/W ECC calculation + * + * @mtd: MTD device structure + * + */ +static void omap_ecc_disable(struct mtd_info *mtd) +{ + writel((readl(&gpmc_cfg->ecc_config) & ~0x1), + &gpmc_cfg->ecc_config); +} + +/* + * omap_calculate_ecc_bch - Read BCH ECC result + * + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed (unused here) + * @ecc: The ECC output buffer + */ +static int omap_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc) +{ + int ret = 0; + size_t i; + unsigned long nsectors, val1, val2, val3, val4; + uint8_t *pecc = ecc; + + nsectors = ((readl(&gpmc_cfg->ecc_config) >> 4) & 0x7) + 1; + + for (i = 0; i < nsectors; i++) { +#if 0 + debug("read res0 @%p, res1 @%p, res2 @%p, res4 @%p\n", + &gpmc_cfg->bch_result_0_3[i].bch_result_x[0], + &gpmc_cfg->bch_result_0_3[i].bch_result_x[1], + &gpmc_cfg->bch_result_0_3[i].bch_result_x[2], + &gpmc_cfg->bch_result_0_3[i].bch_result_x[3]); +#endif + + /* Read hw-computed remainder */ + val1 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[0]); + val2 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[1]); + val3 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[2]); + val4 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[3]); +// debug("val1 = 0x%08lx, val2 = 0x%08lx, val3 = 0x%08lx, val4 = 0x%08lx\n", val1, val2, val3, val4); + + /* + * Add constant polynomial to remainder, in order to get an ecc + * sequence of 0xFFs for a buffer filled with 0xFFs. + */ + *ecc++ = 0xef ^ (val4 & 0xFF); + *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF); + *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF); + *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF); + *ecc++ = 0xed ^ (val3 & 0xFF); + *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF); + *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF); + *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF); + *ecc++ = 0x97 ^ (val2 & 0xFF); + *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF); + *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF); + *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF); + *ecc++ = 0xb5 ^ (val1 & 0xFF); +#if 0 + debug("ecc: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + pecc[0], pecc[1], pecc[2], pecc[3], pecc[4], pecc[5], pecc[6], + pecc[7], pecc[8], pecc[9], pecc[10], pecc[11], pecc[12]); +#endif +} + + /* + * Stop reading anymore ECC vals and clear old results + * enable will be called if more reads are required + */ + omap_ecc_disable(mtd); + + return ret; +} + +/** + * omap_correct_data_bch - Decode received data and correct errors + * @mtd: MTD device structure + * @data: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + */ +static int omap_correct_data_bch(struct mtd_info *mtd, u_char *data, + u_char *read_ecc, u_char *calc_ecc) +{ + int i, count; + /* cannot correct more than 8 errors */ + unsigned int errloc[8]; + struct nand_chip *chip = mtd->priv; + struct bch_control *bch = chip->priv; + + count = decode_bch(bch, NULL, 512, read_ecc, calc_ecc, NULL, errloc); + if (count > 0) { + /* correct errors */ + for (i = 0; i < count; i++) { + /* correct data only, not ecc bytes */ + if (errloc[i] < 8*512) + data[errloc[i]/8] ^= 1 << (errloc[i] & 7); + printf("corrected bitflip %u\n", errloc[i]); + debug("read_ecc: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + read_ecc[0], read_ecc[1], read_ecc[2], read_ecc[3], read_ecc[4], read_ecc[5], read_ecc[6], + read_ecc[7], read_ecc[8], read_ecc[9], read_ecc[10], read_ecc[11], read_ecc[12]); + debug("calc_ecc: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + calc_ecc[0], calc_ecc[1], calc_ecc[2], calc_ecc[3], calc_ecc[4], calc_ecc[5], calc_ecc[6], + calc_ecc[7], calc_ecc[8], calc_ecc[9], calc_ecc[10], calc_ecc[11], calc_ecc[12]); + } + } else if (count < 0) { + puts("ecc unrecoverable error\n"); + } + return count; +} + +/** + * omap_free_bch - Release BCH ecc resources + * @mtd: MTD device structure + */ +static void omap_free_bch(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + if (chip->priv) { + free_bch((struct bch_control *)chip->priv); + chip->priv = NULL; + } +} +#endif /* CONFIG_NAND_OMAP_BCH8 */ + #ifndef CONFIG_SPL_BUILD /* * omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc. * The default is to come up on s/w ecc * - * @hardware - 1 -switch to h/w ecc, 0 - s/w ecc + * @hardware - 1 -switch to h/w ecc, 2 - switch to h/w assisted BCH8, 0 - s/w ecc * */ void omap_nand_switch_ecc(int32_t hardware) @@ -279,6 +462,18 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.calculate = omap_calculate_ecc; omap_hwecc_init(nand); printf("HW ECC selected\n"); +#ifdef CONFIG_NAND_OMAP_BCH8 + } else if (hardware == 2) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_bch8_nand_oob; + nand->ecc.size = 512; + nand->ecc.bytes = 13; + nand->ecc.hwctl = omap_enable_hwecc_bch; + nand->ecc.correct = omap_correct_data_bch; + nand->ecc.calculate = omap_calculate_ecc_bch; + omap_init_hwecc_bch(nand, NAND_ECC_READ); + printf("HW BCH8 selected\n"); +#endif } else { nand->ecc.mode = NAND_ECC_SOFT; /* Use mtd default settings */ @@ -313,6 +508,7 @@ int board_nand_init(struct nand_chip *nand) { int32_t gpmc_config = 0; cs = 0; + nand->priv = NULL;
/* * xloader/Uboot's gpmc configuration would have configured GPMC for @@ -351,6 +547,23 @@ int board_nand_init(struct nand_chip *nand)
nand->chip_delay = 100; /* Default ECC mode */ +#ifdef CONFIG_NAND_OMAP_BCH8 + nand->priv = init_bch(13, 8, 0x201b /* hw polynominal */); + if (!nand->priv) { + puts("Could not init_bch()\n"); + } +#endif + +#if defined CONFIG_NAND_OMAP_BCH8 + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_bch8_nand_oob; + nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; + nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; + nand->ecc.hwctl = omap_enable_hwecc_bch; + nand->ecc.correct = omap_correct_data_bch; + nand->ecc.calculate = omap_calculate_ecc_bch; + omap_init_hwecc_bch(nand, NAND_ECC_READ); +#else #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_NAND_SOFTECC) nand->ecc.mode = NAND_ECC_SOFT; #else @@ -363,6 +576,7 @@ int board_nand_init(struct nand_chip *nand) nand->ecc.calculate = omap_calculate_ecc; omap_hwecc_init(nand); #endif +#endif
#ifdef CONFIG_SPL_BUILD if (nand->options & NAND_BUSWIDTH_16) diff --git a/lib/Makefile b/lib/Makefile index e44e045..20cc40c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -25,9 +25,10 @@ include $(TOPDIR)/config.mk
LIB = $(obj)libgeneric.o
+COBJS-$(CONFIG_BCH) += bch.o + ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_ADDR_MAP) += addr_map.o -COBJS-$(CONFIG_BCH) += bch.o COBJS-$(CONFIG_AES) += aes.o COBJS-$(CONFIG_BZIP2) += bzlib.o COBJS-$(CONFIG_BZIP2) += bzlib_crctable.o

On 11/23/2012 09:14:16 AM, Andreas Bießmann wrote:
diff --git a/arch/arm/cpu/armv7/omap3/board.c b/arch/arm/cpu/armv7/omap3/board.c index f3cd81a..22286c0 100644 --- a/arch/arm/cpu/armv7/omap3/board.c +++ b/arch/arm/cpu/armv7/omap3/board.c @@ -330,8 +330,10 @@ static int do_switch_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char * const arg { if (argc != 2) goto usage;
- if (strncmp(argv[1], "hw", 2) == 0)
- if (strncmp(argv[1], "hw1", 3) == 0) omap_nand_switch_ecc(1);
- else if (strncmp(argv[1], "hw2", 3) == 0)
else if (strncmp(argv[1], "sw", 2) == 0) omap_nand_switch_ecc(0); elseomap_nand_switch_ecc(2);
Will this break any existing scripts? Maybe "hw" should be left alone and "hw2" renamed to something more descriptive (e.g. "bch8")?
@@ -347,7 +349,9 @@ usage: U_BOOT_CMD( nandecc, 2, 1, do_switch_ecc, "switch OMAP3 NAND ECC calculation algorithm",
- "[hw/sw] - Switch between NAND hardware (hw) or software (sw)
ecc algorithm"
- "[hw1/hw2/sw] - Switch between NAND hardware (hw1 - 1bit
hamming),\n"
- "\tNAND hardware assisted BCH8 (hw2 - 8bit BCH)\n"
- "\tor software (sw) ecc algorithm"
);
#endif /* CONFIG_NAND_OMAP_GPMC & !CONFIG_SPL_BUILD */ diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index f1469d1..a5ab046 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -27,6 +27,7 @@ #include <asm/arch/mem.h> #include <asm/arch/omap_gpmc.h> #include <linux/mtd/nand_ecc.h> +#include <linux/bch.h> #include <linux/compiler.h> #include <nand.h>
@@ -234,12 +235,194 @@ static void __maybe_unused omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) } }
+/*
- basic BCH8 support
- */
+#ifdef CONFIG_NAND_OMAP_BCH8 +static struct nand_ecclayout hw_bch8_nand_oob = GPMC_NAND_HW_BCH8_ECC_LAYOUT;
Is this layout going to be used anywhere other than here? If not, why hide the layout in a #define?
+/*
- omap_init_hwecc_bch - Initialize the BCH Hardware ECC for NAND
flash in
GPMC controller
- @mtd: MTD device structure
- @mode: Read/Write mode
- */
+static void omap_init_hwecc_bch(struct nand_chip *chip, int32_t mode) +{
- uint32_t val, dev_width = (chip->options & NAND_BUSWIDTH_16) >>
1;
- /* Clear the ecc result registers, select ecc reg as 1 */
- writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
- /*
* When using BCH, sector size is hardcoded to 512 bytes.
* Here we are using wrapping mode 6 both for reading and
writing, with:
* size0 = 0 (no additional protected byte in spare area)
* size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare
area)
*/
- writel((32 << 22) | (0 << 12), &gpmc_cfg->ecc_size_config);
- /* BCH configuration */
- val = ((1 << 16) | /* enable BCH */
(1 << 12) | /* set fix to BCH8 */
(0x06 << 8) | /* wrap mode = 6 */
(dev_width << 7) | /* bus width */
(0 << 4) | /* number of sectors
(we use 1 sector)*/
(cs << 1) | /* ECC CS */
(0x1)); /* enable ECC */
Most of those magic numbers should be referred to symbolicly.
+static void omap_free_bch(struct mtd_info *mtd) +{
- struct nand_chip *chip = mtd->priv;
- if (chip->priv) {
free_bch((struct bch_control *)chip->priv);
chip->priv = NULL;
- }
Do you really need this cast?
+#ifdef CONFIG_NAND_OMAP_BCH8
- nand->priv = init_bch(13, 8, 0x201b /* hw polynominal */);
- if (!nand->priv) {
puts("Could not init_bch()\n");
- }
+#endif
+#if defined CONFIG_NAND_OMAP_BCH8
Merge these two ifdef sections.
Shouldn't you take some action on the failure case other than printing a message and continuing to try to use bch8?
-Scott

Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Thomas Weber weber@corscience.de Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com --- include/configs/tricorder.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/include/configs/tricorder.h b/include/configs/tricorder.h index 4e4e089..9b8fe14 100644 --- a/include/configs/tricorder.h +++ b/include/configs/tricorder.h @@ -128,6 +128,8 @@
#define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND */ /* devices */ +#define CONFIG_NAND_OMAP_BCH8 +#define CONFIG_BCH
/* commands to include */ #include <config_cmd_default.h> @@ -354,11 +356,13 @@ #define CONFIG_SYS_NAND_OOBSIZE 64 #define CONFIG_SYS_NAND_BLOCK_SIZE (128*1024) #define CONFIG_SYS_NAND_BAD_BLOCK_POS NAND_LARGE_BADBLOCK_POS -#define CONFIG_SYS_NAND_ECCPOS {2, 3, 4, 5, 6, 7, 8, 9,\ - 10, 11, 12, 13} +#define CONFIG_SYS_NAND_ECCPOS {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,\ + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,\ + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,\ + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}
#define CONFIG_SYS_NAND_ECCSIZE 512 -#define CONFIG_SYS_NAND_ECCBYTES 3 +#define CONFIG_SYS_NAND_ECCBYTES 13
#define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_TEXT_BASE

On 11/23/2012 04:14 PM, Andreas Bießmann wrote:
This RFC series implements BCH8 for OMAP3 as provided by linux kernel in commit 0e618ef0a6a33cf7ef96c2c824402088dd8ef48c. This series is heavily influenced by Ilyas series 'NAND support for AM33XX' thus could share some code.
Any comments on that series? I would appreciate to get the BCH8 support in for at least the tricorder board.
I have managed to load kernel from an ubifs written by the kernel driver, but is far away from tested thoroughly.
We used that patchset for a while in-house and could not find obvious issues. However we need to hack the SPL a bit to get the bigger footprint into SRAM with 2013.01. I would really appreciate if some other omap users could test it. One thing is that most current NAND (even SLC) devices require at least 4Bit ecc for most sections.
Cause my NAND device 'NAND device: Manufacturer ID: 0x2c, Chip ID: 0xbc (Micron NAND 512MiB 1,8V 16-bit)' does support 1bit ECC for first sector if erase is less than 1000, the rest requires 4bit ECC. Therefore the SPL needs to support BCH, the impact is about 9k for the SPL.
So my question here is if this series would be accepted for the upcoming release. I could work on it next week full time, so if I get a go for this release I would do so.
Best regards
Andreas Bießmann

On Thu, Mar 28, 2013 at 11:49:54AM +0100, Andreas Bie??mann wrote:
On 11/23/2012 04:14 PM, Andreas Bie??mann wrote:
This RFC series implements BCH8 for OMAP3 as provided by linux kernel in commit 0e618ef0a6a33cf7ef96c2c824402088dd8ef48c. This series is heavily influenced by Ilyas series 'NAND support for AM33XX' thus could share some code.
Any comments on that series? I would appreciate to get the BCH8 support in for at least the tricorder board.
OK, so, some comments: - We should pull the gpmc structs out of arch-*/cpu.h and into <asm/omap_gpmc.h> which also means merging <asm/arch-am33xx/omap_gpmc.h> and <asm/arch-omap3/omap_gpmc.h> but I suspect that's easy. - In terms of 'nandecc' command, I don't like breaking existing setup/scripts, so my first thought is "nandecc hw" -> 1bit, "nandecc sw" -> sw (both just like today), "nandecc hw bch8" -> bch8 and "nandecc hw hamming" -> 1bit, which leaves room down the line for someone else to add nandecc hw bch4 -> bch4 (which is possible and I know exists in custom solutions somewhere).
I have managed to load kernel from an ubifs written by the kernel driver, but is far away from tested thoroughly.
We used that patchset for a while in-house and could not find obvious issues. However we need to hack the SPL a bit to get the bigger footprint into SRAM with 2013.01.
What exactly did you do? We _should_ already be taking up all of SRAM with a few kb saved off for stack. We might be able to get away with less stack, but we'd need to check that a bit with the .su files.
Cause my NAND device 'NAND device: Manufacturer ID: 0x2c, Chip ID: 0xbc (Micron NAND 512MiB 1,8V 16-bit)' does support 1bit ECC for first sector if erase is less than 1000, the rest requires 4bit ECC. Therefore the SPL needs to support BCH, the impact is about 9k for the SPL.
So my question here is if this series would be accepted for the upcoming release. I could work on it next week full time, so if I get a go for this release I would do so.
The RFC was well in time, so yes, I'm agreeable given the scope of the changes.

Dear Tom Rini,
On 03/28/2013 03:21 PM, Tom Rini wrote:
On Thu, Mar 28, 2013 at 11:49:54AM +0100, Andreas Bie??mann wrote:
On 11/23/2012 04:14 PM, Andreas Bie??mann wrote:
This RFC series implements BCH8 for OMAP3 as provided by linux kernel in commit 0e618ef0a6a33cf7ef96c2c824402088dd8ef48c. This series is heavily influenced by Ilyas series 'NAND support for AM33XX' thus could share some code.
Any comments on that series? I would appreciate to get the BCH8 support in for at least the tricorder board.
OK, so, some comments:
- We should pull the gpmc structs out of arch-*/cpu.h and into <asm/omap_gpmc.h> which also means merging <asm/arch-am33xx/omap_gpmc.h> and <asm/arch-omap3/omap_gpmc.h> but I suspect that's easy.
Will do so. But we will need some asm/arch/omap_gpmc.h defining the differences between both systems (ELM based BCH8 has another OOB footprint than HW assisted BCH8 on omap3).
- In terms of 'nandecc' command, I don't like breaking existing setup/scripts, so my first thought is "nandecc hw" -> 1bit, "nandecc sw" -> sw (both just like today), "nandecc hw bch8" -> bch8 and "nandecc hw hamming" -> 1bit, which leaves room down the line for someone else to add nandecc hw bch4 -> bch4 (which is possible and I know exists in custom solutions somewhere).
Sounds good for me. We will end up with 'nandecc hw' -> bch8 on am33xx and 'nandecc hw' -> hamming on omap3 based boards. To enable bch8 explicitly on omap3 based devices we have the 'nandecc hw bch8'. I will add another patch to the series changing the interface from omap_nand_switch_ecc(int32) to omap_nand_switch_ecc(bool hw, uint32 eccbits) (or something like this).
BTW: Has anyone seen that ELM based bch8 on am33xx can not be chosen via nandecc command?
I have managed to load kernel from an ubifs written by the kernel driver, but is far away from tested thoroughly.
We used that patchset for a while in-house and could not find obvious issues. However we need to hack the SPL a bit to get the bigger footprint into SRAM with 2013.01.
What exactly did you do?
Well, disabled FAT for this specific build ...
We _should_ already be taking up all of SRAM with a few kb saved off for stack.
We take 8 kB for stack in most configs on omap3, thus having 54 kB for .text and .*data. Unfortunately the HW assisted BCH on omap3 based devices require the bch library to decode ECC, this will grow the .text + .*data sections about 9k in sum (AFAIR, I've measured it some day).
We might be able to get away with less stack, but we'd need to check that a bit with the .su files.
Changing space for stack to 7 kB worked out with all features in SPL (BCH + FAT for my build), this needs some testing though.
Best regards
Andreas Bießmann

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 04/02/2013 04:49 AM, Andreas Bießmann wrote:
Dear Tom Rini,
On 03/28/2013 03:21 PM, Tom Rini wrote:
On Thu, Mar 28, 2013 at 11:49:54AM +0100, Andreas Bie??mann wrote:
On 11/23/2012 04:14 PM, Andreas Bie??mann wrote:
This RFC series implements BCH8 for OMAP3 as provided by linux kernel in commit 0e618ef0a6a33cf7ef96c2c824402088dd8ef48c. This series is heavily influenced by Ilyas series 'NAND support for AM33XX' thus could share some code.
Any comments on that series? I would appreciate to get the BCH8 support in for at least the tricorder board.
OK, so, some comments: - We should pull the gpmc structs out of arch-*/cpu.h and into <asm/omap_gpmc.h> which also means merging <asm/arch-am33xx/omap_gpmc.h> and <asm/arch-omap3/omap_gpmc.h> but I suspect that's easy.
Will do so. But we will need some asm/arch/omap_gpmc.h defining the differences between both systems (ELM based BCH8 has another OOB footprint than HW assisted BCH8 on omap3).
Shoot. But, we can handle it, see the mmc headers for an example.
- In terms of 'nandecc' command, I don't like breaking existing
setup/scripts, so my first thought is "nandecc hw" -> 1bit, "nandecc sw" -> sw (both just like today), "nandecc hw bch8" -> bch8 and "nandecc hw hamming" -> 1bit, which leaves room down the line for someone else to add nandecc hw bch4 -> bch4 (which is possible and I know exists in custom solutions somewhere).
Sounds good for me. We will end up with 'nandecc hw' -> bch8 on am33xx and 'nandecc hw' -> hamming on omap3 based boards. To enable bch8
There's no nandecc command for am33xx. The plan is that we'll auto-detect when we need BCH16 (as we need BCH8 or BCH16 and the others don't really have found-today use cases).
explicitly on omap3 based devices we have the 'nandecc hw bch8'. I will add another patch to the series changing the interface from omap_nand_switch_ecc(int32) to omap_nand_switch_ecc(bool hw, uint32 eccbits) (or something like this).
OK.
BTW: Has anyone seen that ELM based bch8 on am33xx can not be chosen via nandecc command?
Yes, we only do BCH8 over everything. The ROM talks BCH8 or BCH16 (given what the NAND chip requires) or you tell it (via SYSBOOT pins) tha it's an on-die ECC engine.
I have managed to load kernel from an ubifs written by the kernel driver, but is far away from tested thoroughly.
We used that patchset for a while in-house and could not find obvious issues. However we need to hack the SPL a bit to get the bigger footprint into SRAM with 2013.01.
What exactly did you do?
Well, disabled FAT for this specific build ...
We _should_ already be taking up all of SRAM with a few kb saved off for stack.
We take 8 kB for stack in most configs on omap3, thus having 54 kB for .text and .*data. Unfortunately the HW assisted BCH on omap3 based devices require the bch library to decode ECC, this will grow the .text + .*data sections about 9k in sum (AFAIR, I've measured it some day).
We might be able to get away with less stack, but we'd need to check that a bit with the .su files.
Changing space for stack to 7 kB worked out with all features in SPL (BCH + FAT for my build), this needs some testing though.
Along with some measurements (which are available today in the .su files, see doc/README.SPL) we should be able to do this safely.
- -- Tom

This patch adds the BCH result registers to register mapping for OMAP3 gpmc.
Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com --- since v1: unchanged
arch/arm/include/asm/arch-omap3/cpu.h | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/arm/include/asm/arch-omap3/cpu.h b/arch/arm/include/asm/arch-omap3/cpu.h index 5683e16..c601873 100644 --- a/arch/arm/include/asm/arch-omap3/cpu.h +++ b/arch/arm/include/asm/arch-omap3/cpu.h @@ -109,6 +109,10 @@ struct gpmc_cs { u8 res[8]; /* blow up to 0x30 byte */ };
+struct bch_res_0_3 { + u32 bch_result_x[4]; +}; + struct gpmc { u8 res1[0x10]; u32 sysconfig; /* 0x10 */ @@ -135,6 +139,8 @@ struct gpmc { u32 ecc7_result; /* 0x218 */ u32 ecc8_result; /* 0x21C */ u32 ecc9_result; /* 0x220 */ + u8 res7[0x1C]; /* fill up to 0x240 */ + struct bch_res_0_3 bch_result_0_3[7]; /* 0x240 */ };
/* Used for board specific gpmc initialization */

arch/arm/include/asm/arch-am33xx/omap_gpmc.h and arch/arm/include/asm/arch-omap3/omap_gpmc.h are almost the same, consolidate the common parts into a new header.
Introduce a new asm/omap_gpmc.h which defines the command part and pulls in the architecture specific one.
Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com --- new in v2
arch/arm/cpu/armv7/am33xx/elm.c | 2 +- arch/arm/include/asm/arch-am33xx/omap_gpmc.h | 58 +-------------------- arch/arm/include/asm/arch-omap3/omap_gpmc.h | 57 -------------------- arch/arm/include/asm/{arch-omap3 => }/omap_gpmc.h | 19 +++---- board/isee/igep00x0/igep00x0.c | 2 +- board/overo/overo.c | 2 +- drivers/mtd/nand/omap_gpmc.c | 2 +- 7 files changed, 12 insertions(+), 130 deletions(-) copy arch/arm/include/asm/{arch-omap3 => }/omap_gpmc.h (80%)
diff --git a/arch/arm/cpu/armv7/am33xx/elm.c b/arch/arm/cpu/armv7/am33xx/elm.c index 9eed23d..41df612 100644 --- a/arch/arm/cpu/armv7/am33xx/elm.c +++ b/arch/arm/cpu/armv7/am33xx/elm.c @@ -33,7 +33,7 @@ #include <asm/io.h> #include <asm/errno.h> #include <asm/arch/cpu.h> -#include <asm/arch/omap_gpmc.h> +#include <asm/omap_gpmc.h> #include <asm/arch/elm.h>
#define ELM_DEFAULT_POLY (0) diff --git a/arch/arm/include/asm/arch-am33xx/omap_gpmc.h b/arch/arm/include/asm/arch-am33xx/omap_gpmc.h index 572f9d0..d03f1db 100644 --- a/arch/arm/include/asm/arch-am33xx/omap_gpmc.h +++ b/arch/arm/include/asm/arch-am33xx/omap_gpmc.h @@ -23,63 +23,7 @@ #ifndef __ASM_ARCH_OMAP_GPMC_H #define __ASM_ARCH_OMAP_GPMC_H
-#define GPMC_BUF_EMPTY 0 -#define GPMC_BUF_FULL 1 - -#define ECCCLEAR (0x1 << 8) -#define ECCRESULTREG1 (0x1 << 0) -#define ECCSIZE512BYTE 0xFF -#define ECCSIZE1 (ECCSIZE512BYTE << 22) -#define ECCSIZE0 (ECCSIZE512BYTE << 12) -#define ECCSIZE0SEL (0x000 << 0) - -/* Generic ECC Layouts */ -/* Large Page x8 NAND device Layout */ -#ifdef GPMC_NAND_ECC_LP_x8_LAYOUT -#define GPMC_NAND_HW_ECC_LAYOUT {\ - .eccbytes = 12,\ - .eccpos = {1, 2, 3, 4, 5, 6, 7, 8,\ - 9, 10, 11, 12},\ - .oobfree = {\ - {.offset = 13,\ - .length = 51 } } \ -} -#endif - -/* Large Page x16 NAND device Layout */ -#ifdef GPMC_NAND_ECC_LP_x16_LAYOUT -#define GPMC_NAND_HW_ECC_LAYOUT {\ - .eccbytes = 12,\ - .eccpos = {2, 3, 4, 5, 6, 7, 8, 9,\ - 10, 11, 12, 13},\ - .oobfree = {\ - {.offset = 14,\ - .length = 50 } } \ -} -#endif - -/* Small Page x8 NAND device Layout */ -#ifdef GPMC_NAND_ECC_SP_x8_LAYOUT -#define GPMC_NAND_HW_ECC_LAYOUT {\ - .eccbytes = 3,\ - .eccpos = {1, 2, 3},\ - .oobfree = {\ - {.offset = 4,\ - .length = 12 } } \ -} -#endif - -/* Small Page x16 NAND device Layout */ -#ifdef GPMC_NAND_ECC_SP_x16_LAYOUT -#define GPMC_NAND_HW_ECC_LAYOUT {\ - .eccbytes = 3,\ - .eccpos = {2, 3, 4},\ - .oobfree = {\ - {.offset = 5,\ - .length = 11 } } \ -} -#endif - +/* These GPMC_NAND_HW_BCHx_ECC_LAYOUT defines are based on AM33xx ELM */ #define GPMC_NAND_HW_BCH4_ECC_LAYOUT {\ .eccbytes = 32,\ .eccpos = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\ diff --git a/arch/arm/include/asm/arch-omap3/omap_gpmc.h b/arch/arm/include/asm/arch-omap3/omap_gpmc.h index 800e4ee..13efab0 100644 --- a/arch/arm/include/asm/arch-omap3/omap_gpmc.h +++ b/arch/arm/include/asm/arch-omap3/omap_gpmc.h @@ -23,63 +23,6 @@ #ifndef __ASM_ARCH_OMAP_GPMC_H #define __ASM_ARCH_OMAP_GPMC_H
-#define GPMC_BUF_EMPTY 0 -#define GPMC_BUF_FULL 1 - -#define ECCCLEAR (0x1 << 8) -#define ECCRESULTREG1 (0x1 << 0) -#define ECCSIZE512BYTE 0xFF -#define ECCSIZE1 (ECCSIZE512BYTE << 22) -#define ECCSIZE0 (ECCSIZE512BYTE << 12) -#define ECCSIZE0SEL (0x000 << 0) - -/* Generic ECC Layouts */ -/* Large Page x8 NAND device Layout */ -#ifdef GPMC_NAND_ECC_LP_x8_LAYOUT -#define GPMC_NAND_HW_ECC_LAYOUT {\ - .eccbytes = 12,\ - .eccpos = {1, 2, 3, 4, 5, 6, 7, 8,\ - 9, 10, 11, 12},\ - .oobfree = {\ - {.offset = 13,\ - .length = 51 } } \ -} -#endif - -/* Large Page x16 NAND device Layout */ -#ifdef GPMC_NAND_ECC_LP_x16_LAYOUT -#define GPMC_NAND_HW_ECC_LAYOUT {\ - .eccbytes = 12,\ - .eccpos = {2, 3, 4, 5, 6, 7, 8, 9,\ - 10, 11, 12, 13},\ - .oobfree = {\ - {.offset = 14,\ - .length = 50 } } \ -} -#endif - -/* Small Page x8 NAND device Layout */ -#ifdef GPMC_NAND_ECC_SP_x8_LAYOUT -#define GPMC_NAND_HW_ECC_LAYOUT {\ - .eccbytes = 3,\ - .eccpos = {1, 2, 3},\ - .oobfree = {\ - {.offset = 4,\ - .length = 12 } } \ -} -#endif - -/* Small Page x16 NAND device Layout */ -#ifdef GPMC_NAND_ECC_SP_x16_LAYOUT -#define GPMC_NAND_HW_ECC_LAYOUT {\ - .eccbytes = 3,\ - .eccpos = {2, 3, 4},\ - .oobfree = {\ - {.offset = 5,\ - .length = 11 } } \ -} -#endif - /* GPMC CS configuration for an SMSC LAN9221 ethernet controller */ #define NET_LAN9221_GPMC_CONFIG1 0x00001000 #define NET_LAN9221_GPMC_CONFIG2 0x00060700 diff --git a/arch/arm/include/asm/arch-omap3/omap_gpmc.h b/arch/arm/include/asm/omap_gpmc.h similarity index 80% copy from arch/arm/include/asm/arch-omap3/omap_gpmc.h copy to arch/arm/include/asm/omap_gpmc.h index 800e4ee..5cb1df1 100644 --- a/arch/arm/include/asm/arch-omap3/omap_gpmc.h +++ b/arch/arm/include/asm/omap_gpmc.h @@ -2,6 +2,8 @@ * (C) Copyright 2004-2008 Texas Instruments, <www.ti.com> * Rohit Choraria rohitkc@ti.com * + * (C) Copyright 2013 Andreas Bießmann andreas.devel@googlemail.com + * * See file CREDITS for list of people who contributed to this * project. * @@ -20,8 +22,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ -#ifndef __ASM_ARCH_OMAP_GPMC_H -#define __ASM_ARCH_OMAP_GPMC_H +#ifndef __ASM_OMAP_GPMC_H +#define __ASM_OMAP_GPMC_H + +#include <asm/arch/omap_gpmc.h>
#define GPMC_BUF_EMPTY 0 #define GPMC_BUF_FULL 1 @@ -80,13 +84,4 @@ } #endif
-/* GPMC CS configuration for an SMSC LAN9221 ethernet controller */ -#define NET_LAN9221_GPMC_CONFIG1 0x00001000 -#define NET_LAN9221_GPMC_CONFIG2 0x00060700 -#define NET_LAN9221_GPMC_CONFIG3 0x00020201 -#define NET_LAN9221_GPMC_CONFIG4 0x06000700 -#define NET_LAN9221_GPMC_CONFIG5 0x0006090A -#define NET_LAN9221_GPMC_CONFIG6 0x87030000 -#define NET_LAN9221_GPMC_CONFIG7 0x00000f6c - -#endif /* __ASM_ARCH_OMAP_GPMC_H */ +#endif /* __ASM_OMAP_GPMC_H */ diff --git a/board/isee/igep00x0/igep00x0.c b/board/isee/igep00x0/igep00x0.c index 2bac916..0d4679d 100644 --- a/board/isee/igep00x0/igep00x0.c +++ b/board/isee/igep00x0/igep00x0.c @@ -24,7 +24,7 @@ #include <twl4030.h> #include <netdev.h> #include <asm/gpio.h> -#include <asm/arch/omap_gpmc.h> +#include <asm/omap_gpmc.h> #include <asm/io.h> #include <asm/arch/mem.h> #include <asm/arch/mmc_host_def.h> diff --git a/board/overo/overo.c b/board/overo/overo.c index 8690450..c10c44c 100644 --- a/board/overo/overo.c +++ b/board/overo/overo.c @@ -37,7 +37,7 @@ #include <asm/arch/mux.h> #include <asm/arch/mem.h> #include <asm/arch/sys_proto.h> -#include <asm/arch/omap_gpmc.h> +#include <asm/omap_gpmc.h> #include <asm/gpio.h> #include <asm/mach-types.h> #include "overo.h" diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index bbf5443..c7d4999 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -26,7 +26,7 @@ #include <asm/errno.h> #include <asm/arch/mem.h> #include <asm/arch/cpu.h> -#include <asm/arch/omap_gpmc.h> +#include <asm/omap_gpmc.h> #include <linux/mtd/nand_ecc.h> #include <linux/compiler.h> #include <nand.h>

On Tue, Apr 02, 2013 at 06:05:54PM +0200, Andreas Bie??mann wrote:
arch/arm/include/asm/arch-am33xx/omap_gpmc.h and arch/arm/include/asm/arch-omap3/omap_gpmc.h are almost the same, consolidate the common parts into a new header.
Introduce a new asm/omap_gpmc.h which defines the command part and pulls in the architecture specific one.
Signed-off-by: Andreas Bie??mann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com
Reviewed-by: Tom Rini trini@ti.com

This patch adds BCH8 ooblayout for NAND as provided by 0e618ef0a6a33cf7ef96c2c824402088dd8ef48c in linux kernel. This Layout is currently only provided for 64 byte OOB.
Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com --- since v1: * minor comment changes
arch/arm/include/asm/arch-omap3/omap_gpmc.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/arch/arm/include/asm/arch-omap3/omap_gpmc.h b/arch/arm/include/asm/arch-omap3/omap_gpmc.h index 13efab0..6441281 100644 --- a/arch/arm/include/asm/arch-omap3/omap_gpmc.h +++ b/arch/arm/include/asm/arch-omap3/omap_gpmc.h @@ -23,6 +23,23 @@ #ifndef __ASM_ARCH_OMAP_GPMC_H #define __ASM_ARCH_OMAP_GPMC_H
+/* + * These GPMC_NAND_HW_BCHx_ECC_LAYOUT defines using the BCH library. + * The OOB layout was first defined by linx kernel in commit + * 0e618ef0a6a33cf7ef96c2c824402088dd8ef48c, we have to reuse it here cause + * we want to be compatible. + */ +#define GPMC_NAND_HW_BCH8_ECC_LAYOUT {\ + .eccbytes = 56,\ + .eccpos = {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,\ + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,\ + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,\ + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63},\ + .oobfree = {\ + {.offset = 2,\ + .length = 10 } } \ +} + /* GPMC CS configuration for an SMSC LAN9221 ethernet controller */ #define NET_LAN9221_GPMC_CONFIG1 0x00001000 #define NET_LAN9221_GPMC_CONFIG2 0x00060700

On Tue, Apr 02, 2013 at 06:05:55PM +0200, Andreas Bie??mann wrote:
This patch adds BCH8 ooblayout for NAND as provided by 0e618ef0a6a33cf7ef96c2c824402088dd8ef48c in linux kernel. This Layout is currently only provided for 64 byte OOB.
Signed-off-by: Andreas Bie??mann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com
Reviewed-by: Tom Rini trini@ti.com

With uppcoming BCH support on OMAP devices we need to decide between differnt algorithms when switching the ECC engine. Currently we support 1-bit hammign and 8-bit BCH on HW backend.
In order to switch between differnet ECC algorithms we need to change the interface of omap_nand_switch_ecc() also.
Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com --- new in v2
arch/arm/cpu/armv7/omap3/board.c | 31 ++++++++++---- arch/arm/include/asm/arch-am33xx/sys_proto.h | 2 +- arch/arm/include/asm/arch-omap3/sys_proto.h | 2 +- drivers/mtd/nand/omap_gpmc.c | 56 ++++++++++++++------------ 4 files changed, 55 insertions(+), 36 deletions(-)
diff --git a/arch/arm/cpu/armv7/omap3/board.c b/arch/arm/cpu/armv7/omap3/board.c index c6d9a42..b72fadc 100644 --- a/arch/arm/cpu/armv7/omap3/board.c +++ b/arch/arm/cpu/armv7/omap3/board.c @@ -328,14 +328,25 @@ void abort(void) *****************************************************************************/ static int do_switch_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { - if (argc != 2) + if (argc < 2 || argc > 3) goto usage; - if (strncmp(argv[1], "hw", 2) == 0) - omap_nand_switch_ecc(1); - else if (strncmp(argv[1], "sw", 2) == 0) - omap_nand_switch_ecc(0); - else + + if (strncmp(argv[1], "hw", 2) == 0) { + if (argc == 2) { + omap_nand_switch_ecc(1, 1); + } else { + if (strncmp(argv[2], "hamming", 7) == 0) + omap_nand_switch_ecc(1, 1); + else if (strncmp(argv[2], "bch8", 4) == 0) + omap_nand_switch_ecc(1, 8); + else + goto usage; + } + } else if (strncmp(argv[1], "sw", 2) == 0) { + omap_nand_switch_ecc(0, 0); + } else { goto usage; + }
return 0;
@@ -345,9 +356,13 @@ usage: }
U_BOOT_CMD( - nandecc, 2, 1, do_switch_ecc, + nandecc, 3, 1, do_switch_ecc, "switch OMAP3 NAND ECC calculation algorithm", - "[hw/sw] - Switch between NAND hardware (hw) or software (sw) ecc algorithm" + "hw [hamming|bch8] - Switch between NAND hardware 1-bit hamming and" + " 8-bit BCH\n" + " ecc calculation (second parameter may" + " be omitted).\n" + "nandecc sw - Switch to NAND software ecc algorithm." );
#endif /* CONFIG_NAND_OMAP_GPMC & !CONFIG_SPL_BUILD */ diff --git a/arch/arm/include/asm/arch-am33xx/sys_proto.h b/arch/arm/include/asm/arch-am33xx/sys_proto.h index 0910a94..c913b5f 100644 --- a/arch/arm/include/asm/arch-am33xx/sys_proto.h +++ b/arch/arm/include/asm/arch-am33xx/sys_proto.h @@ -39,5 +39,5 @@ struct gpmc_cs; void gpmc_init(void); void enable_gpmc_cs_config(const u32 *gpmc_config, struct gpmc_cs *cs, u32 base, u32 size); -void omap_nand_switch_ecc(int); +void omap_nand_switch_ecc(uint32_t, uint32_t); #endif diff --git a/arch/arm/include/asm/arch-omap3/sys_proto.h b/arch/arm/include/asm/arch-omap3/sys_proto.h index d60f2ad..dae1312 100644 --- a/arch/arm/include/asm/arch-omap3/sys_proto.h +++ b/arch/arm/include/asm/arch-omap3/sys_proto.h @@ -78,7 +78,7 @@ void sr32(void *, u32, u32, u32); u32 wait_on_value(u32, u32, void *, u32); void sdelay(unsigned long); void make_cs1_contiguous(void); -void omap_nand_switch_ecc(int); +void omap_nand_switch_ecc(uint32_t, uint32_t); void power_init_r(void); void dieid_num_r(void); void do_omap3_emu_romcode_call(u32 service_id, u32 parameters); diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index c7d4999..f073cbe 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -604,13 +604,14 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
#ifndef CONFIG_SPL_BUILD /* - * omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc. - * The default is to come up on s/w ecc - * - * @hardware - 1 -switch to h/w ecc, 0 - s/w ecc + * omap_nand_switch_ecc - switch the ECC operation between different engines + * (h/w and s/w) and different algorithms (hamming and BCHx) * + * @hardware - true if one of the HW engines should be used + * @eccstrength - the number of bits that could be corrected + * (1 - hamming, 4 - BCH4, 8 - BCH8, 16 - BCH16) */ -void omap_nand_switch_ecc(int32_t hardware) +void omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength) { struct nand_chip *nand; struct mtd_info *mtd; @@ -637,28 +638,31 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.calculate = NULL;
/* Setup the ecc configurations again */ - if (hardware == 1) { - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.layout = &hw_nand_oob; - nand->ecc.size = 512; - nand->ecc.bytes = 3; - nand->ecc.hwctl = omap_enable_hwecc; - nand->ecc.correct = omap_correct_data; - nand->ecc.calculate = omap_calculate_ecc; - omap_hwecc_init(nand); - printf("HW ECC selected\n"); + if (hardware) { + if (eccstrength == 1) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_nand_oob; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data; + nand->ecc.calculate = omap_calculate_ecc; + omap_hwecc_init(nand); + printf("1-bit hamming HW ECC selected\n"); + } #ifdef CONFIG_AM33XX - } else if (hardware == 2) { - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.layout = &hw_bch8_nand_oob; - nand->ecc.size = 512; - nand->ecc.bytes = 14; - nand->ecc.read_page = omap_read_page_bch; - nand->ecc.hwctl = omap_enable_ecc_bch; - nand->ecc.correct = omap_correct_data_bch; - nand->ecc.calculate = omap_calculate_ecc_bch; - omap_hwecc_init_bch(nand, NAND_ECC_READ); - printf("HW BCH8 selected\n"); + else if (eccstrength == 8) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_bch8_nand_oob; + nand->ecc.size = 512; + nand->ecc.bytes = 14; + nand->ecc.read_page = omap_read_page_bch; + nand->ecc.hwctl = omap_enable_ecc_bch; + nand->ecc.correct = omap_correct_data_bch; + nand->ecc.calculate = omap_calculate_ecc_bch; + omap_hwecc_init_bch(nand, NAND_ECC_READ); + printf("8-bit BCH HW ECC selected\n"); + } #endif } else { nand->ecc.mode = NAND_ECC_SOFT;

On Tue, Apr 02, 2013 at 06:05:56PM +0200, Andreas Bie??mann wrote:
With uppcoming BCH support on OMAP devices we need to decide between differnt algorithms when switching the ECC engine. Currently we support 1-bit hammign and 8-bit BCH on HW backend.
In order to switch between differnet ECC algorithms we need to change the interface of omap_nand_switch_ecc() also.
Signed-off-by: Andreas Bie??mann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com
I've merged the bool patch, so we should be able to use that:
-void omap_nand_switch_ecc(int32_t hardware) +void omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength)
here, yes?

With uppcoming BCH support on OMAP devices we need to decide between differnt algorithms when switching the ECC engine. Currently we support 1-bit hammign and 8-bit BCH on HW backend.
In order to switch between differnet ECC algorithms we need to change the interface of omap_nand_switch_ecc() also.
Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com --- new in v2
since v2: * use void omap_nand_switch_ecc(bool, uint32_t) * print warning if unknown HW ecc strengs choosen * fix alignment in help test
arch/arm/cpu/armv7/omap3/board.c | 31 +++++++++---- arch/arm/include/asm/arch-am33xx/sys_proto.h | 2 +- arch/arm/include/asm/arch-omap3/sys_proto.h | 2 +- drivers/mtd/nand/omap_gpmc.c | 61 ++++++++++++++------------ 4 files changed, 59 insertions(+), 37 deletions(-)
diff --git a/arch/arm/cpu/armv7/omap3/board.c b/arch/arm/cpu/armv7/omap3/board.c index c6d9a42..0150150 100644 --- a/arch/arm/cpu/armv7/omap3/board.c +++ b/arch/arm/cpu/armv7/omap3/board.c @@ -328,14 +328,25 @@ void abort(void) *****************************************************************************/ static int do_switch_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { - if (argc != 2) + if (argc < 2 || argc > 3) goto usage; - if (strncmp(argv[1], "hw", 2) == 0) - omap_nand_switch_ecc(1); - else if (strncmp(argv[1], "sw", 2) == 0) - omap_nand_switch_ecc(0); - else + + if (strncmp(argv[1], "hw", 2) == 0) { + if (argc == 2) { + omap_nand_switch_ecc(true, 1); + } else { + if (strncmp(argv[2], "hamming", 7) == 0) + omap_nand_switch_ecc(true, 1); + else if (strncmp(argv[2], "bch8", 4) == 0) + omap_nand_switch_ecc(true, 8); + else + goto usage; + } + } else if (strncmp(argv[1], "sw", 2) == 0) { + omap_nand_switch_ecc(false, 0); + } else { goto usage; + }
return 0;
@@ -345,9 +356,13 @@ usage: }
U_BOOT_CMD( - nandecc, 2, 1, do_switch_ecc, + nandecc, 3, 1, do_switch_ecc, "switch OMAP3 NAND ECC calculation algorithm", - "[hw/sw] - Switch between NAND hardware (hw) or software (sw) ecc algorithm" + "hw [hamming|bch8] - Switch between NAND hardware 1-bit hamming and" + " 8-bit BCH\n" + " ecc calculation (second parameter may" + " be omitted).\n" + "nandecc sw - Switch to NAND software ecc algorithm." );
#endif /* CONFIG_NAND_OMAP_GPMC & !CONFIG_SPL_BUILD */ diff --git a/arch/arm/include/asm/arch-am33xx/sys_proto.h b/arch/arm/include/asm/arch-am33xx/sys_proto.h index 0910a94..23a3049 100644 --- a/arch/arm/include/asm/arch-am33xx/sys_proto.h +++ b/arch/arm/include/asm/arch-am33xx/sys_proto.h @@ -39,5 +39,5 @@ struct gpmc_cs; void gpmc_init(void); void enable_gpmc_cs_config(const u32 *gpmc_config, struct gpmc_cs *cs, u32 base, u32 size); -void omap_nand_switch_ecc(int); +void omap_nand_switch_ecc(bool, uint32_t); #endif diff --git a/arch/arm/include/asm/arch-omap3/sys_proto.h b/arch/arm/include/asm/arch-omap3/sys_proto.h index d60f2ad..4ca4e73 100644 --- a/arch/arm/include/asm/arch-omap3/sys_proto.h +++ b/arch/arm/include/asm/arch-omap3/sys_proto.h @@ -78,7 +78,7 @@ void sr32(void *, u32, u32, u32); u32 wait_on_value(u32, u32, void *, u32); void sdelay(unsigned long); void make_cs1_contiguous(void); -void omap_nand_switch_ecc(int); +void omap_nand_switch_ecc(bool, uint32_t); void power_init_r(void); void dieid_num_r(void); void do_omap3_emu_romcode_call(u32 service_id, u32 parameters); diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index c7d4999..0647828 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -604,13 +604,14 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
#ifndef CONFIG_SPL_BUILD /* - * omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc. - * The default is to come up on s/w ecc - * - * @hardware - 1 -switch to h/w ecc, 0 - s/w ecc + * omap_nand_switch_ecc - switch the ECC operation between different engines + * (h/w and s/w) and different algorithms (hamming and BCHx) * + * @hardware - true if one of the HW engines should be used + * @eccstrength - the number of bits that could be corrected + * (1 - hamming, 4 - BCH4, 8 - BCH8, 16 - BCH16) */ -void omap_nand_switch_ecc(int32_t hardware) +void omap_nand_switch_ecc(bool hardware, uint32_t eccstrength) { struct nand_chip *nand; struct mtd_info *mtd; @@ -637,35 +638,41 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.calculate = NULL;
/* Setup the ecc configurations again */ - if (hardware == 1) { - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.layout = &hw_nand_oob; - nand->ecc.size = 512; - nand->ecc.bytes = 3; - nand->ecc.hwctl = omap_enable_hwecc; - nand->ecc.correct = omap_correct_data; - nand->ecc.calculate = omap_calculate_ecc; - omap_hwecc_init(nand); - printf("HW ECC selected\n"); + if (hardware) { + if (eccstrength == 1) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_nand_oob; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data; + nand->ecc.calculate = omap_calculate_ecc; + omap_hwecc_init(nand); + puts("1-bit hamming HW ECC selected\n"); + } #ifdef CONFIG_AM33XX - } else if (hardware == 2) { - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.layout = &hw_bch8_nand_oob; - nand->ecc.size = 512; - nand->ecc.bytes = 14; - nand->ecc.read_page = omap_read_page_bch; - nand->ecc.hwctl = omap_enable_ecc_bch; - nand->ecc.correct = omap_correct_data_bch; - nand->ecc.calculate = omap_calculate_ecc_bch; - omap_hwecc_init_bch(nand, NAND_ECC_READ); - printf("HW BCH8 selected\n"); + else if (eccstrength == 8) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_bch8_nand_oob; + nand->ecc.size = 512; + nand->ecc.bytes = 14; + nand->ecc.read_page = omap_read_page_bch; + nand->ecc.hwctl = omap_enable_ecc_bch; + nand->ecc.correct = omap_correct_data_bch; + nand->ecc.calculate = omap_calculate_ecc_bch; + omap_hwecc_init_bch(nand, NAND_ECC_READ); + puts("8-bit BCH HW ECC selected\n"); + } #endif + else { + puts("Unsupported HW ECC algorithm\n"); + } } else { nand->ecc.mode = NAND_ECC_SOFT; /* Use mtd default settings */ nand->ecc.layout = NULL; nand->ecc.size = 0; - printf("SW ECC selected\n"); + puts("SW ECC selected\n"); }
/* Update NAND handling after ECC mode switch */

The kernel states:
---8<--- The OMAP3 GPMC hardware BCH engine computes remainder polynomials, it does not provide automatic error location and correction: this step is implemented using the BCH library. --->8---
And we do so in u-boot.
This implementation uses the same layout for BCH8 but it is fix. The current provided layout does only work with 64 Byte OOB.
Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com Cc: Mansoor Ahamed mansoor.ahamed@ti.com --- since v1: * cleanups (remove debug stuff) * make checkpach clean (still 2 warnings which I will not fix) * merge some code with the AM33XX implementation
since v2: * fix all checkpatch warnings * add more comments * add NAND section in README.omap3
doc/README.omap3 | 19 +++ drivers/mtd/nand/omap_gpmc.c | 367 +++++++++++++++++++++++++++++++----------- lib/Makefile | 2 +- 3 files changed, 296 insertions(+), 92 deletions(-)
diff --git a/doc/README.omap3 b/doc/README.omap3 index 0a37de0..56aca8e 100644 --- a/doc/README.omap3 +++ b/doc/README.omap3 @@ -145,6 +145,25 @@ int omap3_dma_wait_for_transfer(uint32_t chan) int omap3_dma_get_revision(uint32_t *minor, uint32_t *major) Read silicon Revision of the DMA module
+NAND +==== + +Ther eare some OMAP3 devices out ther with NAND attached. Due to the fact that +OMAP3 ROM code can only handle 1-bit hamming ECC for accessing first page +(place where SPL lives) we require this setup for u-boot at least when reading +the second progam within SPL. A lot of newer NAND chips however require more +than 1-bit ECC for the pages, some can live with 1-bit for the first page. To +handle this we can switch to another ECC algorithm after reading the payload +within SPL. + +BCH8 +---- + +To enable hardware assisted BCH8 (8-bit BCH [Bose, Chaudhuri, Hocquenghem]) on +OMAP3 devices we can use the BCH library in lib/bch.c. To do so add CONFIG_BCH +to enable the library and CONFIG_NAND_OMAP_BCH8 to your board config. +The NAND OOB layout is the same as in linux kernel, if the linux kernel BCH8 +implementation for OMAP3 works for you so the u-boot version should also.
Acknowledgements ================ diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 0647828..de41e46 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -28,6 +28,7 @@ #include <asm/arch/cpu.h> #include <asm/omap_gpmc.h> #include <linux/mtd/nand_ecc.h> +#include <linux/bch.h> #include <linux/compiler.h> #include <nand.h> #ifdef CONFIG_AM33XX @@ -37,6 +38,8 @@ static uint8_t cs; static __maybe_unused struct nand_ecclayout hw_nand_oob = GPMC_NAND_HW_ECC_LAYOUT; +static __maybe_unused struct nand_ecclayout hw_bch8_nand_oob = + GPMC_NAND_HW_BCH8_ECC_LAYOUT;
/* * omap_nand_hwcontrol - Set the address pointers corretly for the @@ -239,13 +242,13 @@ static void __maybe_unused omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) }
/* - * BCH8 support (needs ELM and thus AM33xx-only) + * Generic BCH interface */ -#ifdef CONFIG_AM33XX struct nand_bch_priv { uint8_t mode; uint8_t type; uint8_t nibbles; + struct bch_control *control; };
/* bch types */ @@ -253,21 +256,146 @@ struct nand_bch_priv { #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
-static struct nand_ecclayout hw_bch8_nand_oob = GPMC_NAND_HW_BCH8_ECC_LAYOUT; - -static struct nand_bch_priv bch_priv = { +/* + * 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 + * library). + * 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 + .nibbles = ECC_BCH8_NIBBLES, + .control = NULL };
/* + * omap_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in + * GPMC controller + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +__maybe_unused +static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode) +{ + uint32_t val; + uint32_t dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1; +#ifdef CONFIG_AM33XX + uint32_t unused_length = 0; +#endif + uint32_t wr_mode = BCH_WRAPMODE_6; + struct nand_bch_priv *bch = chip->priv; + + /* Clear the ecc result registers, select ecc reg as 1 */ + writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); + +#ifdef CONFIG_AM33XX + wr_mode = BCH_WRAPMODE_1; + + switch (bch->nibbles) { + case ECC_BCH4_NIBBLES: + unused_length = 3; + break; + case ECC_BCH8_NIBBLES: + unused_length = 2; + break; + case ECC_BCH16_NIBBLES: + unused_length = 0; + break; + } + + /* + * This is ecc_size_config for ELM mode. + * Here we are using different settings for read and write access and + * also depending on BCH strength. + */ + switch (mode) { + case NAND_ECC_WRITE: + /* write access only setup eccsize1 config */ + val = ((unused_length + bch->nibbles) << 22); + break; + + case NAND_ECC_READ: + default: + /* + * by default eccsize0 selected for ecc1resultsize + * eccsize0 config. + */ + val = (bch->nibbles << 12); + /* eccsize1 config */ + val |= (unused_length << 22); + break; + } +#else + /* + * This ecc_size_config setting is for BCH sw library. + * + * Note: we only support BCH8 currently with BCH sw library! + * Should be really easy to adobt to BCH4, however some omap3 have + * flaws with BCH4. + * + * Here we are using wrapping mode 6 both for reading and writing, with: + * size0 = 0 (no additional protected byte in spare area) + * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) + */ + val = (32 << 22) | (0 << 12); +#endif + /* ecc size configuration */ + writel(val, &gpmc_cfg->ecc_size_config); + + /* + * Configure the ecc engine in gpmc + * We assume 512 Byte sector pages for access to NAND. + */ + val = (1 << 16); /* enable BCH mode */ + val |= (bch->type << 12); /* setup BCH type */ + val |= (wr_mode << 8); /* setup wrapping mode */ + val |= (dev_width << 7); /* setup device width (16 or 8 bit) */ + val |= (cs << 1); /* setup chip select to work on */ + debug("set ECC_CONFIG=0x%08x\n", val); + writel(val, &gpmc_cfg->ecc_config); +} + +/* + * omap_enable_ecc_bch - This function enables the bch h/w ecc functionality + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +__maybe_unused +static void omap_enable_ecc_bch(struct mtd_info *mtd, int32_t mode) +{ + struct nand_chip *chip = mtd->priv; + + omap_hwecc_init_bch(chip, mode); + /* enable ecc */ + writel((readl(&gpmc_cfg->ecc_config) | 0x1), &gpmc_cfg->ecc_config); +} + +/* + * omap_ecc_disable - Disable H/W ECC calculation + * + * @mtd: MTD device structure + */ +static void __maybe_unused omap_ecc_disable(struct mtd_info *mtd) +{ + writel((readl(&gpmc_cfg->ecc_config) & ~0x1), &gpmc_cfg->ecc_config); +} + +/* + * BCH8 support (needs ELM and thus AM33xx-only) + */ +#ifdef CONFIG_AM33XX +/* * omap_read_bch8_result - Read BCH result for BCH8 level * * @mtd: MTD device structure @@ -306,18 +434,6 @@ static void omap_read_bch8_result(struct mtd_info *mtd, uint8_t big_endian, }
/* - * omap_ecc_disable - Disable H/W ECC calculation - * - * @mtd: MTD device structure - * - */ -static void omap_ecc_disable(struct mtd_info *mtd) -{ - writel((readl(&gpmc_cfg->ecc_config) & ~0x1), - &gpmc_cfg->ecc_config); -} - -/* * omap_rotate_ecc_bch - Rotate the syndrome bytes * * @mtd: MTD device structure @@ -468,76 +584,6 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat,
return 0; } -/* - * omap_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in - * GPMC controller - * @mtd: MTD device structure - * @mode: Read/Write mode - */ -static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode) -{ - uint32_t val, dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1; - uint32_t unused_length = 0; - struct nand_bch_priv *bch = chip->priv; - - switch (bch->nibbles) { - case ECC_BCH4_NIBBLES: - unused_length = 3; - break; - case ECC_BCH8_NIBBLES: - unused_length = 2; - break; - case ECC_BCH16_NIBBLES: - unused_length = 0; - break; - } - - /* Clear the ecc result registers, select ecc reg as 1 */ - writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); - - switch (mode) { - case NAND_ECC_WRITE: - /* eccsize1 config */ - val = ((unused_length + bch->nibbles) << 22); - break; - - case NAND_ECC_READ: - default: - /* by default eccsize0 selected for ecc1resultsize */ - /* eccsize0 config */ - val = (bch->nibbles << 12); - /* eccsize1 config */ - val |= (unused_length << 22); - break; - } - /* ecc size configuration */ - writel(val, &gpmc_cfg->ecc_size_config); - /* by default 512bytes sector page is selected */ - /* set bch mode */ - val = (1 << 16); - /* bch4 / bch8 / bch16 */ - val |= (bch->type << 12); - /* set wrap mode to 1 */ - val |= (1 << 8); - val |= (dev_width << 7); - val |= (cs << 1); - writel(val, &gpmc_cfg->ecc_config); -} - -/* - * omap_enable_ecc_bch- This function enables the bch h/w ecc functionality - * @mtd: MTD device structure - * @mode: Read/Write mode - * - */ -static void omap_enable_ecc_bch(struct mtd_info *mtd, int32_t mode) -{ - struct nand_chip *chip = mtd->priv; - - omap_hwecc_init_bch(chip, mode); - /* enable ecc */ - writel((readl(&gpmc_cfg->ecc_config) | 0x1), &gpmc_cfg->ecc_config); -}
/** * omap_read_page_bch - hardware ecc based page read function @@ -602,6 +648,127 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, } #endif /* CONFIG_AM33XX */
+/* + * OMAP3 BCH8 support (with BCH library) + */ +#ifdef CONFIG_NAND_OMAP_BCH8 +/* + * omap_calculate_ecc_bch - Read BCH ECC result + * + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed (unused here) + * @ecc: The ECC output buffer + */ +static int omap_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc) +{ + int ret = 0; + size_t i; + unsigned long nsectors, val1, val2, val3, val4; + + nsectors = ((readl(&gpmc_cfg->ecc_config) >> 4) & 0x7) + 1; + + for (i = 0; i < nsectors; i++) { + /* Read hw-computed remainder */ + val1 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[0]); + val2 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[1]); + val3 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[2]); + val4 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[3]); + + /* + * Add constant polynomial to remainder, in order to get an ecc + * sequence of 0xFFs for a buffer filled with 0xFFs. + */ + *ecc++ = 0xef ^ (val4 & 0xFF); + *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF); + *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF); + *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF); + *ecc++ = 0xed ^ (val3 & 0xFF); + *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF); + *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF); + *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF); + *ecc++ = 0x97 ^ (val2 & 0xFF); + *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF); + *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF); + *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF); + *ecc++ = 0xb5 ^ (val1 & 0xFF); + } + + /* + * Stop reading anymore ECC vals and clear old results + * enable will be called if more reads are required + */ + omap_ecc_disable(mtd); + + return ret; +} + +/** + * omap_correct_data_bch - Decode received data and correct errors + * @mtd: MTD device structure + * @data: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + */ +static int omap_correct_data_bch(struct mtd_info *mtd, u_char *data, + u_char *read_ecc, u_char *calc_ecc) +{ + int i, count; + /* cannot correct more than 8 errors */ + unsigned int errloc[8]; + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *chip_priv = chip->priv; + struct bch_control *bch = chip_priv->control; + + count = decode_bch(bch, NULL, 512, read_ecc, calc_ecc, NULL, errloc); + if (count > 0) { + /* correct errors */ + for (i = 0; i < count; i++) { + /* correct data only, not ecc bytes */ + if (errloc[i] < 8*512) + data[errloc[i]/8] ^= 1 << (errloc[i] & 7); + printf("corrected bitflip %u\n", errloc[i]); +#ifdef DEBUG + puts("read_ecc: "); + /* + * BCH8 have 13 bytes of ECC; BCH4 needs adoption + * here! + */ + for (i = 0; i < 13; i++) + printf("%02x ", read_ecc[i]); + puts("\n"); + puts("calc_ecc: "); + for (i = 0; i < 13; i++) + printf("%02x ", calc_ecc[i]); + puts("\n"); +#endif + } + } else if (count < 0) { + puts("ecc unrecoverable error\n"); + } + return count; +} + +/** + * omap_free_bch - Release BCH ecc resources + * @mtd: MTD device structure + */ +static void __maybe_unused omap_free_bch(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *chip_priv = chip->priv; + struct bch_control *bch = NULL; + + if (chip_priv) + bch = chip_priv->control; + + if (bch) { + free_bch(bch); + chip_priv->control = NULL; + } +} +#endif /* CONFIG_NAND_OMAP_BCH8 */ + #ifndef CONFIG_SPL_BUILD /* * omap_nand_switch_ecc - switch the ECC operation between different engines @@ -650,13 +817,17 @@ void omap_nand_switch_ecc(bool hardware, uint32_t eccstrength) omap_hwecc_init(nand); puts("1-bit hamming HW ECC selected\n"); } -#ifdef CONFIG_AM33XX +#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) else if (eccstrength == 8) { nand->ecc.mode = NAND_ECC_HW; nand->ecc.layout = &hw_bch8_nand_oob; nand->ecc.size = 512; +#ifdef CONFIG_AM33XX nand->ecc.bytes = 14; nand->ecc.read_page = omap_read_page_bch; +#else + nand->ecc.bytes = 13; +#endif nand->ecc.hwctl = omap_enable_ecc_bch; nand->ecc.correct = omap_correct_data_bch; nand->ecc.calculate = omap_calculate_ecc_bch; @@ -739,16 +910,28 @@ int board_nand_init(struct nand_chip *nand)
nand->chip_delay = 100;
+#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) #ifdef CONFIG_AM33XX + /* AM33xx uses the ELM */ /* required in case of BCH */ elm_init(); - +#else + /* + * Whereas other OMAP based SoC do not have the ELM, they use the BCH + * SW library. + */ + bch_priv.control = init_bch(13, 8, 0x201b /* hw polynominal */); + if (!bch_priv.control) { + puts("Could not init_bch()\n"); + return -ENODEV; + } +#endif /* BCH info that will be correct for SPL or overridden otherwise. */ nand->priv = &bch_priv; #endif
/* Default ECC mode */ -#ifdef CONFIG_AM33XX +#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) nand->ecc.mode = NAND_ECC_HW; nand->ecc.layout = &hw_bch8_nand_oob; nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; @@ -756,7 +939,9 @@ int board_nand_init(struct nand_chip *nand) nand->ecc.hwctl = omap_enable_ecc_bch; nand->ecc.correct = omap_correct_data_bch; nand->ecc.calculate = omap_calculate_ecc_bch; +#ifdef CONFIG_AM33XX nand->ecc.read_page = omap_read_page_bch; +#endif omap_hwecc_init_bch(nand, NAND_ECC_READ); #else #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_NAND_SOFTECC) diff --git a/lib/Makefile b/lib/Makefile index 1bfd3ee..5d71b80 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -27,7 +27,6 @@ LIB = $(obj)libgeneric.o
ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_ADDR_MAP) += addr_map.o -COBJS-$(CONFIG_BCH) += bch.o COBJS-$(CONFIG_AES) += aes.o COBJS-$(CONFIG_BZIP2) += bzlib.o COBJS-$(CONFIG_BZIP2) += bzlib_crctable.o @@ -67,6 +66,7 @@ COBJS-$(CONFIG_SPL_NET_SUPPORT) += errno.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += hashtable.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += net_utils.o endif +COBJS-$(CONFIG_BCH) += bch.o COBJS-y += crc32.o COBJS-y += ctype.o COBJS-y += div64.o

Hello Andreas,
I have the following problem when switching from nandecc hw hamming to nandecc hw bch8.
Thomas
-------8<---------- U-Boot 2013.04-rc2-00006-g9abd2ec (Apr 05 2013 - 09:58:48)
OMAP3530-GP ES3.1, CPU-OPP2, L3-165MHz, Max CPU Clock 600 mHz OMAP3 DevKit8000 + LPDDR/NAND I2C: ready DRAM: 128 MiB NAND: 128 MiB MMC: OMAP SD/MMC: 0 *** Warning - bad CRC, using default environment
In: serial Out: serial Err: serial ethaddr not set, using Die ID Die ID #1d82000400000000040365f90100b017 Net: dm9000 Hit any key to stop autoboot: 0 OMAP3 DevKit8000 # nandecc hw bch8 Unsupported HW ECC algorithm OMAP3 DevKit8000 # nandecc hw bch8 Unsupported HW ECC algorithm OMAP3 DevKit8000 # nandecc hw hamming 1-bit hamming HW ECC selected OMAP3 DevKit8000 # nandecc sw SW ECC selected OMAP3 DevKit8000 # nandecc hw bch8 Unsupported HW ECC algorithm OMAP3 DevKit8000 # nandecc hw hamming 1-bit hamming HW ECC selected OMAP3 DevKit8000 # nandecc hw bch8 Unsupported HW ECC algorithm No ECC functions supplied; Hardware ECC not possible BUG: failure at nand_base.c:3014/nand_scan_tail()! BUG! resetting ... ----------->8--------------

Dear Thomas Weber,
On 04/05/2013 10:13 AM, Thomas Weber wrote:
Hello Andreas,
I have the following problem when switching from nandecc hw hamming to nandecc hw bch8.
Thomas
-------8<---------- U-Boot 2013.04-rc2-00006-g9abd2ec (Apr 05 2013 - 09:58:48)
OMAP3530-GP ES3.1, CPU-OPP2, L3-165MHz, Max CPU Clock 600 mHz OMAP3 DevKit8000 + LPDDR/NAND I2C: ready DRAM: 128 MiB NAND: 128 MiB MMC: OMAP SD/MMC: 0 *** Warning - bad CRC, using default environment
In: serial Out: serial Err: serial ethaddr not set, using Die ID Die ID #1d82000400000000040365f90100b017 Net: dm9000 Hit any key to stop autoboot: 0 OMAP3 DevKit8000 # nandecc hw bch8 Unsupported HW ECC algorithm OMAP3 DevKit8000 # nandecc hw bch8 Unsupported HW ECC algorithm OMAP3 DevKit8000 # nandecc hw hamming 1-bit hamming HW ECC selected OMAP3 DevKit8000 # nandecc sw SW ECC selected OMAP3 DevKit8000 # nandecc hw bch8 Unsupported HW ECC algorithm OMAP3 DevKit8000 # nandecc hw hamming 1-bit hamming HW ECC selected OMAP3 DevKit8000 # nandecc hw bch8 Unsupported HW ECC algorithm No ECC functions supplied; Hardware ECC not possible BUG: failure at nand_base.c:3014/nand_scan_tail()! BUG!
You are right. I'v never tested the nandecc changes without the new CONFIG_NAND_OMAP_BCH8 config parameter, many thanks to you!
I've a fix for that and will repost v4 ASAP.
Best regards
Andreas Bießmann

Hello Andreas,
On 04/03/2013 04:50 PM, Andreas Bießmann wrote:
The kernel states:
---8<--- The OMAP3 GPMC hardware BCH engine computes remainder polynomials, it does not provide automatic error location and correction: this step is implemented using the BCH library. --->8---
And we do so in u-boot.
This implementation uses the same layout for BCH8 but it is fix. The current provided layout does only work with 64 Byte OOB.
Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com Cc: Mansoor Ahamed mansoor.ahamed@ti.com
since v1:
- cleanups (remove debug stuff)
- make checkpach clean (still 2 warnings which I will not fix)
- merge some code with the AM33XX implementation
since v2:
- fix all checkpatch warnings
- add more comments
- add NAND section in README.omap3
doc/README.omap3 | 19 +++ drivers/mtd/nand/omap_gpmc.c | 367 +++++++++++++++++++++++++++++++----------- lib/Makefile | 2 +- 3 files changed, 296 insertions(+), 92 deletions(-)
diff --git a/doc/README.omap3 b/doc/README.omap3 index 0a37de0..56aca8e 100644 --- a/doc/README.omap3 +++ b/doc/README.omap3 @@ -145,6 +145,25 @@ int omap3_dma_wait_for_transfer(uint32_t chan) int omap3_dma_get_revision(uint32_t *minor, uint32_t *major) Read silicon Revision of the DMA module
+NAND +====
+Ther eare some OMAP3 devices out ther with NAND attached. Due to the fact that
ther -> there
+OMAP3 ROM code can only handle 1-bit hamming ECC for accessing first page +(place where SPL lives) we require this setup for u-boot at least when reading +the second progam within SPL. A lot of newer NAND chips however require more +than 1-bit ECC for the pages, some can live with 1-bit for the first page. To +handle this we can switch to another ECC algorithm after reading the payload +within SPL.
+BCH8 +----
+To enable hardware assisted BCH8 (8-bit BCH [Bose, Chaudhuri, Hocquenghem]) on +OMAP3 devices we can use the BCH library in lib/bch.c. To do so add CONFIG_BCH +to enable the library and CONFIG_NAND_OMAP_BCH8 to your board config. +The NAND OOB layout is the same as in linux kernel, if the linux kernel BCH8 +implementation for OMAP3 works for you so the u-boot version should also.
Acknowledgements
Thomas

Dear Thomas Weber,
On 04/05/2013 01:44 PM, Thomas Weber wrote:
Hello Andreas,
On 04/03/2013 04:50 PM, Andreas Bießmann wrote:
<snip>
diff --git a/doc/README.omap3 b/doc/README.omap3 index 0a37de0..56aca8e 100644 --- a/doc/README.omap3 +++ b/doc/README.omap3 @@ -145,6 +145,25 @@ int omap3_dma_wait_for_transfer(uint32_t chan) int omap3_dma_get_revision(uint32_t *minor, uint32_t *major) Read silicon Revision of the DMA module
+NAND +====
+Ther eare some OMAP3 devices out ther with NAND attached. Due to the fact that
ther -> there
thank you for the hint, will fix. Are you trying this on devkit8000? Can you share your experience with us and maybe send a Tested-by?
Best regards
Andreas Bießmann

The kernel states:
---8<--- The OMAP3 GPMC hardware BCH engine computes remainder polynomials, it does not provide automatic error location and correction: this step is implemented using the BCH library. --->8---
And we do so in u-boot.
This implementation uses the same layout for BCH8 but it is fix. The current provided layout does only work with 64 Byte OOB.
Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com Cc: Mansoor Ahamed mansoor.ahamed@ti.com Cc: Thomas Weber thomas.weber.linux@googlemail.com --- since v1: * cleanups (remove debug stuff) * make checkpach clean (still 2 warnings which I will not fix) * merge some code with the AM33XX implementation
since v2: * fix all checkpatch warnings * add more comments * add NAND section in README.omap3
since v3: * fix typos in README (thanks to Thoams Weber) * append required parameters for BCH8 in SPL
doc/README.omap3 | 28 ++++ drivers/mtd/nand/omap_gpmc.c | 367 +++++++++++++++++++++++++++++++----------- lib/Makefile | 2 +- 3 files changed, 305 insertions(+), 92 deletions(-)
diff --git a/doc/README.omap3 b/doc/README.omap3 index 0a37de0..1fbe79d 100644 --- a/doc/README.omap3 +++ b/doc/README.omap3 @@ -145,6 +145,34 @@ int omap3_dma_wait_for_transfer(uint32_t chan) int omap3_dma_get_revision(uint32_t *minor, uint32_t *major) Read silicon Revision of the DMA module
+NAND +==== + +There are some OMAP3 devices out there with NAND attached. Due to the fact that +OMAP3 ROM code can only handle 1-bit hamming ECC for accessing first page +(place where SPL lives) we require this setup for u-boot at least when reading +the second progam within SPL. A lot of newer NAND chips however require more +than 1-bit ECC for the pages, some can live with 1-bit for the first page. To +handle this we can switch to another ECC algorithm after reading the payload +within SPL. + +BCH8 +---- + +To enable hardware assisted BCH8 (8-bit BCH [Bose, Chaudhuri, Hocquenghem]) on +OMAP3 devices we can use the BCH library in lib/bch.c. To do so add CONFIG_BCH +to enable the library and CONFIG_NAND_OMAP_BCH8 to to enable hardware assisted +syndrom generation to your board config. +The NAND OOB layout is the same as in linux kernel, if the linux kernel BCH8 +implementation for OMAP3 works for you so the u-boot version should also. +When you require the SPL to read with BCH8 there are two more configs to +change: + + * CONFIG_SYS_NAND_ECCPOS (must be the same as .eccpos in + GPMC_NAND_HW_BCH8_ECC_LAYOUT defined in + arch/arm/include/asm/arch-omap3/omap_gpmc.h) + * CONFIG_SYS_NAND_ECCSIZE must be 512 + * CONFIG_SYS_NAND_ECCBYTES must be 13 for this BCH8 setup
Acknowledgements ================ diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 3468c78..bc1bcad 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -28,6 +28,7 @@ #include <asm/arch/cpu.h> #include <asm/omap_gpmc.h> #include <linux/mtd/nand_ecc.h> +#include <linux/bch.h> #include <linux/compiler.h> #include <nand.h> #ifdef CONFIG_AM33XX @@ -37,6 +38,8 @@ static uint8_t cs; static __maybe_unused struct nand_ecclayout hw_nand_oob = GPMC_NAND_HW_ECC_LAYOUT; +static __maybe_unused struct nand_ecclayout hw_bch8_nand_oob = + GPMC_NAND_HW_BCH8_ECC_LAYOUT;
/* * omap_nand_hwcontrol - Set the address pointers corretly for the @@ -239,13 +242,13 @@ static void __maybe_unused omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) }
/* - * BCH8 support (needs ELM and thus AM33xx-only) + * Generic BCH interface */ -#ifdef CONFIG_AM33XX struct nand_bch_priv { uint8_t mode; uint8_t type; uint8_t nibbles; + struct bch_control *control; };
/* bch types */ @@ -253,21 +256,146 @@ struct nand_bch_priv { #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
-static struct nand_ecclayout hw_bch8_nand_oob = GPMC_NAND_HW_BCH8_ECC_LAYOUT; - -static struct nand_bch_priv bch_priv = { +/* + * 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 + * library). + * 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 + .nibbles = ECC_BCH8_NIBBLES, + .control = NULL };
/* + * omap_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in + * GPMC controller + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +__maybe_unused +static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode) +{ + uint32_t val; + uint32_t dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1; +#ifdef CONFIG_AM33XX + uint32_t unused_length = 0; +#endif + uint32_t wr_mode = BCH_WRAPMODE_6; + struct nand_bch_priv *bch = chip->priv; + + /* Clear the ecc result registers, select ecc reg as 1 */ + writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); + +#ifdef CONFIG_AM33XX + wr_mode = BCH_WRAPMODE_1; + + switch (bch->nibbles) { + case ECC_BCH4_NIBBLES: + unused_length = 3; + break; + case ECC_BCH8_NIBBLES: + unused_length = 2; + break; + case ECC_BCH16_NIBBLES: + unused_length = 0; + break; + } + + /* + * This is ecc_size_config for ELM mode. + * Here we are using different settings for read and write access and + * also depending on BCH strength. + */ + switch (mode) { + case NAND_ECC_WRITE: + /* write access only setup eccsize1 config */ + val = ((unused_length + bch->nibbles) << 22); + break; + + case NAND_ECC_READ: + default: + /* + * by default eccsize0 selected for ecc1resultsize + * eccsize0 config. + */ + val = (bch->nibbles << 12); + /* eccsize1 config */ + val |= (unused_length << 22); + break; + } +#else + /* + * This ecc_size_config setting is for BCH sw library. + * + * Note: we only support BCH8 currently with BCH sw library! + * Should be really easy to adobt to BCH4, however some omap3 have + * flaws with BCH4. + * + * Here we are using wrapping mode 6 both for reading and writing, with: + * size0 = 0 (no additional protected byte in spare area) + * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) + */ + val = (32 << 22) | (0 << 12); +#endif + /* ecc size configuration */ + writel(val, &gpmc_cfg->ecc_size_config); + + /* + * Configure the ecc engine in gpmc + * We assume 512 Byte sector pages for access to NAND. + */ + val = (1 << 16); /* enable BCH mode */ + val |= (bch->type << 12); /* setup BCH type */ + val |= (wr_mode << 8); /* setup wrapping mode */ + val |= (dev_width << 7); /* setup device width (16 or 8 bit) */ + val |= (cs << 1); /* setup chip select to work on */ + debug("set ECC_CONFIG=0x%08x\n", val); + writel(val, &gpmc_cfg->ecc_config); +} + +/* + * omap_enable_ecc_bch - This function enables the bch h/w ecc functionality + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +__maybe_unused +static void omap_enable_ecc_bch(struct mtd_info *mtd, int32_t mode) +{ + struct nand_chip *chip = mtd->priv; + + omap_hwecc_init_bch(chip, mode); + /* enable ecc */ + writel((readl(&gpmc_cfg->ecc_config) | 0x1), &gpmc_cfg->ecc_config); +} + +/* + * omap_ecc_disable - Disable H/W ECC calculation + * + * @mtd: MTD device structure + */ +static void __maybe_unused omap_ecc_disable(struct mtd_info *mtd) +{ + writel((readl(&gpmc_cfg->ecc_config) & ~0x1), &gpmc_cfg->ecc_config); +} + +/* + * BCH8 support (needs ELM and thus AM33xx-only) + */ +#ifdef CONFIG_AM33XX +/* * omap_read_bch8_result - Read BCH result for BCH8 level * * @mtd: MTD device structure @@ -306,18 +434,6 @@ static void omap_read_bch8_result(struct mtd_info *mtd, uint8_t big_endian, }
/* - * omap_ecc_disable - Disable H/W ECC calculation - * - * @mtd: MTD device structure - * - */ -static void omap_ecc_disable(struct mtd_info *mtd) -{ - writel((readl(&gpmc_cfg->ecc_config) & ~0x1), - &gpmc_cfg->ecc_config); -} - -/* * omap_rotate_ecc_bch - Rotate the syndrome bytes * * @mtd: MTD device structure @@ -468,76 +584,6 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat,
return 0; } -/* - * omap_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in - * GPMC controller - * @mtd: MTD device structure - * @mode: Read/Write mode - */ -static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode) -{ - uint32_t val, dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1; - uint32_t unused_length = 0; - struct nand_bch_priv *bch = chip->priv; - - switch (bch->nibbles) { - case ECC_BCH4_NIBBLES: - unused_length = 3; - break; - case ECC_BCH8_NIBBLES: - unused_length = 2; - break; - case ECC_BCH16_NIBBLES: - unused_length = 0; - break; - } - - /* Clear the ecc result registers, select ecc reg as 1 */ - writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); - - switch (mode) { - case NAND_ECC_WRITE: - /* eccsize1 config */ - val = ((unused_length + bch->nibbles) << 22); - break; - - case NAND_ECC_READ: - default: - /* by default eccsize0 selected for ecc1resultsize */ - /* eccsize0 config */ - val = (bch->nibbles << 12); - /* eccsize1 config */ - val |= (unused_length << 22); - break; - } - /* ecc size configuration */ - writel(val, &gpmc_cfg->ecc_size_config); - /* by default 512bytes sector page is selected */ - /* set bch mode */ - val = (1 << 16); - /* bch4 / bch8 / bch16 */ - val |= (bch->type << 12); - /* set wrap mode to 1 */ - val |= (1 << 8); - val |= (dev_width << 7); - val |= (cs << 1); - writel(val, &gpmc_cfg->ecc_config); -} - -/* - * omap_enable_ecc_bch- This function enables the bch h/w ecc functionality - * @mtd: MTD device structure - * @mode: Read/Write mode - * - */ -static void omap_enable_ecc_bch(struct mtd_info *mtd, int32_t mode) -{ - struct nand_chip *chip = mtd->priv; - - omap_hwecc_init_bch(chip, mode); - /* enable ecc */ - writel((readl(&gpmc_cfg->ecc_config) | 0x1), &gpmc_cfg->ecc_config); -}
/** * omap_read_page_bch - hardware ecc based page read function @@ -602,6 +648,127 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, } #endif /* CONFIG_AM33XX */
+/* + * OMAP3 BCH8 support (with BCH library) + */ +#ifdef CONFIG_NAND_OMAP_BCH8 +/* + * omap_calculate_ecc_bch - Read BCH ECC result + * + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed (unused here) + * @ecc: The ECC output buffer + */ +static int omap_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc) +{ + int ret = 0; + size_t i; + unsigned long nsectors, val1, val2, val3, val4; + + nsectors = ((readl(&gpmc_cfg->ecc_config) >> 4) & 0x7) + 1; + + for (i = 0; i < nsectors; i++) { + /* Read hw-computed remainder */ + val1 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[0]); + val2 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[1]); + val3 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[2]); + val4 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[3]); + + /* + * Add constant polynomial to remainder, in order to get an ecc + * sequence of 0xFFs for a buffer filled with 0xFFs. + */ + *ecc++ = 0xef ^ (val4 & 0xFF); + *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF); + *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF); + *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF); + *ecc++ = 0xed ^ (val3 & 0xFF); + *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF); + *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF); + *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF); + *ecc++ = 0x97 ^ (val2 & 0xFF); + *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF); + *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF); + *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF); + *ecc++ = 0xb5 ^ (val1 & 0xFF); + } + + /* + * Stop reading anymore ECC vals and clear old results + * enable will be called if more reads are required + */ + omap_ecc_disable(mtd); + + return ret; +} + +/** + * omap_correct_data_bch - Decode received data and correct errors + * @mtd: MTD device structure + * @data: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + */ +static int omap_correct_data_bch(struct mtd_info *mtd, u_char *data, + u_char *read_ecc, u_char *calc_ecc) +{ + int i, count; + /* cannot correct more than 8 errors */ + unsigned int errloc[8]; + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *chip_priv = chip->priv; + struct bch_control *bch = chip_priv->control; + + count = decode_bch(bch, NULL, 512, read_ecc, calc_ecc, NULL, errloc); + if (count > 0) { + /* correct errors */ + for (i = 0; i < count; i++) { + /* correct data only, not ecc bytes */ + if (errloc[i] < 8*512) + data[errloc[i]/8] ^= 1 << (errloc[i] & 7); + printf("corrected bitflip %u\n", errloc[i]); +#ifdef DEBUG + puts("read_ecc: "); + /* + * BCH8 have 13 bytes of ECC; BCH4 needs adoption + * here! + */ + for (i = 0; i < 13; i++) + printf("%02x ", read_ecc[i]); + puts("\n"); + puts("calc_ecc: "); + for (i = 0; i < 13; i++) + printf("%02x ", calc_ecc[i]); + puts("\n"); +#endif + } + } else if (count < 0) { + puts("ecc unrecoverable error\n"); + } + return count; +} + +/** + * omap_free_bch - Release BCH ecc resources + * @mtd: MTD device structure + */ +static void __maybe_unused omap_free_bch(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *chip_priv = chip->priv; + struct bch_control *bch = NULL; + + if (chip_priv) + bch = chip_priv->control; + + if (bch) { + free_bch(bch); + chip_priv->control = NULL; + } +} +#endif /* CONFIG_NAND_OMAP_BCH8 */ + #ifndef CONFIG_SPL_BUILD /* * omap_nand_switch_ecc - switch the ECC operation between different engines @@ -651,13 +818,17 @@ void omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength) omap_hwecc_init(nand); printf("1-bit hamming HW ECC selected\n"); } -#ifdef CONFIG_AM33XX +#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) else if (eccstrength == 8) { nand->ecc.mode = NAND_ECC_HW; nand->ecc.layout = &hw_bch8_nand_oob; nand->ecc.size = 512; +#ifdef CONFIG_AM33XX nand->ecc.bytes = 14; nand->ecc.read_page = omap_read_page_bch; +#else + nand->ecc.bytes = 13; +#endif nand->ecc.hwctl = omap_enable_ecc_bch; nand->ecc.correct = omap_correct_data_bch; nand->ecc.calculate = omap_calculate_ecc_bch; @@ -737,16 +908,28 @@ int board_nand_init(struct nand_chip *nand)
nand->chip_delay = 100;
+#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) #ifdef CONFIG_AM33XX + /* AM33xx uses the ELM */ /* required in case of BCH */ elm_init(); - +#else + /* + * Whereas other OMAP based SoC do not have the ELM, they use the BCH + * SW library. + */ + bch_priv.control = init_bch(13, 8, 0x201b /* hw polynominal */); + if (!bch_priv.control) { + puts("Could not init_bch()\n"); + return -ENODEV; + } +#endif /* BCH info that will be correct for SPL or overridden otherwise. */ nand->priv = &bch_priv; #endif
/* Default ECC mode */ -#ifdef CONFIG_AM33XX +#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) nand->ecc.mode = NAND_ECC_HW; nand->ecc.layout = &hw_bch8_nand_oob; nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; @@ -754,7 +937,9 @@ int board_nand_init(struct nand_chip *nand) nand->ecc.hwctl = omap_enable_ecc_bch; nand->ecc.correct = omap_correct_data_bch; nand->ecc.calculate = omap_calculate_ecc_bch; +#ifdef CONFIG_AM33XX nand->ecc.read_page = omap_read_page_bch; +#endif omap_hwecc_init_bch(nand, NAND_ECC_READ); #else #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_NAND_SOFTECC) diff --git a/lib/Makefile b/lib/Makefile index 1bfd3ee..5d71b80 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -27,7 +27,6 @@ LIB = $(obj)libgeneric.o
ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_ADDR_MAP) += addr_map.o -COBJS-$(CONFIG_BCH) += bch.o COBJS-$(CONFIG_AES) += aes.o COBJS-$(CONFIG_BZIP2) += bzlib.o COBJS-$(CONFIG_BZIP2) += bzlib_crctable.o @@ -67,6 +66,7 @@ COBJS-$(CONFIG_SPL_NET_SUPPORT) += errno.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += hashtable.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += net_utils.o endif +COBJS-$(CONFIG_BCH) += bch.o COBJS-y += crc32.o COBJS-y += ctype.o COBJS-y += div64.o

With uppcoming BCH support on OMAP devices we need to decide between differnt algorithms when switching the ECC engine. Currently we support 1-bit hammign and 8-bit BCH on HW backend.
In order to switch between differnet ECC algorithms we need to change the interface of omap_nand_switch_ecc() also.
Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Thomas Weber thomas.weber.linux@googlemail.com --- new in v2
since v2: * use void omap_nand_switch_ecc(bool, uint32_t) * print warning if unknown HW ecc strengs choosen * fix alignment in help test
since v3: * reset ecc.mode to NAND_ECC_NONE to prevent a BUG() on wrong eccstrength input to changed omap_nand_switch_ecc(); With this change a user has the chance to switch back to another ECC mode via nandecc command. Many thanks to Thomas Weber for reporting this.
arch/arm/cpu/armv7/omap3/board.c | 31 ++++++++++---- arch/arm/include/asm/arch-am33xx/sys_proto.h | 2 +- arch/arm/include/asm/arch-omap3/sys_proto.h | 2 +- drivers/mtd/nand/omap_gpmc.c | 57 ++++++++++++++------------ 4 files changed, 56 insertions(+), 36 deletions(-)
diff --git a/arch/arm/cpu/armv7/omap3/board.c b/arch/arm/cpu/armv7/omap3/board.c index c6d9a42..b72fadc 100644 --- a/arch/arm/cpu/armv7/omap3/board.c +++ b/arch/arm/cpu/armv7/omap3/board.c @@ -328,14 +328,25 @@ void abort(void) *****************************************************************************/ static int do_switch_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { - if (argc != 2) + if (argc < 2 || argc > 3) goto usage; - if (strncmp(argv[1], "hw", 2) == 0) - omap_nand_switch_ecc(1); - else if (strncmp(argv[1], "sw", 2) == 0) - omap_nand_switch_ecc(0); - else + + if (strncmp(argv[1], "hw", 2) == 0) { + if (argc == 2) { + omap_nand_switch_ecc(1, 1); + } else { + if (strncmp(argv[2], "hamming", 7) == 0) + omap_nand_switch_ecc(1, 1); + else if (strncmp(argv[2], "bch8", 4) == 0) + omap_nand_switch_ecc(1, 8); + else + goto usage; + } + } else if (strncmp(argv[1], "sw", 2) == 0) { + omap_nand_switch_ecc(0, 0); + } else { goto usage; + }
return 0;
@@ -345,9 +356,13 @@ usage: }
U_BOOT_CMD( - nandecc, 2, 1, do_switch_ecc, + nandecc, 3, 1, do_switch_ecc, "switch OMAP3 NAND ECC calculation algorithm", - "[hw/sw] - Switch between NAND hardware (hw) or software (sw) ecc algorithm" + "hw [hamming|bch8] - Switch between NAND hardware 1-bit hamming and" + " 8-bit BCH\n" + " ecc calculation (second parameter may" + " be omitted).\n" + "nandecc sw - Switch to NAND software ecc algorithm." );
#endif /* CONFIG_NAND_OMAP_GPMC & !CONFIG_SPL_BUILD */ diff --git a/arch/arm/include/asm/arch-am33xx/sys_proto.h b/arch/arm/include/asm/arch-am33xx/sys_proto.h index 0910a94..c913b5f 100644 --- a/arch/arm/include/asm/arch-am33xx/sys_proto.h +++ b/arch/arm/include/asm/arch-am33xx/sys_proto.h @@ -39,5 +39,5 @@ struct gpmc_cs; void gpmc_init(void); void enable_gpmc_cs_config(const u32 *gpmc_config, struct gpmc_cs *cs, u32 base, u32 size); -void omap_nand_switch_ecc(int); +void omap_nand_switch_ecc(uint32_t, uint32_t); #endif diff --git a/arch/arm/include/asm/arch-omap3/sys_proto.h b/arch/arm/include/asm/arch-omap3/sys_proto.h index d60f2ad..dae1312 100644 --- a/arch/arm/include/asm/arch-omap3/sys_proto.h +++ b/arch/arm/include/asm/arch-omap3/sys_proto.h @@ -78,7 +78,7 @@ void sr32(void *, u32, u32, u32); u32 wait_on_value(u32, u32, void *, u32); void sdelay(unsigned long); void make_cs1_contiguous(void); -void omap_nand_switch_ecc(int); +void omap_nand_switch_ecc(uint32_t, uint32_t); void power_init_r(void); void dieid_num_r(void); void do_omap3_emu_romcode_call(u32 service_id, u32 parameters); diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index c7d4999..3468c78 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -604,13 +604,14 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
#ifndef CONFIG_SPL_BUILD /* - * omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc. - * The default is to come up on s/w ecc - * - * @hardware - 1 -switch to h/w ecc, 0 - s/w ecc + * omap_nand_switch_ecc - switch the ECC operation between different engines + * (h/w and s/w) and different algorithms (hamming and BCHx) * + * @hardware - true if one of the HW engines should be used + * @eccstrength - the number of bits that could be corrected + * (1 - hamming, 4 - BCH4, 8 - BCH8, 16 - BCH16) */ -void omap_nand_switch_ecc(int32_t hardware) +void omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength) { struct nand_chip *nand; struct mtd_info *mtd; @@ -628,6 +629,7 @@ void omap_nand_switch_ecc(int32_t hardware) nand->options |= NAND_OWN_BUFFERS;
/* Reset ecc interface */ + nand->ecc.mode = NAND_ECC_NONE; nand->ecc.read_page = NULL; nand->ecc.write_page = NULL; nand->ecc.read_oob = NULL; @@ -637,28 +639,31 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.calculate = NULL;
/* Setup the ecc configurations again */ - if (hardware == 1) { - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.layout = &hw_nand_oob; - nand->ecc.size = 512; - nand->ecc.bytes = 3; - nand->ecc.hwctl = omap_enable_hwecc; - nand->ecc.correct = omap_correct_data; - nand->ecc.calculate = omap_calculate_ecc; - omap_hwecc_init(nand); - printf("HW ECC selected\n"); + if (hardware) { + if (eccstrength == 1) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_nand_oob; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data; + nand->ecc.calculate = omap_calculate_ecc; + omap_hwecc_init(nand); + printf("1-bit hamming HW ECC selected\n"); + } #ifdef CONFIG_AM33XX - } else if (hardware == 2) { - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.layout = &hw_bch8_nand_oob; - nand->ecc.size = 512; - nand->ecc.bytes = 14; - nand->ecc.read_page = omap_read_page_bch; - nand->ecc.hwctl = omap_enable_ecc_bch; - nand->ecc.correct = omap_correct_data_bch; - nand->ecc.calculate = omap_calculate_ecc_bch; - omap_hwecc_init_bch(nand, NAND_ECC_READ); - printf("HW BCH8 selected\n"); + else if (eccstrength == 8) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_bch8_nand_oob; + nand->ecc.size = 512; + nand->ecc.bytes = 14; + nand->ecc.read_page = omap_read_page_bch; + nand->ecc.hwctl = omap_enable_ecc_bch; + nand->ecc.correct = omap_correct_data_bch; + nand->ecc.calculate = omap_calculate_ecc_bch; + omap_hwecc_init_bch(nand, NAND_ECC_READ); + printf("8-bit BCH HW ECC selected\n"); + } #endif } else { nand->ecc.mode = NAND_ECC_SOFT;

Hello Andreas,
this patch fixes the reported BUG().
The output is now:
-----8<--------- OMAP3 DevKit8000 # nandecc hw hamming 1-bit hamming HW ECC selected OMAP3 DevKit8000 # nandecc hw bch8 Unsupported HW ECC algorithm NAND_ECC_NONE selected by board driver. This is not recommended !! OMAP3 DevKit8000 # ----->8---------
Thomas
On 04/05/2013 11:52 AM, Andreas Bießmann wrote:
With uppcoming BCH support on OMAP devices we need to decide between differnt algorithms when switching the ECC engine. Currently we support 1-bit hammign and 8-bit BCH on HW backend.
In order to switch between differnet ECC algorithms we need to change the interface of omap_nand_switch_ecc() also.
Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Thomas Weber thomas.weber.linux@googlemail.com
new in v2
since v2:
- use void omap_nand_switch_ecc(bool, uint32_t)
- print warning if unknown HW ecc strengs choosen
- fix alignment in help test
since v3:
- reset ecc.mode to NAND_ECC_NONE to prevent a BUG() on wrong eccstrength input to changed omap_nand_switch_ecc(); With this change a user has the chance to switch back to another ECC mode via nandecc command. Many thanks to Thomas Weber for reporting this.
arch/arm/cpu/armv7/omap3/board.c | 31 ++++++++++---- arch/arm/include/asm/arch-am33xx/sys_proto.h | 2 +- arch/arm/include/asm/arch-omap3/sys_proto.h | 2 +- drivers/mtd/nand/omap_gpmc.c | 57 ++++++++++++++------------ 4 files changed, 56 insertions(+), 36 deletions(-)
diff --git a/arch/arm/cpu/armv7/omap3/board.c b/arch/arm/cpu/armv7/omap3/board.c index c6d9a42..b72fadc 100644 --- a/arch/arm/cpu/armv7/omap3/board.c +++ b/arch/arm/cpu/armv7/omap3/board.c @@ -328,14 +328,25 @@ void abort(void) *****************************************************************************/ static int do_switch_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) {
- if (argc != 2)
- if (argc < 2 || argc > 3) goto usage;
- if (strncmp(argv[1], "hw", 2) == 0)
omap_nand_switch_ecc(1);
- else if (strncmp(argv[1], "sw", 2) == 0)
omap_nand_switch_ecc(0);
- else
if (strncmp(argv[1], "hw", 2) == 0) {
if (argc == 2) {
omap_nand_switch_ecc(1, 1);
} else {
if (strncmp(argv[2], "hamming", 7) == 0)
omap_nand_switch_ecc(1, 1);
else if (strncmp(argv[2], "bch8", 4) == 0)
omap_nand_switch_ecc(1, 8);
else
goto usage;
}
} else if (strncmp(argv[1], "sw", 2) == 0) {
omap_nand_switch_ecc(0, 0);
} else { goto usage;
}
return 0;
@@ -345,9 +356,13 @@ usage: }
U_BOOT_CMD(
- nandecc, 2, 1, do_switch_ecc,
- nandecc, 3, 1, do_switch_ecc, "switch OMAP3 NAND ECC calculation algorithm",
- "[hw/sw] - Switch between NAND hardware (hw) or software (sw) ecc algorithm"
- "hw [hamming|bch8] - Switch between NAND hardware 1-bit hamming and"
- " 8-bit BCH\n"
- " ecc calculation (second parameter may"
- " be omitted).\n"
- "nandecc sw - Switch to NAND software ecc algorithm."
);
#endif /* CONFIG_NAND_OMAP_GPMC & !CONFIG_SPL_BUILD */ diff --git a/arch/arm/include/asm/arch-am33xx/sys_proto.h b/arch/arm/include/asm/arch-am33xx/sys_proto.h index 0910a94..c913b5f 100644 --- a/arch/arm/include/asm/arch-am33xx/sys_proto.h +++ b/arch/arm/include/asm/arch-am33xx/sys_proto.h @@ -39,5 +39,5 @@ struct gpmc_cs; void gpmc_init(void); void enable_gpmc_cs_config(const u32 *gpmc_config, struct gpmc_cs *cs, u32 base, u32 size); -void omap_nand_switch_ecc(int); +void omap_nand_switch_ecc(uint32_t, uint32_t); #endif diff --git a/arch/arm/include/asm/arch-omap3/sys_proto.h b/arch/arm/include/asm/arch-omap3/sys_proto.h index d60f2ad..dae1312 100644 --- a/arch/arm/include/asm/arch-omap3/sys_proto.h +++ b/arch/arm/include/asm/arch-omap3/sys_proto.h @@ -78,7 +78,7 @@ void sr32(void *, u32, u32, u32); u32 wait_on_value(u32, u32, void *, u32); void sdelay(unsigned long); void make_cs1_contiguous(void); -void omap_nand_switch_ecc(int); +void omap_nand_switch_ecc(uint32_t, uint32_t); void power_init_r(void); void dieid_num_r(void); void do_omap3_emu_romcode_call(u32 service_id, u32 parameters); diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index c7d4999..3468c78 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -604,13 +604,14 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
#ifndef CONFIG_SPL_BUILD /*
- omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc.
- The default is to come up on s/w ecc
- @hardware - 1 -switch to h/w ecc, 0 - s/w ecc
- omap_nand_switch_ecc - switch the ECC operation between different engines
- (h/w and s/w) and different algorithms (hamming and BCHx)
- @hardware - true if one of the HW engines should be used
- @eccstrength - the number of bits that could be corrected
*/
(1 - hamming, 4 - BCH4, 8 - BCH8, 16 - BCH16)
-void omap_nand_switch_ecc(int32_t hardware) +void omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength) { struct nand_chip *nand; struct mtd_info *mtd; @@ -628,6 +629,7 @@ void omap_nand_switch_ecc(int32_t hardware) nand->options |= NAND_OWN_BUFFERS;
/* Reset ecc interface */
- nand->ecc.mode = NAND_ECC_NONE; nand->ecc.read_page = NULL; nand->ecc.write_page = NULL; nand->ecc.read_oob = NULL;
@@ -637,28 +639,31 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.calculate = NULL;
/* Setup the ecc configurations again */
- if (hardware == 1) {
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.layout = &hw_nand_oob;
nand->ecc.size = 512;
nand->ecc.bytes = 3;
nand->ecc.hwctl = omap_enable_hwecc;
nand->ecc.correct = omap_correct_data;
nand->ecc.calculate = omap_calculate_ecc;
omap_hwecc_init(nand);
printf("HW ECC selected\n");
- if (hardware) {
if (eccstrength == 1) {
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.layout = &hw_nand_oob;
nand->ecc.size = 512;
nand->ecc.bytes = 3;
nand->ecc.hwctl = omap_enable_hwecc;
nand->ecc.correct = omap_correct_data;
nand->ecc.calculate = omap_calculate_ecc;
omap_hwecc_init(nand);
printf("1-bit hamming HW ECC selected\n");
}
#ifdef CONFIG_AM33XX
- } else if (hardware == 2) {
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.layout = &hw_bch8_nand_oob;
nand->ecc.size = 512;
nand->ecc.bytes = 14;
nand->ecc.read_page = omap_read_page_bch;
nand->ecc.hwctl = omap_enable_ecc_bch;
nand->ecc.correct = omap_correct_data_bch;
nand->ecc.calculate = omap_calculate_ecc_bch;
omap_hwecc_init_bch(nand, NAND_ECC_READ);
printf("HW BCH8 selected\n");
else if (eccstrength == 8) {
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.layout = &hw_bch8_nand_oob;
nand->ecc.size = 512;
nand->ecc.bytes = 14;
nand->ecc.read_page = omap_read_page_bch;
nand->ecc.hwctl = omap_enable_ecc_bch;
nand->ecc.correct = omap_correct_data_bch;
nand->ecc.calculate = omap_calculate_ecc_bch;
omap_hwecc_init_bch(nand, NAND_ECC_READ);
printf("8-bit BCH HW ECC selected\n");
}
#endif } else { nand->ecc.mode = NAND_ECC_SOFT;

Dear Thomas Weber,
On 04/05/2013 01:48 PM, Thomas Weber wrote:
Hello Andreas,
this patch fixes the reported BUG().
The output is now:
-----8<--------- OMAP3 DevKit8000 # nandecc hw hamming 1-bit hamming HW ECC selected OMAP3 DevKit8000 # nandecc hw bch8 Unsupported HW ECC algorithm NAND_ECC_NONE selected by board driver. This is not recommended !! OMAP3 DevKit8000 # ----->8---------
as expected. It is up to the user to choose the correct setup. Now he has the opportunity to switch back to hamming for example.
Best regards
Andreas Bießmann

The BCH for OMAP3 is implemented as the linux kernel in 0e618ef0a6a33cf7ef96c2c824402088dd8ef48c does.
The kernel states:
---8<--- The OMAP3 GPMC hardware BCH engine computes remainder polynomials, it does not provide automatic error location and correction: this step is implemented using the BCH library. --->8---
And we do so in u-boot.
This implementation uses the same layout for BCH8 but it is fix. The current provided layout does only work with 64 Byte OOB.
Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com Cc: Mansoor Ahamed mansoor.ahamed@ti.com --- since v1: * cleanups (remove debug stuff) * make checkpach clean (still 2 warnings which I will not fix) * merge some code with the AM33XX implementation
known checkpatch errors: WARNING: line over 80 characters #92: FILE: drivers/mtd/nand/omap_gpmc.c:273: +static void __maybe_unused omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode)
WARNING: line over 80 characters #165: FILE: drivers/mtd/nand/omap_gpmc.c:346: +static void __maybe_unused omap_enable_ecc_bch(struct mtd_info *mtd, int32_t mode)
total: 0 errors, 2 warnings, 0 checks, 428 lines checked
drivers/mtd/nand/omap_gpmc.c | 347 +++++++++++++++++++++++++++++++----------- lib/Makefile | 2 +- 2 files changed, 258 insertions(+), 91 deletions(-)
diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index f073cbe..5f36971 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -28,6 +28,7 @@ #include <asm/arch/cpu.h> #include <asm/omap_gpmc.h> #include <linux/mtd/nand_ecc.h> +#include <linux/bch.h> #include <linux/compiler.h> #include <nand.h> #ifdef CONFIG_AM33XX @@ -37,6 +38,8 @@ static uint8_t cs; static __maybe_unused struct nand_ecclayout hw_nand_oob = GPMC_NAND_HW_ECC_LAYOUT; +static __maybe_unused struct nand_ecclayout hw_bch8_nand_oob = + GPMC_NAND_HW_BCH8_ECC_LAYOUT;
/* * omap_nand_hwcontrol - Set the address pointers corretly for the @@ -239,13 +242,13 @@ static void __maybe_unused omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) }
/* - * BCH8 support (needs ELM and thus AM33xx-only) + * Generic BCH interface */ -#ifdef CONFIG_AM33XX struct nand_bch_priv { uint8_t mode; uint8_t type; uint8_t nibbles; + struct bch_control *control; };
/* bch types */ @@ -253,21 +256,127 @@ struct nand_bch_priv { #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
-static struct nand_ecclayout hw_bch8_nand_oob = GPMC_NAND_HW_BCH8_ECC_LAYOUT; - -static struct nand_bch_priv bch_priv = { +static __maybe_unused struct nand_bch_priv bch_priv = { .mode = NAND_ECC_HW_BCH, .type = ECC_BCH8, - .nibbles = ECC_BCH8_NIBBLES + .nibbles = ECC_BCH8_NIBBLES, + .control = NULL };
/* + * omap_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in + * GPMC controller + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +static void __maybe_unused omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode) +{ + uint32_t val; + uint32_t dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1; +#ifdef CONFIG_AM33XX + uint32_t unused_length = 0; +#endif + uint32_t wr_mode = BCH_WRAPMODE_6; + struct nand_bch_priv *bch = chip->priv; + + /* Clear the ecc result registers, select ecc reg as 1 */ + writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); + +#ifdef CONFIG_AM33XX + wr_mode = BCH_WRAPMODE_1; + + switch (bch->nibbles) { + case ECC_BCH4_NIBBLES: + unused_length = 3; + break; + case ECC_BCH8_NIBBLES: + unused_length = 2; + break; + case ECC_BCH16_NIBBLES: + unused_length = 0; + break; + } + + switch (mode) { + case NAND_ECC_WRITE: + /* eccsize1 config */ + val = ((unused_length + bch->nibbles) << 22); + break; + + case NAND_ECC_READ: + default: + /* by default eccsize0 selected for ecc1resultsize */ + /* eccsize0 config */ + val = (bch->nibbles << 12); + /* eccsize1 config */ + val |= (unused_length << 22); + break; + } +#else + /* + * This is for BCH sw library, we need to choose wrap mode 6 and fixed + * layout. + * Here we are using wrapping mode 6 both for reading and writing, with: + * size0 = 0 (no additional protected byte in spare area) + * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) + */ + val = (32 << 22) | (0 << 12); +#endif + /* ecc size configuration */ + writel(val, &gpmc_cfg->ecc_size_config); + + /* by default 512bytes sector page is selected */ + /* set bch mode */ + val = (1 << 16); + /* bch4 / bch8 / bch16 */ + val |= (bch->type << 12); + val |= (wr_mode << 8); + val |= (dev_width << 7); + val |= (cs << 1); + debug("set ECC_CONFIG=0x%08x\n", val); + writel(val, &gpmc_cfg->ecc_config); +} + +/* + * omap_enable_ecc_bch - This function enables the bch h/w ecc functionality + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +static void __maybe_unused omap_enable_ecc_bch(struct mtd_info *mtd, int32_t mode) +{ + struct nand_chip *chip = mtd->priv; + + omap_hwecc_init_bch(chip, mode); + /* enable ecc */ + writel((readl(&gpmc_cfg->ecc_config) | 0x1), &gpmc_cfg->ecc_config); +} + +/* + * omap_ecc_disable - Disable H/W ECC calculation + * + * @mtd: MTD device structure + */ +static void __maybe_unused omap_ecc_disable(struct mtd_info *mtd) +{ + writel((readl(&gpmc_cfg->ecc_config) & ~0x1), &gpmc_cfg->ecc_config); +} + +/* + * BCH8 support (needs ELM and thus AM33xx-only) + */ +#ifdef CONFIG_AM33XX + +/* * omap_read_bch8_result - Read BCH result for BCH8 level * * @mtd: MTD device structure @@ -306,18 +415,6 @@ static void omap_read_bch8_result(struct mtd_info *mtd, uint8_t big_endian, }
/* - * omap_ecc_disable - Disable H/W ECC calculation - * - * @mtd: MTD device structure - * - */ -static void omap_ecc_disable(struct mtd_info *mtd) -{ - writel((readl(&gpmc_cfg->ecc_config) & ~0x1), - &gpmc_cfg->ecc_config); -} - -/* * omap_rotate_ecc_bch - Rotate the syndrome bytes * * @mtd: MTD device structure @@ -468,76 +565,7 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat,
return 0; } -/* - * omap_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in - * GPMC controller - * @mtd: MTD device structure - * @mode: Read/Write mode - */ -static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode) -{ - uint32_t val, dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1; - uint32_t unused_length = 0; - struct nand_bch_priv *bch = chip->priv; - - switch (bch->nibbles) { - case ECC_BCH4_NIBBLES: - unused_length = 3; - break; - case ECC_BCH8_NIBBLES: - unused_length = 2; - break; - case ECC_BCH16_NIBBLES: - unused_length = 0; - break; - } - - /* Clear the ecc result registers, select ecc reg as 1 */ - writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); - - switch (mode) { - case NAND_ECC_WRITE: - /* eccsize1 config */ - val = ((unused_length + bch->nibbles) << 22); - break; - - case NAND_ECC_READ: - default: - /* by default eccsize0 selected for ecc1resultsize */ - /* eccsize0 config */ - val = (bch->nibbles << 12); - /* eccsize1 config */ - val |= (unused_length << 22); - break; - } - /* ecc size configuration */ - writel(val, &gpmc_cfg->ecc_size_config); - /* by default 512bytes sector page is selected */ - /* set bch mode */ - val = (1 << 16); - /* bch4 / bch8 / bch16 */ - val |= (bch->type << 12); - /* set wrap mode to 1 */ - val |= (1 << 8); - val |= (dev_width << 7); - val |= (cs << 1); - writel(val, &gpmc_cfg->ecc_config); -}
-/* - * omap_enable_ecc_bch- This function enables the bch h/w ecc functionality - * @mtd: MTD device structure - * @mode: Read/Write mode - * - */ -static void omap_enable_ecc_bch(struct mtd_info *mtd, int32_t mode) -{ - struct nand_chip *chip = mtd->priv; - - omap_hwecc_init_bch(chip, mode); - /* enable ecc */ - writel((readl(&gpmc_cfg->ecc_config) | 0x1), &gpmc_cfg->ecc_config); -}
/** * omap_read_page_bch - hardware ecc based page read function @@ -602,6 +630,127 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, } #endif /* CONFIG_AM33XX */
+/* + * basic BCH8 support + */ +#ifdef CONFIG_NAND_OMAP_BCH8 +/* + * omap_calculate_ecc_bch - Read BCH ECC result + * + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed (unused here) + * @ecc: The ECC output buffer + */ +static int omap_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc) +{ + int ret = 0; + size_t i; + unsigned long nsectors, val1, val2, val3, val4; + + nsectors = ((readl(&gpmc_cfg->ecc_config) >> 4) & 0x7) + 1; + + for (i = 0; i < nsectors; i++) { + /* Read hw-computed remainder */ + val1 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[0]); + val2 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[1]); + val3 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[2]); + val4 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[3]); + + /* + * Add constant polynomial to remainder, in order to get an ecc + * sequence of 0xFFs for a buffer filled with 0xFFs. + */ + *ecc++ = 0xef ^ (val4 & 0xFF); + *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF); + *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF); + *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF); + *ecc++ = 0xed ^ (val3 & 0xFF); + *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF); + *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF); + *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF); + *ecc++ = 0x97 ^ (val2 & 0xFF); + *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF); + *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF); + *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF); + *ecc++ = 0xb5 ^ (val1 & 0xFF); + } + + /* + * Stop reading anymore ECC vals and clear old results + * enable will be called if more reads are required + */ + omap_ecc_disable(mtd); + + return ret; +} + +/** + * omap_correct_data_bch - Decode received data and correct errors + * @mtd: MTD device structure + * @data: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + */ +static int omap_correct_data_bch(struct mtd_info *mtd, u_char *data, + u_char *read_ecc, u_char *calc_ecc) +{ + int i, count; + /* cannot correct more than 8 errors */ + unsigned int errloc[8]; + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *chip_priv = chip->priv; + struct bch_control *bch = chip_priv->control; + + count = decode_bch(bch, NULL, 512, read_ecc, calc_ecc, NULL, errloc); + if (count > 0) { + /* correct errors */ + for (i = 0; i < count; i++) { + /* correct data only, not ecc bytes */ + if (errloc[i] < 8*512) + data[errloc[i]/8] ^= 1 << (errloc[i] & 7); + printf("corrected bitflip %u\n", errloc[i]); +#ifdef DEBUG + puts("read_ecc: "); + /* + * BCH8 have 13 bytes of ECC; BCH4 needs adoption + * here! + */ + for (i = 0; i < 13; i++) + printf("%02x ", read_ecc[i]); + puts("\n"); + puts("calc_ecc: "); + for (i = 0; i < 13; i++) + printf("%02x ", calc_ecc[i]); + puts("\n"); +#endif + } + } else if (count < 0) { + puts("ecc unrecoverable error\n"); + } + return count; +} + +/** + * omap_free_bch - Release BCH ecc resources + * @mtd: MTD device structure + */ +static void __maybe_unused omap_free_bch(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *chip_priv = chip->priv; + struct bch_control *bch = NULL; + + if (chip_priv) + bch = chip_priv->control; + + if (bch) { + free_bch(bch); + chip_priv->control = NULL; + } +} +#endif /* CONFIG_NAND_OMAP_BCH8 */ + #ifndef CONFIG_SPL_BUILD /* * omap_nand_switch_ecc - switch the ECC operation between different engines @@ -650,13 +799,17 @@ void omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength) omap_hwecc_init(nand); printf("1-bit hamming HW ECC selected\n"); } -#ifdef CONFIG_AM33XX +#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) else if (eccstrength == 8) { nand->ecc.mode = NAND_ECC_HW; nand->ecc.layout = &hw_bch8_nand_oob; nand->ecc.size = 512; +#ifdef CONFIG_AM33XX nand->ecc.bytes = 14; nand->ecc.read_page = omap_read_page_bch; +#else + nand->ecc.bytes = 13; +#endif nand->ecc.hwctl = omap_enable_ecc_bch; nand->ecc.correct = omap_correct_data_bch; nand->ecc.calculate = omap_calculate_ecc_bch; @@ -736,16 +889,28 @@ int board_nand_init(struct nand_chip *nand)
nand->chip_delay = 100;
+#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) #ifdef CONFIG_AM33XX + /* AM33xx uses the ELM */ /* required in case of BCH */ elm_init(); - +#else + /* + * Whereas other OMAP based SoC do not have the ELM, they use the BCH + * SW library. + */ + bch_priv.control = init_bch(13, 8, 0x201b /* hw polynominal */); + if (!bch_priv.control) { + puts("Could not init_bch()\n"); + return -ENODEV; + } +#endif /* BCH info that will be correct for SPL or overridden otherwise. */ nand->priv = &bch_priv; #endif
/* Default ECC mode */ -#ifdef CONFIG_AM33XX +#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) nand->ecc.mode = NAND_ECC_HW; nand->ecc.layout = &hw_bch8_nand_oob; nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; @@ -753,7 +918,9 @@ int board_nand_init(struct nand_chip *nand) nand->ecc.hwctl = omap_enable_ecc_bch; nand->ecc.correct = omap_correct_data_bch; nand->ecc.calculate = omap_calculate_ecc_bch; +#ifdef CONFIG_AM33XX nand->ecc.read_page = omap_read_page_bch; +#endif omap_hwecc_init_bch(nand, NAND_ECC_READ); #else #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_NAND_SOFTECC) diff --git a/lib/Makefile b/lib/Makefile index 1bfd3ee..5d71b80 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -27,7 +27,6 @@ LIB = $(obj)libgeneric.o
ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_ADDR_MAP) += addr_map.o -COBJS-$(CONFIG_BCH) += bch.o COBJS-$(CONFIG_AES) += aes.o COBJS-$(CONFIG_BZIP2) += bzlib.o COBJS-$(CONFIG_BZIP2) += bzlib_crctable.o @@ -67,6 +66,7 @@ COBJS-$(CONFIG_SPL_NET_SUPPORT) += errno.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += hashtable.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += net_utils.o endif +COBJS-$(CONFIG_BCH) += bch.o COBJS-y += crc32.o COBJS-y += ctype.o COBJS-y += div64.o

On Tue, Apr 02, 2013 at 06:05:57PM +0200, Andreas Bie??mann wrote:
The BCH for OMAP3 is implemented as the linux kernel in 0e618ef0a6a33cf7ef96c2c824402088dd8ef48c does.
The kernel states:
---8<--- The OMAP3 GPMC hardware BCH engine computes remainder polynomials, it does not provide automatic error location and correction: this step is implemented using the BCH library. --->8---
And we do so in u-boot.
This implementation uses the same layout for BCH8 but it is fix. The current provided layout does only work with 64 Byte OOB.
Signed-off-by: Andreas Bie??mann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com Cc: Mansoor Ahamed mansoor.ahamed@ti.com
since v1:
- cleanups (remove debug stuff)
- make checkpach clean (still 2 warnings which I will not fix)
- merge some code with the AM33XX implementation
known checkpatch errors: WARNING: line over 80 characters #92: FILE: drivers/mtd/nand/omap_gpmc.c:273: +static void __maybe_unused omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode)
WARNING: line over 80 characters #165: FILE: drivers/mtd/nand/omap_gpmc.c:346: +static void __maybe_unused omap_enable_ecc_bch(struct mtd_info *mtd, int32_t mode)
total: 0 errors, 2 warnings, 0 checks, 428 lines checked
Please correct these.
- default:
/* by default eccsize0 selected for ecc1resultsize */
/* eccsize0 config */
/* * Like this. */
- /* by default 512bytes sector page is selected */
Insert a newline here since this isn't a multi-line comment, but a general note, followed by comments about what's going on.
- /* set bch mode */
- val = (1 << 16);
So newline between these blocks
- /* bch4 / bch8 / bch16 */
- val |= (bch->type << 12);
- val |= (wr_mode << 8);
- val |= (dev_width << 7);
- val |= (cs << 1);
And here.
- debug("set ECC_CONFIG=0x%08x\n", val);
- writel(val, &gpmc_cfg->ecc_config);
+}
And I see these are copied style problems.

Signed-off-by: Andreas Bießmann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Thomas Weber weber@corscience.de Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com --- since v1: * reduce SPL stack size (increase code segment)
include/configs/tricorder.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/include/configs/tricorder.h b/include/configs/tricorder.h index ebd7a25..4c75cc5 100644 --- a/include/configs/tricorder.h +++ b/include/configs/tricorder.h @@ -125,6 +125,8 @@
#define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND */ /* devices */ +#define CONFIG_NAND_OMAP_BCH8 +#define CONFIG_BCH
/* commands to include */ #include <config_cmd_default.h> @@ -290,7 +292,7 @@ #define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR 0x300 /* address 0x60000 */
#define CONFIG_SPL_TEXT_BASE 0x40200000 /*CONFIG_SYS_SRAM_START*/ -#define CONFIG_SPL_MAX_SIZE (54 * 1024) /* 8 KB for stack */ +#define CONFIG_SPL_MAX_SIZE (55 * 1024) /* 7 KB for stack */ #define CONFIG_SPL_STACK LOW_LEVEL_SRAM_STACK
#define CONFIG_SPL_BSS_START_ADDR 0x80000000 /*CONFIG_SYS_SDRAM_BASE*/ @@ -303,11 +305,14 @@ #define CONFIG_SYS_NAND_OOBSIZE 64 #define CONFIG_SYS_NAND_BLOCK_SIZE (128*1024) #define CONFIG_SYS_NAND_BAD_BLOCK_POS NAND_LARGE_BADBLOCK_POS -#define CONFIG_SYS_NAND_ECCPOS {2, 3, 4, 5, 6, 7, 8, 9,\ - 10, 11, 12, 13} +#define CONFIG_SYS_NAND_ECCPOS {12, 13, 14, 15, 16, 17, 18, 19, 20,\ + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,\ + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,\ + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,\ + 60, 61, 62, 63}
#define CONFIG_SYS_NAND_ECCSIZE 512 -#define CONFIG_SYS_NAND_ECCBYTES 3 +#define CONFIG_SYS_NAND_ECCBYTES 13
#define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_TEXT_BASE

On Tue, Apr 02, 2013 at 06:05:53PM +0200, Andreas Bie??mann wrote:
This patch adds the BCH result registers to register mapping for OMAP3 gpmc.
Signed-off-by: Andreas Bie??mann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com
Reviewed-by: Tom Rini trini@ti.com

On Tue, Apr 02, 2013 at 06:05:53PM +0200, Andreas Bie??mann wrote:
This patch adds the BCH result registers to register mapping for OMAP3 gpmc.
Signed-off-by: Andreas Bie??mann andreas.devel@googlemail.com Cc: Tom Rini trini@ti.com Cc: Ilya Yanok ilya.yanok@cogentembedded.com Cc: Scott Wood scottwood@freescale.com
This, along with the rest of the series (v4 of 4/6 and 5/6), applied to u-boot-ti/master, thanks!
participants (4)
-
Andreas Bießmann
-
Scott Wood
-
Thomas Weber
-
Tom Rini