Index: ata-nand-tcc780x.c =================================================================== --- ata-nand-tcc780x.c (revision 16464) +++ ata-nand-tcc780x.c (working copy) @@ -52,6 +52,19 @@ #define NFC_IREQ (*(volatile unsigned long *)0xF0053060) #define NFC_RST (*(volatile unsigned long *)0xF0053064) +#define ECC_CTRL (*(volatile unsigned long *)0xF005B000) +#define ECC_BASE (*(volatile unsigned long *)0xF005B004) +#define ECC_CLR (*(volatile unsigned long *)0xF005B00C) +#define ECC_MLC0W (*(volatile unsigned long *)0xF005B030) +#define ECC_MLC1W (*(volatile unsigned long *)0xF005B034) +#define ECC_MLC2W (*(volatile unsigned long *)0xF005B038) +#define ECC_ERR (*(volatile unsigned long *)0xF005B070) +#define ECC_ERRADDR (*(volatile unsigned long *)0xF005B050) +#define ECC_ERRDATA (*(volatile unsigned long *)0xF005B060) +#define ECC_M4EN (1<<6) +#define ECC_ENC (1<<27) +#define ECC_READY (1<<26) + /* NFC_CTRL flags */ #define NFC_16BIT (1<<26) #define NFC_CS0 (1<<23) @@ -271,7 +284,75 @@ BCLKCTR &= ~DEV_NAND; } +static void ecc_enable(bool enable, bool encode) +{ + if (enable) + { + BCLKCTR |= DEV_ECC; + ECC_BASE = (unsigned)&NFC_WDATA; + ECC_CTRL |= ECC_M4EN; + ECC_CTRL |= ECC_READY; + ECC_CLR = 0; + if (encode) + ECC_CTRL |= ECC_ENC; + else + ECC_CTRL &= ~ECC_ENC; + } + else + { + BCLKCTR |= DEV_ECC; + ECC_CTRL &= ~ECC_M4EN; + BCLKCTR &= ~DEV_ECC; + } +} +static bool ecc_correct(void *buf, void *spare) +{ + unsigned int errors; + unsigned long addr; + unsigned int i; + bool rv = true; + + BCLKCTR |= DEV_ECC; + ECC_CTRL |= ECC_M4EN; + ECC_CTRL &= ~ECC_ENC; + ECC_CTRL |= ECC_READY; + + /* Use bytes 0,1,8-15 for ECC */ + ECC_MLC0W = *(unsigned short *)spare; + ECC_MLC1W = *((unsigned long *)spare + 2); + ECC_MLC2W = *((unsigned long *)spare + 3); + + asm("nop\n nop\n nop\n nop\n nop\n nop\n"); + + while (!(ECC_CTRL & ECC_READY)); + + errors = ECC_ERR & 7; + if (errors == 7) + { + /* can't correct, fail. */ + rv = false; + } + else if (errors == 4) + { + /* no errors to correct */ + } + else + { + for (i = 0; i < errors + 1; i++) + { + addr = 0x207 - *(&ECC_ERRADDR + i); + ((char *)buf)[addr] ^= *(&ECC_ERRDATA + i); + } + /* printf("corrected errors"); */ + } + + ECC_CTRL &= ~ECC_M4EN; + BCLKCTR &= ~DEV_ECC; + + return rv; +} + /* NB: Output buffer must currently be word-aligned */ static bool nand_read_sector(int segment, int sector, void* buf) { @@ -281,7 +362,10 @@ int page_in_seg = sector / sectors_per_page; int sec_in_page = sector % sectors_per_page; - + int page_offset = sec_in_page * (SECTOR_SIZE + 16); + + unsigned char spare_buf[16]; + /* TODO: Check if there are any 0x15 pages referring to this segment/sector combination. If present we need to read that data instead. */ @@ -301,13 +385,11 @@ page += page_in_seg/4; - nand_read_raw(bank, page, - sec_in_page * (SECTOR_SIZE+16), - SECTOR_SIZE, buf); - - /* TODO: Read the 16 spare bytes, perform ECC correction */ - - return true; + ecc_enable(true, false); + nand_read_raw(bank, page, page_offset, SECTOR_SIZE, buf); + ecc_enable(false, false); + nand_read_raw(bank, page, page_offset + SECTOR_SIZE, 16, spare_buf); + return ecc_correct(buf, spare_buf); }