@ -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 ) ;