nand: Detect and correct bit errors on the sysmoBTSv2D and later platforms

I disabled the initialization of the vector interrupt tables in
davinci.c and then I can use u-boot commands like these

load and start ubl
dhcp; set serverip 192.168.0.88; tftp ubl_sysmobts_v2.elf; bootelf

manipulate bits. Flip a 1 to a 0:

nand read.raw 0x85000000 0x00080000 1
mm.b 0x85000000
nand write.raw 0x85000000 0x00080000 1
This commit is contained in:
Holger Hans Peter Freyther 2014-04-29 16:35:49 +02:00
parent 7eb4f90930
commit 1cfc889457
2 changed files with 68 additions and 4 deletions

View File

@ -42,7 +42,7 @@
/* #define NAND_DEBUG_WRITE_RAMP 1 */
#if defined(board_sysmobts_v2)
#define UBL_VERSION_STR "sysmobts_v2 Loader v1.1.0 (" __DATE__ "-" __TIME__ ")"
#define UBL_VERSION_STR "sysmobts_v2 Loader v1.1.0a (" __DATE__ "-" __TIME__ ")"
#else
#define UBL_VERSION_STR "HV-UBL v0.2.11"
#endif
@ -50,7 +50,7 @@
/* Define this for bypassing the ECC check when reading from the NAND.
* This is useful for debugging or during development. */
#define NAND_BYPASS_READ_PAGE_ECC_CHECK 1
//#define NAND_BYPASS_READ_PAGE_ECC_CHECK 1
#define MAGIC_NUMBER_MASK 0xFFFFFF00
#define MAGIC_NUMBER_VALID 0xA1ACED00

68
nand.c
View File

@ -596,13 +596,77 @@ nand_read_spare(void)
return spare_ecc_temp;
}
/*
* GPLv2 code taken from the kernel to correct the bit error.
* The content of the ECC register is in the sprue20c.pdf. For
* the odd/even there are four bits spare each. The kernel is
* shifting this around and it is the easiest just to do the
* same.
*/
static uint32_t squeeze_ecc_bits(uint32_t tmp)
{
/* 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 */
return ~tmp;
}
static int nand_check_fix(
const uint32_t _calc_ecc,
const uint32_t _ecc_nand, uint8_t *dat, int size)
{
uint32_t ecc_nand, ecc_calc;
ecc_calc = squeeze_ecc_bits(_calc_ecc);
ecc_nand = squeeze_ecc_bits(_ecc_nand);
uint32_t diff = ecc_calc ^ ecc_nand;
if (diff) {
if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) {
/* Correctable error */
if ((diff >> (12 + 3)) < size) {
uint8_t find_bit = 1 << ((diff >> 12) & 7);
uint32_t find_byte = diff >> (12 + 3);
dat[find_byte] ^= find_bit;
log_info("Correcting single bit error.");
uart_send_str("BYTE=");
uart_send_hexnum(find_byte, 8);
uart_send_str(" BIT=");
uart_send_hexnum(find_bit, 8);
uart_send_lf();
return 1;
} else {
log_info("Too many bit errors:");
uart_send_hexnum(diff >> (12+3), 8);
uart_send_lf();
return -1;
}
} else if (!(diff & (diff - 1))) {
/* Single bit ECC error in the ECC itself,
nothing to fix */
log_info("Bit error in the spare data. Ignoring");
return 1;
} else {
/* Uncorrectable error */
log_info("Uncorrectable error");
return -1;
}
}
return 0;
}
/* Read a page from NAND */
int
nand_read_page(uint32_t block, uint32_t page, uint8_t *dest)
nand_read_page(uint32_t block, uint32_t page, uint8_t *orig_dest)
{
uint32_t hw_ecc[4];
uint32_t spare_ecc[4];
uint8_t numReads, i;
uint8_t *dest = orig_dest;
numReads = (nand_info.bytes_per_page >> 9); /* Divide by 512 */
if (numReads == 0)
@ -651,7 +715,7 @@ nand_read_page(uint32_t block, uint32_t page, uint8_t *dest)
#ifndef NAND_BYPASS_READ_PAGE_ECC_CHECK
for (i = 0; i < numReads; i++) {
/* Verify ECC values */
if (hw_ecc[i] != spare_ecc[i]) {
if (nand_check_fix(hw_ecc[i], spare_ecc[i], &orig_dest[i * nand_info.chunk_size], nand_info.chunk_size) < 0) {
log_info("NAND ECC failure:");
uart_send_str("HW = ");
uart_send_hexnum(hw_ecc[i], 8);