mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-12-09 13:15:18 -05:00
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:
parent
4c5ae4b068
commit
e783d0c82a
3 changed files with 169 additions and 75 deletions
|
|
@ -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))
|
||||
|
|
@ -374,11 +388,8 @@ static void nand_read_raw(int bank, int row, int column, int size, void* buf)
|
|||
((unsigned int*)buf)[i] = NFC_WDATA;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue