diff options
author | Rob Purchase <shotofadds@rockbox.org> | 2009-08-12 19:26:04 +0000 |
---|---|---|
committer | Rob Purchase <shotofadds@rockbox.org> | 2009-08-12 19:26:04 +0000 |
commit | e783d0c82a6673d036a71f3eab3e69f95d4b0b37 (patch) | |
tree | 4e3966d266b4e8858bd4a016b5e828a73f364f24 /firmware/target/arm/ata-nand-telechips.c | |
parent | 4c5ae4b06853a8aee9a4bda7d06e148a52d20285 (diff) | |
download | rockbox-e783d0c82a6673d036a71f3eab3e69f95d4b0b37.tar.gz rockbox-e783d0c82a6673d036a71f3eab3e69f95d4b0b37.zip |
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
Diffstat (limited to 'firmware/target/arm/ata-nand-telechips.c')
-rw-r--r-- | firmware/target/arm/ata-nand-telechips.c | 144 |
1 files changed, 123 insertions, 21 deletions
diff --git a/firmware/target/arm/ata-nand-telechips.c b/firmware/target/arm/ata-nand-telechips.c index ffe6de897e..7250211eb9 100644 --- a/firmware/target/arm/ata-nand-telechips.c +++ b/firmware/target/arm/ata-nand-telechips.c | |||
@@ -31,7 +31,10 @@ | |||
31 | 31 | ||
32 | #define SECTOR_SIZE 512 | 32 | #define SECTOR_SIZE 512 |
33 | 33 | ||
34 | /* #define USE_ECC_CORRECTION */ | 34 | /* ECC on read is implemented on the assumption that MLC-style 4-bit correction |
35 | is always used regardless of NAND chip type. This assumption is true for at | ||
36 | least D2 (MLC) and M200 (SLC). */ | ||
37 | #define USE_ECC_CORRECTION | ||
35 | 38 | ||
36 | /* for compatibility */ | 39 | /* for compatibility */ |
37 | int ata_spinup_time = 0; | 40 | int ata_spinup_time = 0; |
@@ -140,12 +143,6 @@ static struct write_cache write_caches[MAX_WRITE_CACHES]; | |||
140 | 143 | ||
141 | static int write_caches_in_use = 0; | 144 | static int write_caches_in_use = 0; |
142 | 145 | ||
143 | #ifdef USE_ECC_CORRECTION | ||
144 | static unsigned int ecc_sectors_corrected = 0; | ||
145 | static unsigned int ecc_bits_corrected = 0; | ||
146 | static unsigned int ecc_fail_count = 0; | ||
147 | #endif | ||
148 | |||
149 | 146 | ||
150 | /* Conversion functions */ | 147 | /* Conversion functions */ |
151 | 148 | ||
@@ -315,7 +312,7 @@ static void nand_read_uid(int bank, unsigned int* uid_buf) | |||
315 | } | 312 | } |
316 | 313 | ||
317 | 314 | ||
318 | static void nand_read_raw(int bank, int row, int column, int size, void* buf) | 315 | static void nand_setup_read(int bank, int row, int column) |
319 | { | 316 | { |
320 | int i; | 317 | int i; |
321 | 318 | ||
@@ -355,6 +352,23 @@ static void nand_read_raw(int bank, int row, int column, int size, void* buf) | |||
355 | 352 | ||
356 | /* Wait until complete */ | 353 | /* Wait until complete */ |
357 | while (!(NFC_CTRL & NFC_READY)) {}; | 354 | while (!(NFC_CTRL & NFC_READY)) {}; |
355 | } | ||
356 | |||
357 | |||
358 | static void nand_end_read(void) | ||
359 | { | ||
360 | nand_chip_select(-1); | ||
361 | |||
362 | /* Disable NFC bus clock */ | ||
363 | BCLKCTR &= ~DEV_NAND; | ||
364 | } | ||
365 | |||
366 | |||
367 | static void nand_read_raw(int bank, int row, int column, int size, void* buf) | ||
368 | { | ||
369 | int i; | ||
370 | |||
371 | nand_setup_read(bank, row, column); | ||
358 | 372 | ||
359 | /* Read data into page buffer */ | 373 | /* Read data into page buffer */ |
360 | if (((unsigned int)buf & 3) || (size & 3)) | 374 | 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) | |||
374 | ((unsigned int*)buf)[i] = NFC_WDATA; | 388 | ((unsigned int*)buf)[i] = NFC_WDATA; |
375 | } | 389 | } |
376 | } | 390 | } |
377 | 391 | ||
378 | nand_chip_select(-1); | 392 | nand_end_read(); |
379 | |||
380 | /* Disable NFC bus clock */ | ||
381 | BCLKCTR &= ~DEV_NAND; | ||
382 | } | 393 | } |
383 | 394 | ||
384 | 395 | ||
@@ -477,15 +488,106 @@ static void nand_get_chip_info(void) | |||
477 | static bool nand_read_sector_of_phys_page(int bank, int page, | 488 | static bool nand_read_sector_of_phys_page(int bank, int page, |
478 | int sector, void* buf) | 489 | int sector, void* buf) |
479 | { | 490 | { |
480 | #ifndef USE_ECC_CORRECTION | 491 | bool ret = true; |
481 | nand_read_raw(bank, page, | 492 | int i; |
482 | sector * (SECTOR_SIZE+16), | 493 | int page_offset = sector * (SECTOR_SIZE + 16); |
483 | SECTOR_SIZE, buf); | 494 | |
484 | return true; | 495 | #ifdef USE_ECC_CORRECTION |
485 | #else | 496 | unsigned long spare_buf[4]; |
486 | /* Not yet implemented */ | 497 | |
487 | return false; | 498 | /* Set up the ECC controller to monitor reads from NFC_WDATA */ |
499 | BCLKCTR |= DEV_ECC; | ||
500 | ECC_BASE = (unsigned long)&NFC_WDATA; | ||
501 | ECC_CTRL |= ECC_M4EN; | ||
502 | ECC_CTRL &= ~ECC_ENC; | ||
503 | ECC_CTRL |= ECC_READY; | ||
504 | ECC_CLR = 0; | ||
488 | #endif | 505 | #endif |
506 | |||
507 | /* Read the sector data */ | ||
508 | nand_setup_read(bank, page, page_offset); | ||
509 | |||
510 | /* Read data into page buffer */ | ||
511 | if ((unsigned int)buf & 3) | ||
512 | { | ||
513 | /* If unaligned, read into a temporary buffer and copy to destination. | ||
514 | This way, reads are always done through NFC_WDATA - otherwise they | ||
515 | would not be 'seen' by the ECC controller. */ | ||
516 | static char temp_buf[SECTOR_SIZE]; | ||
517 | |||
518 | unsigned int* ptr = (unsigned int*) temp_buf; | ||
519 | |||
520 | for (i = 0; i < (SECTOR_SIZE/4); i++) | ||
521 | { | ||
522 | *ptr++ = NFC_WDATA; | ||
523 | } | ||
524 | |||
525 | memcpy(buf, temp_buf, SECTOR_SIZE); | ||
526 | } | ||
527 | else | ||
528 | { | ||
529 | /* Use straight word copy as buffer and size are both word-aligned */ | ||
530 | unsigned int* ptr = (unsigned int*) buf; | ||
531 | |||
532 | for (i = 0; i < (SECTOR_SIZE/4); i++) | ||
533 | { | ||
534 | *ptr++ = NFC_WDATA; | ||
535 | } | ||
536 | } | ||
537 | |||
538 | #ifdef USE_ECC_CORRECTION | ||
539 | /* Stop monitoring before we read the OOB data */ | ||
540 | ECC_CTRL &= ~ECC_M4EN; | ||
541 | BCLKCTR &= ~DEV_ECC; | ||
542 | |||
543 | /* Read a further 4 words (sector OOB data) */ | ||
544 | spare_buf[0] = NFC_WDATA; | ||
545 | spare_buf[1] = NFC_WDATA; | ||
546 | spare_buf[2] = NFC_WDATA; | ||
547 | spare_buf[3] = NFC_WDATA; | ||
548 | |||
549 | /* Calculate MLC4 ECC using bytes 0,1,8-15 */ | ||
550 | BCLKCTR |= DEV_ECC; | ||
551 | ECC_CTRL |= ECC_M4EN; | ||
552 | |||
553 | MLC_ECC0W = *(unsigned short*)spare_buf; | ||
554 | MLC_ECC1W = spare_buf[2]; | ||
555 | MLC_ECC2W = spare_buf[3]; | ||
556 | |||
557 | while (!(ECC_CTRL & ECC_READY)) {}; | ||
558 | |||
559 | int errors = ECC_ERR_NUM & 7; | ||
560 | |||
561 | switch (errors) | ||
562 | { | ||
563 | case 4: /* nothing to correct */ | ||
564 | break; | ||
565 | |||
566 | case 7: /* fail, can't correct */ | ||
567 | ret = false; | ||
568 | break; | ||
569 | |||
570 | default: /* between 1 and 4 errors */ | ||
571 | { | ||
572 | int i; | ||
573 | unsigned char* char_buf = (unsigned char*)buf; | ||
574 | |||
575 | for (i = 0; i < errors + 1; i++) | ||
576 | { | ||
577 | int offset = 0x207 - ECC_ERRADDR(i); | ||
578 | char_buf[offset] ^= ECC_ERRDATA(i); | ||
579 | } | ||
580 | } | ||
581 | } | ||
582 | |||
583 | /* Disable ECC block */ | ||
584 | ECC_CTRL &= ~ECC_M4EN; | ||
585 | BCLKCTR &= ~DEV_ECC; | ||
586 | #endif | ||
587 | |||
588 | nand_end_read(); | ||
589 | |||
590 | return ret; | ||
489 | } | 591 | } |
490 | 592 | ||
491 | 593 | ||