ARM: Davinci: NAND fix for large page ECC and linux compatibility
RFC PATCH 1/1 ARM: Davinci: NAND fix for large page ECC and linux compatibility
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 code depends on a #define CFG_LINUX_COMPATIBLE_ECC, which is undefined by default, making the default state backwards compatible. I have verified this by compiling without the #define and producing a binary byte-for-byte identical to one without this patch.
[NOTE: I have not yet been able to get my hands on a board with small-page NAND to test, but large page does work. If anybody is interested in testing it, please do and let me know if it works for you (i.e. uboot with this patch and davinci git kernel can read/write the same NAND).]
--- cpu/arm926ejs/davinci/nand.c | 79 ++++++++++++++++++++++++++++++++++++++-- include/configs/davinci_dvevm.h | 12 ++++++ 2 files changed, 89 insertions(+), 2 deletions(-)
cpu/arm926ejs/davinci/nand.c +77 -2
@@ -87,6 +87,10 @@ static void nand_davinci_select_chip(str }#ifdef CFG_NAND_HW_ECC + +#ifndef CFG_LINUX_COMPATIBLE_ECC +/* Linux-compatible ECC uses MTD defaults. */ +/* These layouts are not compatible with Linux or RBL/UBL. */ #ifdef CFG_NAND_LARGEPAGE static struct nand_oobinfo davinci_nand_oobinfo = { .useecc = MTD_NANDECC_AUTOPLACE,
@@ -104,6 +108,7 @@ static struct nand_oobinfo davinci_nand_#else #error "Either CFG_NAND_LARGEPAGE or CFG_NAND_SMALLPAGE must be defined!"#endif +#endif static void nand_davinci_enable_hwecc (struct mtd_info *mtd,int mode) {
@@ -141,12 +146,29 @@ static u_int32_t nand_davinci_readecc(ststatic int nand_davinci_calculate_ecc (struct mtd_info *mtd,const u_char *dat,u_char *ecc_code) { +#ifdef CFG_LINUX_COMPATIBLE_ECC +unsigned int ecc_val =nand_davinci_readecc (mtd,1 ); +/* squeeze 0 middle bits out so that it fits in 3 bytes */ +unsigned int tmp = (ecc_val&0x0fff )|((ecc_val&0x0fff0000 )>>4 ); +/* invert so that erased block ecc is correct */ + tmp = ~tmp; + ecc_code[0 ] = (u_char)(tmp); + ecc_code[1 ] = (u_char)(tmp >>8 ); + ecc_code[2 ] = (u_char)(tmp >>16 ); +#else u_int32_t tmp;int region, n;struct nand_chip *this = mtd->priv; n = (this->eccmode ==NAND_ECC_HW12_2048 ) ?4 :1 ; +/* +* 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. +*/ region =1 ;while (n--) { tmp =nand_davinci_readecc (mtd, region);
@@ -155,9 +177,51 @@ static int nand_davinci_calculate_ecc(st *ecc_code++ = ((tmp >>8 ) &0x0f ) | ((tmp >>20 ) &0xf0 ); region++; } +#endif +return (0 ); } +#ifdef CFG_LINUX_COMPATIBLE_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 *chip = mtd->priv; +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 )) < chip->eccsize) { +uint8_t find_bit =1 << ((diff>>12 )&7 ); +uint32_t find_byte = diff>>(12 +3 ); + dat[find_byte] ^= find_bit; +DEBUG (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 */ +DEBUG (MTD_DEBUG_LEVEL0 ,"Single bit ECC error in ECC.\n" ); +return 1 ; + }else { +/* Uncorrectable error */ +DEBUG (MTD_DEBUG_LEVEL0 ,"ECC UNCORRECTED_ERROR 1\n" ); +return -1 ; + } + + } +return 0 ; +} + +#else +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 );
@@ -291,7 +355,9 @@ static int nand_davinci_correct_data(str }return (0 ); } -#endif +#endif /* CFG_LINUX_COMPATIBLE_ECC */ + +#endif /* CFG_NAND_HW_ECC */ static int nand_davinci_dev_ready (struct mtd_info *mtd) {
@@ -356,7 +422,13 @@ int board_nand_init(struct nand_chip *na#ifdef CFG_NAND_USE_FLASH_BBT nand->options =NAND_USE_FLASH_BBT ;#endif +#ifdef CFG_NAND_HW_ECC + +#ifdef CFG_LINUX_COMPATIBLE_ECC + nand->eccmode =NAND_ECC_HW3_512 ; +#else +#ifdef CFG_NAND_LARGEPAGE nand->eccmode =NAND_ECC_HW12_2048 ;#elif defined(CFG_NAND_SMALLPAGE )
@@ -365,12 +437,15 @@ int board_nand_init(struct nand_chip *na#error "Either CFG_NAND_LARGEPAGE or CFG_NAND_SMALLPAGE must be defined!"#endif nand->autooob = &davinci_nand_oobinfo; + +#endif /* CFG_LINUX_COMPATIBLE_ECC */ + nand->calculate_ecc = nand_davinci_calculate_ecc; nand->correct_data = nand_davinci_correct_data; nand->enable_hwecc = nand_davinci_enable_hwecc;#else nand->eccmode =NAND_ECC_SOFT ; -#endif +#endif /* CFG_NAND_HW_ECC */ /* Set address of hardware control function */ nand->hwcontrol = nand_davinci_hwcontrol;
include/configs/davinci_dvevm.h +12 -0
@@ -46,6 +46,18 @@#define CONFIG_NOR_UART_BOOT */ +/* +* Previous versions of u-boot (1.3.3 and prior) 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). +* Don't turn this on if you want backwards compatibility. +* Do turn this on if you want u-boot to be able to read and write NAND +* that can be written or read by the Linux davinci git kernel. +* +#define CFG_LINUX_COMPATIBLE_ECC +*/ +/*=======*/ /* Board */ /*=======*/
RFC PATCH 1/1 ARM: Davinci: NAND fix for large page ECC and linux compatibility
cpu/arm926ejs/davinci/nand.c static u_int32_t nand_davinci_readecc(st · line 152
Quoted 13 lines
#endif
cpu/arm926ejs/davinci/nand.c +5
-0+#endif static void nand_davinci_enable_hwecc (struct mtd_info *mtd,int mode) {@@ -141,12 +146,29 @@ static u_int32_t nand_davinci_readecc(ststatic int nand_davinci_calculate_ecc (struct mtd_info *mtd,const u_char *dat,u_char *ecc_code) { +#ifdef CFG_LINUX_COMPATIBLE_ECC +unsigned int ecc_val =nand_davinci_readecc (mtd,1 ); +/* squeeze 0 middle bits out so that it fits in 3 bytes */ +unsigned int tmp = (ecc_val&0x0fff )|((ecc_val&0x0fff0000 )>>4 );
unsigned int tmp = (ecc_val & 0x0fff) | ((ecc_val & 0x0fff0000) >> 4);
please and space between operator
plese use the same alignement
add an empty line
Quoted 13 lines
cpu/arm926ejs/davinci/nand.c +7
-0+/* invert so that erased block ecc is correct */ + tmp = ~tmp; + ecc_code[0 ] = (u_char)(tmp); + ecc_code[1 ] = (u_char)(tmp >>8 ); + ecc_code[2 ] = (u_char)(tmp >>16 ); +#else u_int32_t tmp;int region, n;struct nand_chip *this = mtd->priv; n = (this->eccmode ==NAND_ECC_HW12_2048 ) ?4 :1 ; + u_char *read_ecc, u_char *calc_ecc)
cpu/arm926ejs/davinci/nand.c static u_int32_t nand_davinci_readecc(st
Quoted 10 lines
cpu/arm926ejs/davinci/nand.c +10
-0+{ +struct nand_chip *chip = mtd->priv; +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 ) {
please and space between operator
cpu/arm926ejs/davinci/nand.c static int nand_davinci_calculate_ecc(st · line 198
Quoted 1 line
cpu/arm926ejs/davinci/nand.c +1
-0+/* Correctable error */
please and space between operator
cpu/arm926ejs/davinci/nand.c static int nand_davinci_calculate_ecc(st · line 200
Quoted 2 lines
cpu/arm926ejs/davinci/nand.c +2
-0+if ((diff>>(12 +3 )) < chip->eccsize) { +uint8_t find_bit =1 << ((diff>>12 )&7 );
please and space between operator
cpu/arm926ejs/davinci/nand.c static int nand_davinci_calculate_ecc(st · line 201
Quoted 1 line
cpu/arm926ejs/davinci/nand.c +1
-0+uint32_t find_byte = diff>>(12 +3 );
uint32_t find_byte = diff >> 15;
please and space between operator
cpu/arm926ejs/davinci/nand.c static int nand_davinci_calculate_ecc(st · line 203
Quoted 2 lines
cpu/arm926ejs/davinci/nand.c +2
-0+ dat[find_byte] ^= find_bit; +DEBUG (MTD_DEBUG_LEVEL0 ,"Correcting single bit ECC error at offset: %d, bit: %d\n" , find_byte, find_bit);
too long please split
cpu/arm926ejs/davinci/nand.c static int nand_davinci_calculate_ecc(st · line 208
Quoted 5 lines
cpu/arm926ejs/davinci/nand.c +5
-0+return 1 ; + }else { +return -1 ; + } + }else if (!(diff & (diff-1 ))) {
please and space between operator
cpu/arm926ejs/davinci/nand.c static int nand_davinci_calculate_ecc(st · line 210
Quoted 2 lines
cpu/arm926ejs/davinci/nand.c +2
-0+/* Single bit ECC error in the ECC itself, +nothing to fix */
please use this style of comment
/*
*
*/
Quoted 17 lines
cpu/arm926ejs/davinci/nand.c +6
-0+DEBUG (MTD_DEBUG_LEVEL0 ,"Single bit ECC error in ECC.\n" ); +return 1 ; + }else { +/* Uncorrectable error */ +DEBUG (MTD_DEBUG_LEVEL0 ,"ECC UNCORRECTED_ERROR 1\n" ); +return -1 ;/* Set address of hardware control function */ nand->hwcontrol = nand_davinci_hwcontrol;Index:
u-boot-1.3.3/include/configs/davinci_dvevm.h
===================================================================include/configs/davinci_dvevm.h +1
-0@@ -46,6 +46,18 @@#define CONFIG_NOR_UART_BOOT */ +/*
include/configs/davinci_dvevm.h line 59
Quoted 10 lines
include/configs/davinci_dvevm.h +10
-0+* Previous versions of u-boot (1.3.3 and prior) 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). +* Don't turn this on if you want backwards compatibility. +* Do turn this on if you want u-boot to be able to read and write NAND +* that can be written or read by the Linux davinci git kernel. +* +#define CFG_LINUX_COMPATIBLE_ECC +*/
please move this in README.davinci
include/configs/davinci_dvevm.h line 60
Quoted 1 line
include/configs/davinci_dvevm.h +1
-0+
Best Regards,
J.
PATCH 1/1 ARM DaVinci: Fix broken HW ECC for large page NAND.
In reply to Bernard BlackhamARM DaVinci: Fix broken HW ECC for large page NAND.
---
Based on original patch by Bernard Blackham <[email protected]>
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.
cpu/arm926ejs/davinci/nand.c | 81 +++++++++++++++++++++++++++++++++++++++--- doc/README.nand | 8 ++++ 2 files changed, 84 insertions(+), 5 deletions(-)
cpu/arm926ejs/davinci/nand.c +76 -5
@@ -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;
doc/README.nand +8 -0
@@ -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: =====
PATCH 1/1 ARM DaVinci: Fix broken HW ECC for large page NAND.
On Sat, 30 Aug 2008, Hugo Villeneuve wrote:
Quoted 224 lines
ARM DaVinci: Fix broken HW ECC for large page NAND.
Signed-off-by Hugo Villeneuve <[email protected]>---
Based on original patch by Bernard Blackham <[email protected]>
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.
cpu/arm926ejs/davinci/nand.c| 81
+++++++++++++++++++++++++++++++++++++++---
doc/README.nand | 8 ++++2 files changed, 84 insertions(+), 5 deletions(-)cpu/arm926ejs/davinci/nand.c +3
-0@@ -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,cpu/arm926ejs/davinci/nand.c +0
-0@@ -112,6 +115,7 @@ static struct nand_ecclayout davinci_nand_ecclayout = {#else #error "Either CFG_NAND_LARGEPAGE or CFG_NAND_SMALLPAGE must bedefined!"
#endif
+#endif /*CFG_DAVINCI_BROKEN_ECC*/static void
nand_davinci_enable_hwecc(struct mtd_info *mtd, int mode){cpu/arm926ejs/davinci/nand.c +0
-0@@ -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;cpu/arm926ejs/davinci/nand.c +3
-0+#ifdef CFG_DAVINCI_BROKEN_ECC +/* +* This is not how you should read ECCs on large page Davinci devices.
cpu/arm926ejs/davinci/nand.c +1
-0+* The region parameter gets you ECCs for flash chips on different chip
cpu/arm926ejs/davinci/nand.c +5
-0+* 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;cpu/arm926ejs/davinci/nand.c +0
-0@@ -163,9 +176,26 @@ static int nand_davinci_calculate_ecc(struct
mtd_info*mtd, constu_char*dat, u*ecc_code++ = ((tmp >> 8) & 0x0f) | ((tmp >> 20) & 0xf0); region++; }cpu/arm926ejs/davinci/nand.c +16
-0+#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 voidnand_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);cpu/arm926ejs/davinci/nand.c +3
-2@@ -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) {cpu/arm926ejs/davinci/nand.c +0
-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; }cpu/arm926ejs/davinci/nand.c +12
-0+#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);
cpu/arm926ejs/davinci/nand.c +4
-0+uint32_t find_byte = diff >> (12 +3 ); + + dat[find_byte] ^= find_bit; + MTDDEBUG(MTD_DEBUG_LEVEL0, "Correctingsingle "
cpu/arm926ejs/davinci/nand.c +1
-0+ "bit ECC error at offset: %d,bit: "
cpu/arm926ejs/davinci/nand.c +9
-0+ "%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 errorin "
cpu/arm926ejs/davinci/nand.c +5
-0+ "ECC.\n"); +return 1 ; + }else { +/* Uncorrectable error */ + MTDDEBUG(MTD_DEBUG_LEVEL0, "ECC
UNCORRECTED_ERROR1\n");cpu/arm926ejs/davinci/nand.c +4
-0+ return -1; + } + } +#endif /* CFG_DAVINCI_BROKEN_ECC */ return(0); }cpu/arm926ejs/davinci/nand.c +1
-1-#endif +#endif /* CFG_NAND_HW_ECC */ static int
nand_davinci_dev_ready(struct mtd_info *mtd){cpu/arm926ejs/davinci/nand.c +2
-0@@ -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 bedefined!"
#endifcpu/arm926ejs/davinci/nand.c +4
-1- 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;cpu/arm926ejs/davinci/nand.c +1
-1-#endif +#endif /* CFG_NAND_HW_ECC */ /* Set address of hardware control function */ nand->cmd_ctrl = nand_davinci_hwcontrol;doc/README.nand +3
-0@@ -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 smallpage
+ NAND ECCs were incompatible with the Linux davinci git tree
(sincedoc/README.nand +4
-0+ 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.
Quoted 7 lines
NOTE:
=====
_______________________________________________
U-Boot mailing list
U-Boot at lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot
---
******************************************************************
* KSI at home KOI8 Net < > The impossible we do immediately. *
* Las Vegas NV, USA < > Miracles require 24-hour notice. *
******************************************************************
PATCH 1/1 ARM DaVinci: Fix broken HW ECC for large page NAND.
In reply to Hugo VilleneuveQuoted 3 lines · Hugo Villeneuve
ARM DaVinci: Fix broken HW ECC for large page NAND.
Signed-off-by Hugo Villeneuve <[email protected]>
Thanks for following up on this, Hugo.
doc/README.nand More Definitions: · line 179
Quoted 3 lines
doc/README.nand +3
-0+ #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
This version should now be 1.3.4. Otherwise, ack.
Cheers,
Bernard.
PATCH 1/1 ARM DaVinci: Fix broken HW ECC for large page NAND.
In reply to Hugo VilleneuveQuoted 16 lines · Hugo Villeneuve
ARM DaVinci: Fix broken HW ECC for large page NAND.
Signed-off-by Hugo Villeneuve <[email protected]>---
Based on original patch by Bernard Blackham <[email protected]>
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.
Quoted 8 lines
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.
Applied to u-boot-nand-flash. Next time, please put the commit message above the "---", except for extra commentary that isn't intended to go into the git history.
-Scott