TCC: Implement ECC error correction for sectors read from NAND. Tested on D2 (78x, MLC) and M200 (77x, SLC).

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22284 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Rob Purchase 2009-08-12 19:26:04 +00:00
parent 4c5ae4b068
commit e783d0c82a
3 changed files with 169 additions and 75 deletions

View file

@ -182,51 +182,43 @@
/* ECC controller */
#define ECC_CTRL (*(volatile unsigned long *)0x80000900)
#define ECC_DMA_REQ (1<<28)
#define ECC_ENC (1<<27) /* MLC ECC3/4 */
#define ECC_FLG (1<<26)
#define ECC_IEN (1<<25)
#define ECC_MANUAL (1<<22)
#define ECC_WCNT (1<<12) /* [21:12] */
#define ECC_HOLD (1<<7)
#define ECC_M4EN (1<<6)
#define ECC_ZERO (1<<5)
#define ECC_M3EN (1<<4)
#define ECC_CNT_MASK (7<<1)
#define ECC_CNT (1<<1)
#define ECC_SLC (1<<0)
#define ECC_CTRL (*(volatile unsigned long *)0x80000900)
#define ECC_DMA_REQ (1<<28)
#define ECC_ENC (1<<27) /* MLC ECC3/4 */
#define ECC_READY (1<<26)
#define ECC_IEN (1<<25)
#define ECC_MANUAL (1<<22)
#define ECC_WCNT (1<<12) /* [21:12] */
#define ECC_HOLD (1<<7)
#define ECC_M4EN (1<<6)
#define ECC_ZERO (1<<5)
#define ECC_M3EN (1<<4)
#define ECC_CNT_MASK (7<<1)
#define ECC_CNT (1<<1)
#define ECC_SLC (1<<0)
#define ECC_BASE (*(volatile unsigned long *)0x80000904)
#define ECC_MASK (*(volatile unsigned long *)0x80000908)
#define ECC_CLR (*(volatile unsigned long *)0x8000090c)
#define SLC_ECC0 (*(volatile unsigned long *)0x80000910)
#define SLC_ECC1 (*(volatile unsigned long *)0x80000914)
#define SLC_ECC2 (*(volatile unsigned long *)0x80000918)
#define SLC_ECC3 (*(volatile unsigned long *)0x8000091c)
#define SLC_ECC4 (*(volatile unsigned long *)0x80000920)
#define SLC_ECC5 (*(volatile unsigned long *)0x80000924)
#define SLC_ECC6 (*(volatile unsigned long *)0x80000928)
#define SLC_ECC7 (*(volatile unsigned long *)0x8000092c)
#define MLC_ECC0W (*(volatile unsigned long *)0x80000930)
#define MLC_ECC1W (*(volatile unsigned long *)0x80000934)
#define MLC_ECC2W (*(volatile unsigned long *)0x80000938)
#define MLC_ECC0R (*(volatile unsigned long *)0x80000940)
#define MLC_ECC1R (*(volatile unsigned long *)0x80000944)
#define MLC_ECC2R (*(volatile unsigned long *)0x80000948)
#define ECC_BASE (*(volatile unsigned long *)0x80000904)
#define ECC_MASK (*(volatile unsigned long *)0x80000908)
#define ECC_CLR (*(volatile unsigned long *)0x8000090c)
#define SLC_ECC0 (*(volatile unsigned long *)0x80000910)
#define SLC_ECC1 (*(volatile unsigned long *)0x80000914)
#define SLC_ECC2 (*(volatile unsigned long *)0x80000918)
#define SLC_ECC3 (*(volatile unsigned long *)0x8000091c)
#define SLC_ECC4 (*(volatile unsigned long *)0x80000920)
#define SLC_ECC5 (*(volatile unsigned long *)0x80000924)
#define SLC_ECC6 (*(volatile unsigned long *)0x80000928)
#define SLC_ECC7 (*(volatile unsigned long *)0x8000092c)
#define MLC_ECC0W (*(volatile unsigned long *)0x80000930)
#define MLC_ECC1W (*(volatile unsigned long *)0x80000934)
#define MLC_ECC2W (*(volatile unsigned long *)0x80000938)
#define MLC_ECC0R (*(volatile unsigned long *)0x80000940)
#define MLC_ECC1R (*(volatile unsigned long *)0x80000944)
#define MLC_ECC2R (*(volatile unsigned long *)0x80000948)
#define ECC_CORR_START (*(volatile unsigned long *)0x8000094c)
#define ECC_ERRADDR1 (*(volatile unsigned long *)0x80000950)
#define ECC_ERRADDR2 (*(volatile unsigned long *)0x80000954)
#define ECC_ERRADDR3 (*(volatile unsigned long *)0x80000958)
#define ECC_ERRADDR4 (*(volatile unsigned long *)0x8000095c)
#define ECC_ERRDATA1 (*(volatile unsigned long *)0x80000960)
#define ECC_ERRDATA2 (*(volatile unsigned long *)0x80000964)
#define ECC_ERRDATA3 (*(volatile unsigned long *)0x80000968)
#define ECC_ERRDATA4 (*(volatile unsigned long *)0x8000096c)
#define ECC_ERR_NUM (*(volatile unsigned long *)0x80000970)
#define ECC_ERRADDR(x) (*(volatile unsigned long *)(0x80000950+4*(x)))
#define ECC_ERRDATA(x) (*(volatile unsigned long *)(0x80000960+4*(x)))
#define ECC_ERR_NUM (*(volatile unsigned long *)0x80000970)
#define ECC_ERRDATA(x) (*(volatile unsigned long *)(0x80000960 + (x) * 4))
#define ECC_ERRADDR(x) (*(volatile unsigned long *)(0x80000950 + (x) * 4))
/* Digital Audio Interface */
#define DADI_L0 (*(volatile unsigned long *)0x80000000)

View file

@ -232,18 +232,18 @@
/* ECC Controller */
#define ECC_CTRL (*(volatile unsigned long *)0xF005B000)
#define ECC_M4EN (1<<6)
#define ECC_ENC (1<<27)
#define ECC_READY (1<<26)
#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_ERRADDR (*(volatile unsigned long *)0xF005B050)
#define ECC_ERRDATA (*(volatile unsigned long *)0xF005B060)
#define ECC_ERR (*(volatile unsigned long *)0xF005B070)
#define ECC_CTRL (*(volatile unsigned long *)0xF005B000)
#define ECC_ENC (1<<27)
#define ECC_READY (1<<26)
#define ECC_M4EN (1<<6)
#define ECC_BASE (*(volatile unsigned long *)0xF005B004)
#define ECC_CLR (*(volatile unsigned long *)0xF005B00C)
#define MLC_ECC0W (*(volatile unsigned long *)0xF005B030)
#define MLC_ECC1W (*(volatile unsigned long *)0xF005B034)
#define MLC_ECC2W (*(volatile unsigned long *)0xF005B038)
#define ECC_ERRADDR(x) (*(volatile unsigned long *)(0xF005B050+4*(x)))
#define ECC_ERRDATA(x) (*(volatile unsigned long *)(0xF005B060+4*(x)))
#define ECC_ERR_NUM (*(volatile unsigned long *)0xF005B070)
/* SD/MMC Controller */

View file

@ -31,7 +31,10 @@
#define SECTOR_SIZE 512
/* #define USE_ECC_CORRECTION */
/* ECC on read is implemented on the assumption that MLC-style 4-bit correction
is always used regardless of NAND chip type. This assumption is true for at
least D2 (MLC) and M200 (SLC). */
#define USE_ECC_CORRECTION
/* for compatibility */
int ata_spinup_time = 0;
@ -140,12 +143,6 @@ static struct write_cache write_caches[MAX_WRITE_CACHES];
static int write_caches_in_use = 0;
#ifdef USE_ECC_CORRECTION
static unsigned int ecc_sectors_corrected = 0;
static unsigned int ecc_bits_corrected = 0;
static unsigned int ecc_fail_count = 0;
#endif
/* Conversion functions */
@ -315,7 +312,7 @@ static void nand_read_uid(int bank, unsigned int* uid_buf)
}
static void nand_read_raw(int bank, int row, int column, int size, void* buf)
static void nand_setup_read(int bank, int row, int column)
{
int i;
@ -355,6 +352,23 @@ static void nand_read_raw(int bank, int row, int column, int size, void* buf)
/* Wait until complete */
while (!(NFC_CTRL & NFC_READY)) {};
}
static void nand_end_read(void)
{
nand_chip_select(-1);
/* Disable NFC bus clock */
BCLKCTR &= ~DEV_NAND;
}
static void nand_read_raw(int bank, int row, int column, int size, void* buf)
{
int i;
nand_setup_read(bank, row, column);
/* Read data into page buffer */
if (((unsigned int)buf & 3) || (size & 3))
@ -375,10 +389,7 @@ static void nand_read_raw(int bank, int row, int column, int size, void* buf)
}
}
nand_chip_select(-1);
/* Disable NFC bus clock */
BCLKCTR &= ~DEV_NAND;
nand_end_read();
}
@ -477,15 +488,106 @@ static void nand_get_chip_info(void)
static bool nand_read_sector_of_phys_page(int bank, int page,
int sector, void* buf)
{
#ifndef USE_ECC_CORRECTION
nand_read_raw(bank, page,
sector * (SECTOR_SIZE+16),
SECTOR_SIZE, buf);
return true;
#else
/* Not yet implemented */
return false;
bool ret = true;
int i;
int page_offset = sector * (SECTOR_SIZE + 16);
#ifdef USE_ECC_CORRECTION
unsigned long spare_buf[4];
/* Set up the ECC controller to monitor reads from NFC_WDATA */
BCLKCTR |= DEV_ECC;
ECC_BASE = (unsigned long)&NFC_WDATA;
ECC_CTRL |= ECC_M4EN;
ECC_CTRL &= ~ECC_ENC;
ECC_CTRL |= ECC_READY;
ECC_CLR = 0;
#endif
/* Read the sector data */
nand_setup_read(bank, page, page_offset);
/* Read data into page buffer */
if ((unsigned int)buf & 3)
{
/* If unaligned, read into a temporary buffer and copy to destination.
This way, reads are always done through NFC_WDATA - otherwise they
would not be 'seen' by the ECC controller. */
static char temp_buf[SECTOR_SIZE];
unsigned int* ptr = (unsigned int*) temp_buf;
for (i = 0; i < (SECTOR_SIZE/4); i++)
{
*ptr++ = NFC_WDATA;
}
memcpy(buf, temp_buf, SECTOR_SIZE);
}
else
{
/* Use straight word copy as buffer and size are both word-aligned */
unsigned int* ptr = (unsigned int*) buf;
for (i = 0; i < (SECTOR_SIZE/4); i++)
{
*ptr++ = NFC_WDATA;
}
}
#ifdef USE_ECC_CORRECTION
/* Stop monitoring before we read the OOB data */
ECC_CTRL &= ~ECC_M4EN;
BCLKCTR &= ~DEV_ECC;
/* Read a further 4 words (sector OOB data) */
spare_buf[0] = NFC_WDATA;
spare_buf[1] = NFC_WDATA;
spare_buf[2] = NFC_WDATA;
spare_buf[3] = NFC_WDATA;
/* Calculate MLC4 ECC using bytes 0,1,8-15 */
BCLKCTR |= DEV_ECC;
ECC_CTRL |= ECC_M4EN;
MLC_ECC0W = *(unsigned short*)spare_buf;
MLC_ECC1W = spare_buf[2];
MLC_ECC2W = spare_buf[3];
while (!(ECC_CTRL & ECC_READY)) {};
int errors = ECC_ERR_NUM & 7;
switch (errors)
{
case 4: /* nothing to correct */
break;
case 7: /* fail, can't correct */
ret = false;
break;
default: /* between 1 and 4 errors */
{
int i;
unsigned char* char_buf = (unsigned char*)buf;
for (i = 0; i < errors + 1; i++)
{
int offset = 0x207 - ECC_ERRADDR(i);
char_buf[offset] ^= ECC_ERRDATA(i);
}
}
}
/* Disable ECC block */
ECC_CTRL &= ~ECC_M4EN;
BCLKCTR &= ~DEV_ECC;
#endif
nand_end_read();
return ret;
}