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 1master
parent
7eb4f90930
commit
1cfc889457
4
common.h
4
common.h
|
@ -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
68
nand.c
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue