From 9b05aa788bfdd3264ff1bc9418cb19550a7234e4 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Sat, 30 Aug 2008 17:06:55 -0400 Subject: [PATCH 1/5] ARM DaVinci: Fix broken HW ECC for large page NAND. Based on original patch by Bernard Blackham U-boot's HW ECC support for large page NAND on Davinci is completely broken. Some kernels, such as the 2.6.10 one supported by MontaVista for DaVinci, rely upon this broken behaviour as they share the same code for ECCs. In the existing scheme, error detection *might* work on large page, but error correction definitely does not. Small page ECC correction works, but the format is not compatible with the mainline git kernel. This patch adds ECC code that matches what is currently in the Davinci git repository (since NAND support was added in 2.6.24). This makes the ECC and OOB layout written by u-boot compatible with Linux for both small page and large page devices and fixes ECC correction for large page devices. The old behaviour can be restored by defining the macro CFG_DAVINCI_BROKEN_ECC, which is undefined by default. Signed-off-by: Hugo Villeneuve Acked-by: Sergey Kubushyn Signed-off-by: Scott Wood --- cpu/arm926ejs/davinci/nand.c | 81 +++++++++++++++++++++++++++++++++--- doc/README.nand | 8 ++++ 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/cpu/arm926ejs/davinci/nand.c b/cpu/arm926ejs/davinci/nand.c index 5a1da633d3..f7cf0c9ec2 100644 --- a/cpu/arm926ejs/davinci/nand.c +++ b/cpu/arm926ejs/davinci/nand.c @@ -88,6 +88,9 @@ static void nand_davinci_select_chip(struct mtd_info *mtd, int chip) } #ifdef CFG_NAND_HW_ECC +#ifdef CFG_DAVINCI_BROKEN_ECC +/* Linux-compatible ECC uses MTD defaults. */ +/* These layouts are not compatible with Linux or RBL/UBL. */ #ifdef CFG_NAND_LARGEPAGE static struct nand_ecclayout davinci_nand_ecclayout = { .eccbytes = 12, @@ -112,6 +115,7 @@ static struct nand_ecclayout davinci_nand_ecclayout = { #else #error "Either CFG_NAND_LARGEPAGE or CFG_NAND_SMALLPAGE must be defined!" #endif +#endif /* CFG_DAVINCI_BROKEN_ECC */ static void nand_davinci_enable_hwecc(struct mtd_info *mtd, int mode) { @@ -150,6 +154,15 @@ static u_int32_t nand_davinci_readecc(struct mtd_info *mtd, u_int32_t region) static int nand_davinci_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { u_int32_t tmp; +#ifdef CFG_DAVINCI_BROKEN_ECC + /* + * This is not how you should read ECCs on large page Davinci devices. + * The region parameter gets you ECCs for flash chips on different chip + * selects, not the 4x512 byte pages in a 2048 byte page. + * + * Preserved for backwards compatibility though. + */ + int region, n; struct nand_chip *this = mtd->priv; @@ -163,9 +176,26 @@ static int nand_davinci_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u *ecc_code++ = ((tmp >> 8) & 0x0f) | ((tmp >> 20) & 0xf0); region++; } +#else + const int region = 1; + + tmp = nand_davinci_readecc(mtd, region); + + /* Squeeze 4 bytes ECC into 3 bytes by removing RESERVED bits + * and shifting. RESERVED bits are 31 to 28 and 15 to 12. */ + tmp = (tmp & 0x00000fff) | ((tmp & 0x0fff0000) >> 4); + + /* Invert so that erased block ECC is correct */ + tmp = ~tmp; + + *ecc_code++ = tmp; + *ecc_code++ = tmp >> 8; + *ecc_code++ = tmp >> 16; +#endif /* CFG_DAVINCI_BROKEN_ECC */ return(0); } +#ifdef CFG_DAVINCI_BROKEN_ECC static void nand_davinci_gen_true_ecc(u_int8_t *ecc_buf) { u_int32_t tmp = ecc_buf[0] | (ecc_buf[1] << 16) | ((ecc_buf[2] & 0xf0) << 20) | ((ecc_buf[2] & 0x0f) << 8); @@ -282,13 +312,14 @@ static int nand_davinci_compare_ecc(u_int8_t *ecc_nand, u_int8_t *ecc_calc, u_in return(-1); } } +#endif /* CFG_DAVINCI_BROKEN_ECC */ static int nand_davinci_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { - struct nand_chip *this; + struct nand_chip *this = mtd->priv; +#ifdef CFG_DAVINCI_BROKEN_ECC int block_count = 0, i, rc; - this = mtd->priv; block_count = (this->ecc.size/512); for (i = 0; i < block_count; i++) { if (memcmp(read_ecc, calc_ecc, 3) != 0) { @@ -301,9 +332,44 @@ static int nand_davinci_correct_data(struct mtd_info *mtd, u_char *dat, u_char * calc_ecc += 3; dat += 512; } +#else + u_int32_t ecc_nand = read_ecc[0] | (read_ecc[1] << 8) | + (read_ecc[2] << 16); + u_int32_t ecc_calc = calc_ecc[0] | (calc_ecc[1] << 8) | + (calc_ecc[2] << 16); + u_int32_t diff = ecc_calc ^ ecc_nand; + + if (diff) { + if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) { + /* Correctable error */ + if ((diff >> (12 + 3)) < this->ecc.size) { + uint8_t find_bit = 1 << ((diff >> 12) & 7); + uint32_t find_byte = diff >> (12 + 3); + + dat[find_byte] ^= find_bit; + MTDDEBUG(MTD_DEBUG_LEVEL0, "Correcting single " + "bit ECC error at offset: %d, bit: " + "%d\n", find_byte, find_bit); + return 1; + } else { + return -1; + } + } else if (!(diff & (diff - 1))) { + /* Single bit ECC error in the ECC itself, + nothing to fix */ + MTDDEBUG(MTD_DEBUG_LEVEL0, "Single bit ECC error in " + "ECC.\n"); + return 1; + } else { + /* Uncorrectable error */ + MTDDEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n"); + return -1; + } + } +#endif /* CFG_DAVINCI_BROKEN_ECC */ return(0); } -#endif +#endif /* CFG_NAND_HW_ECC */ static int nand_davinci_dev_ready(struct mtd_info *mtd) { @@ -370,6 +436,8 @@ int board_nand_init(struct nand_chip *nand) #endif #ifdef CFG_NAND_HW_ECC nand->ecc.mode = NAND_ECC_HW; +#ifdef CFG_DAVINCI_BROKEN_ECC + nand->ecc.layout = &davinci_nand_ecclayout; #ifdef CFG_NAND_LARGEPAGE nand->ecc.size = 2048; nand->ecc.bytes = 12; @@ -379,13 +447,16 @@ int board_nand_init(struct nand_chip *nand) #else #error "Either CFG_NAND_LARGEPAGE or CFG_NAND_SMALLPAGE must be defined!" #endif - nand->ecc.layout = &davinci_nand_ecclayout; +#else + nand->ecc.size = 512; + nand->ecc.bytes = 3; +#endif /* CFG_DAVINCI_BROKEN_ECC */ nand->ecc.calculate = nand_davinci_calculate_ecc; nand->ecc.correct = nand_davinci_correct_data; nand->ecc.hwctl = nand_davinci_enable_hwecc; #else nand->ecc.mode = NAND_ECC_SOFT; -#endif +#endif /* CFG_NAND_HW_ECC */ /* Set address of hardware control function */ nand->cmd_ctrl = nand_davinci_hwcontrol; diff --git a/doc/README.nand b/doc/README.nand index 171380e274..6dac24cd90 100644 --- a/doc/README.nand +++ b/doc/README.nand @@ -174,6 +174,14 @@ More Definitions: #define NAND_MAX_FLOORS 1 #define NAND_MAX_CHIPS 1 + #define CFG_DAVINCI_BROKEN_ECC + Versions of U-Boot <= 1.3.3 and Montavista Linux kernels + generated bogus ECCs on large-page NAND. Both large and small page + NAND ECCs were incompatible with the Linux davinci git tree (since + NAND was integrated in 2.6.24). + Turn this ON if you want backwards compatibility. + Turn this OFF if you want U-Boot and the Linux davinci git kernel + to use the same ECC format. NOTE: ===== From 6644641d072aee3087da129d8443187196a4d8a9 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Wed, 10 Sep 2008 11:48:49 -0500 Subject: [PATCH 2/5] delta, zylonite: Update nand_oobinfo to nand_ecclayout. This is part of the switch to newer upstream MTD code. Signed-off-by: Scott Wood --- board/delta/nand.c | 6 ++---- board/zylonite/nand.c | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/board/delta/nand.c b/board/delta/nand.c index 4ce78a1e1d..ceb798bd2d 100644 --- a/board/delta/nand.c +++ b/board/delta/nand.c @@ -58,14 +58,12 @@ static struct nand_bbt_descr delta_bbt_descr = { .pattern = scan_ff_pattern }; -static struct nand_oobinfo delta_oob = { - .useecc = MTD_NANDECC_AUTOPL_USR, /* MTD_NANDECC_PLACEONLY, */ +static struct nand_ecclayout delta_oob = { .eccbytes = 6, .eccpos = {2, 3, 4, 5, 6, 7}, .oobfree = { {8, 2}, {12, 4} } }; - /* * not required for Monahans DFC */ @@ -541,6 +539,7 @@ int board_nand_init(struct nand_chip *nand) nand->cmd_ctrl = dfc_hwcontrol; /* nand->dev_ready = dfc_device_ready; */ nand->ecc.mode = NAND_ECC_SOFT; + nand->ecc.layout = &delta_oob; nand->options = NAND_BUSWIDTH_16; nand->waitfunc = dfc_wait; nand->read_byte = dfc_read_byte; @@ -549,7 +548,6 @@ int board_nand_init(struct nand_chip *nand) nand->write_buf = dfc_write_buf; nand->cmdfunc = dfc_cmdfunc; -/* nand->autooob = &delta_oob; */ nand->badblock_pattern = &delta_bbt_descr; return 0; } diff --git a/board/zylonite/nand.c b/board/zylonite/nand.c index 09bcbb233d..7f22935230 100644 --- a/board/zylonite/nand.c +++ b/board/zylonite/nand.c @@ -58,14 +58,12 @@ static struct nand_bbt_descr delta_bbt_descr = { .pattern = scan_ff_pattern }; -static struct nand_oobinfo delta_oob = { - .useecc = MTD_NANDECC_AUTOPL_USR, /* MTD_NANDECC_PLACEONLY, */ +static struct nand_ecclayout delta_oob = { .eccbytes = 6, .eccpos = {2, 3, 4, 5, 6, 7}, .oobfree = { {8, 2}, {12, 4} } }; - /* * not required for Monahans DFC */ @@ -545,6 +543,7 @@ int board_nand_init(struct nand_chip *nand) nand->cmd_ctrl = dfc_hwcontrol; /* nand->dev_ready = dfc_device_ready; */ nand->ecc.mode = NAND_ECC_SOFT; + nand->ecc.layout = &delta_oob; nand->options = NAND_BUSWIDTH_16; nand->waitfunc = dfc_wait; nand->read_byte = dfc_read_byte; @@ -553,7 +552,6 @@ int board_nand_init(struct nand_chip *nand) nand->write_buf = dfc_write_buf; nand->cmdfunc = dfc_cmdfunc; -/* nand->autooob = &delta_oob; */ nand->badblock_pattern = &delta_bbt_descr; return 0; } From 97ae023648e764f794ffb9c52da109d6caf09c47 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Fri, 27 Jun 2008 23:04:04 +0400 Subject: [PATCH 3/5] fsl_elbc_nand: fix OOB workability for large page NAND chips For large page chips, nand_bbt is looking into OOB area, and checking for "0xff 0xff" pattern at OOB offset 0. That is, two bytes should be reserved for bbt means. But ELBC driver is specifying ecclayout so that oobfree area starts at offset 1, so only one byte left for the bbt purposes. This causes problems with any OOB users, namely JFFS2: after first mount JFFS2 will fill all OOBs with "erased marker", so OOBs will contain: OOB Data: ff 19 85 20 03 00 ff ff ff 00 00 08 ff ff ff ff OOB Data: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff OOB Data: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff OOB Data: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff And on the next boot, NAND core will rescan for bad blocks, then will see "0xff 0x19" pattern, and will mark all blocks as bad ones. To fix the issue we should implement our own bad block pattern: just one byte at OOB start. Though, this will work only for x8 chips. For x16 chips two bytes must be checked. Since ELBC driver does not support x16 NANDs (yet), we're safe for now. Signed-off-by: Anton Vorontsov Signed-off-by: David Woodhouse Signed-off-by: Scott Wood --- drivers/mtd/nand/fsl_elbc_nand.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 674c542005..b8bc143a8c 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -122,6 +122,20 @@ static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = { .oobavail = 48, }; +/* + * fsl_elbc_oob_lp_eccm* specify that LP NAND's OOB free area starts at offset + * 1, so we have to adjust bad block pattern. This pattern should be used for + * x8 chips only. So far hardware does not support x16 chips anyway. + */ +static u8 scan_ff_pattern[] = { 0xff, }; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 1, + .pattern = scan_ff_pattern, +}; + /*=================================*/ /* @@ -750,9 +764,10 @@ int board_nand_init(struct nand_chip *nand) priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT); - /* adjust Option Register and ECC to match Flash page size */ + /* Large-page-specific setup */ if (or & OR_FCM_PGS) { priv->page_size = 1; + nand->badblock_pattern = &largepage_memorybased; /* adjust ecc setup if needed */ if ((br & BR_DECC) == BR_DECC_CHK_GEN) { From 8f42bf1c393d53a70c2545e9f329d11c46d74794 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Fri, 27 Jun 2008 23:04:13 +0400 Subject: [PATCH 4/5] fsl_elbc_nand: implement support for flash-based BBT This patch implements support for flash-based BBT for chips working through ELBC NAND controller, so that NAND core will not have to re-scan for bad blocks on every boot. Because ELBC controller may provide HW-generated ECCs we should adjust bbt pattern and bbt version positions in the OOB free area. Signed-off-by: Anton Vorontsov Signed-off-by: David Woodhouse Signed-off-by: Scott Wood --- drivers/mtd/nand/fsl_elbc_nand.c | 35 +++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index b8bc143a8c..e4e4de0870 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -136,6 +136,34 @@ static struct nand_bbt_descr largepage_memorybased = { .pattern = scan_ff_pattern, }; +/* + * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt, + * interfere with ECC positions, that's why we implement our own descriptors. + * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0. + */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 11, + .len = 4, + .veroffs = 15, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 11, + .len = 4, + .veroffs = 15, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + /*=================================*/ /* @@ -738,7 +766,12 @@ int board_nand_init(struct nand_chip *nand) nand->waitfunc = fsl_elbc_wait; /* set up nand options */ - nand->options = NAND_NO_READRDY | NAND_NO_AUTOINCR; + nand->bbt_td = &bbt_main_descr; + nand->bbt_md = &bbt_mirror_descr; + + /* set up nand options */ + nand->options = NAND_NO_READRDY | NAND_NO_AUTOINCR | + NAND_USE_FLASH_BBT; nand->controller = &elbc_ctrl->controller; nand->priv = priv; From 0008b6d968160abe2bfd936493f3a516a7c8da20 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Fri, 27 Jun 2008 23:04:20 +0400 Subject: [PATCH 5/5] fsl_elbc_nand: ecclayout cleanups This patch deletes oobavail assignments, they're calculated by the nand core code in nand_scan_tail, plus current oobavail values are wrong for the LP NANDs. Signed-off-by: Anton Vorontsov Signed-off-by: David Woodhouse Signed-off-by: Scott Wood --- drivers/mtd/nand/fsl_elbc_nand.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index e4e4de0870..4351824388 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -95,7 +95,6 @@ static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = { .eccbytes = 3, .eccpos = {6, 7, 8}, .oobfree = { {0, 5}, {9, 7} }, - .oobavail = 12, }; /* Small Page FLASH with FMR[ECCM] = 1 */ @@ -103,7 +102,6 @@ static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = { .eccbytes = 3, .eccpos = {8, 9, 10}, .oobfree = { {0, 5}, {6, 2}, {11, 5} }, - .oobavail = 12, }; /* Large Page FLASH with FMR[ECCM] = 0 */ @@ -111,7 +109,6 @@ static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = { .eccbytes = 12, .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56}, .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} }, - .oobavail = 48, }; /* Large Page FLASH with FMR[ECCM] = 1 */ @@ -119,7 +116,6 @@ static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = { .eccbytes = 12, .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58}, .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} }, - .oobavail = 48, }; /*