summaryrefslogtreecommitdiff
path: root/firmware/target
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target')
-rw-r--r--firmware/target/arm/ata-nand-telechips.c144
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 */
37int ata_spinup_time = 0; 40int ata_spinup_time = 0;
@@ -140,12 +143,6 @@ static struct write_cache write_caches[MAX_WRITE_CACHES];
140 143
141static int write_caches_in_use = 0; 144static int write_caches_in_use = 0;
142 145
143#ifdef USE_ECC_CORRECTION
144static unsigned int ecc_sectors_corrected = 0;
145static unsigned int ecc_bits_corrected = 0;
146static 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
318static void nand_read_raw(int bank, int row, int column, int size, void* buf) 315static 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
358static void nand_end_read(void)
359{
360 nand_chip_select(-1);
361
362 /* Disable NFC bus clock */
363 BCLKCTR &= ~DEV_NAND;
364}
365
366
367static 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)
477static bool nand_read_sector_of_phys_page(int bank, int page, 488static 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