summaryrefslogtreecommitdiff
path: root/firmware/target
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target')
-rw-r--r--firmware/target/arm/tcc780x/ata-nand-tcc780x.c379
1 files changed, 276 insertions, 103 deletions
diff --git a/firmware/target/arm/tcc780x/ata-nand-tcc780x.c b/firmware/target/arm/tcc780x/ata-nand-tcc780x.c
index bd7e9bb336..906635c51a 100644
--- a/firmware/target/arm/tcc780x/ata-nand-tcc780x.c
+++ b/firmware/target/arm/tcc780x/ata-nand-tcc780x.c
@@ -38,6 +38,10 @@ static bool initialized = false;
38static long next_yield = 0; 38static long next_yield = 0;
39#define MIN_YIELD_PERIOD 2000 39#define MIN_YIELD_PERIOD 2000
40 40
41static struct mutex ata_mtx NOCACHEBSS_ATTR;
42
43#define SECTOR_SIZE 512
44
41/* TCC780x NAND Flash Controller */ 45/* TCC780x NAND Flash Controller */
42 46
43#define NFC_CMD (*(volatile unsigned long *)0xF0053000) 47#define NFC_CMD (*(volatile unsigned long *)0xF0053000)
@@ -59,18 +63,27 @@ static long next_yield = 0;
59static int page_size = 0; 63static int page_size = 0;
60static int spare_size = 0; 64static int spare_size = 0;
61static int pages_per_block = 0; 65static int pages_per_block = 0;
62static int total_blocks = 0; 66static int blocks_per_bank = 0;
63static int total_pages = 0; 67static int pages_per_bank = 0;
64static int row_cycles = 0; 68static int row_cycles = 0;
65static int col_cycles = 0; 69static int col_cycles = 0;
66static int total_banks = 0; 70static int total_banks = 0;
71static int sectors_per_page = 0;
72static int bytes_per_segment = 0;
73static int sectors_per_segment = 0;
67 74
68/* Static page buffer */ 75/* Maximum values for static buffers */
69 76
70#define MAX_PAGE_SIZE 4096 77#define MAX_PAGE_SIZE 4096
71#define MAX_SPARE_SIZE 128 78#define MAX_SPARE_SIZE 128
79#define MAX_BLOCKS_PER_BANK 8192
80#define MAX_BANKS 4
72 81
73static int page_buf[(MAX_PAGE_SIZE+MAX_SPARE_SIZE)/4]; 82/*
83 Block translation table - maps logical Segment Number to physical page address
84 Format: 0xBTPPPPPP (B = Bank; T = Block Type flag; P = Page Address)
85 */
86static int segment_location[MAX_BLOCKS_PER_BANK * MAX_BANKS / 4];
74 87
75 88
76static void nand_chip_select(int chip) 89static void nand_chip_select(int chip)
@@ -201,9 +214,13 @@ static void nand_read_uid(int chip, unsigned int* uid_buf)
201 214
202 215
203/* NB: size must be divisible by 4 due to 32-bit read */ 216/* NB: size must be divisible by 4 due to 32-bit read */
204static void nand_read(int chip, int row, int column, int size) 217static void nand_read_raw(int chip, int row, int column, int size, void* buf)
205{ 218{
206 int i; 219 int i;
220
221 /* Currently this relies on a word-aligned input buffer */
222 unsigned int* int_buf = (unsigned int*)buf;
223 if ((unsigned int)buf & 3) panicf("nand_read_raw() non-aligned input buffer");
207 224
208 /* Enable NFC bus clock */ 225 /* Enable NFC bus clock */
209 BCLKCTR |= DEV_NAND; 226 BCLKCTR |= DEV_NAND;
@@ -245,7 +262,7 @@ static void nand_read(int chip, int row, int column, int size)
245 /* Read data into page buffer */ 262 /* Read data into page buffer */
246 for (i = 0; i < (size/4); i++) 263 for (i = 0; i < (size/4); i++)
247 { 264 {
248 page_buf[i] = NFC_WDATA; 265 int_buf[i] = NFC_WDATA;
249 } 266 }
250 267
251 nand_chip_select(-1); 268 nand_chip_select(-1);
@@ -255,89 +272,42 @@ static void nand_read(int chip, int row, int column, int size)
255} 272}
256 273
257 274
258/* TEMP testing function */ 275/* NB: Output buffer must currently be word-aligned */
259#include "lcd.h" 276static bool nand_read_sector(int segment, int sector, void* buf)
260
261extern int line;
262static unsigned char str_buf[MAX_PAGE_SIZE];
263
264static void nand_test(void)
265{ 277{
266 int i,j,row; 278 int physaddr = segment_location[segment];
267 int pages_per_mb = 1048576/page_size; 279 int bank = physaddr >> 28;
268 280 int page = physaddr & 0xffffff;
269 printf("%d banks", total_banks);
270 printf("* %d pages", total_pages);
271 printf("* %d bytes per page", page_size);
272 281
273 while (!button_read_device()) {}; 282 int page_in_seg = sector / sectors_per_page;
283 int sec_in_page = sector % sectors_per_page;
284
285 /* TODO: Check if there are any 0x15 pages referring to this segment/sector
286 combination. If present we need to read that data instead. */
274 287
275 /* Now for fun, scan the raw pages for 'TAG' and display the contents */ 288 if (physaddr == -1) return false;
276 289
277 row = 0; 290 if (page_in_seg & 1)
278 while (row < total_pages)
279 { 291 {
280 bool found = false; 292 /* Data is located in block+1 */
281 unsigned char* buf_ptr = (unsigned char*)page_buf; 293 page += pages_per_block;
282 294 }
283 line = 0;
284
285 if (row % pages_per_mb == 0) printf("%dMb", row/pages_per_mb);
286
287 /* Read a page from chip 0 */
288 nand_read(0, row, 0, page_size);
289
290 for (j = 0; j < page_size; j++)
291 {
292 if (buf_ptr[j] == 'T' && buf_ptr[j+1] == 'A' && buf_ptr[j+2] == 'G')
293 found = true;
294 }
295
296 if (found)
297 {
298 unsigned char* str_ptr = str_buf;
299
300 printf("Row %d:", row);
301
302 /* Copy ascii-readable parts out to a string */
303 for (i = 0; i < page_size; i++)
304 {
305 str_buf[i] = ' ';
306 if (buf_ptr[i] > 31 && buf_ptr[i] < 128)
307 {
308 *str_ptr++ = buf_ptr[i];
309 }
310 }
311
312 str_ptr = str_buf;
313 295
314 /* Nasty piece of code to display the text in a readable manner */ 296 if (page_in_seg & 2)
315 for (i = 1; i < 30; i++) 297 {
316 { 298 /* Data is located in second plane */
317 for (j = 0; j < 48; j++) 299 page += (blocks_per_bank/2) * pages_per_block;
318 { 300 }
319 /* In the absence of a putc() we have this mess... */
320 unsigned char buf2[2];
321 buf2[0] = *str_ptr++;
322 buf2[1] = '\0';
323 lcd_puts(j,i,buf2);
324 }
325 }
326 301
327 /* Alternate hex display code 302 page += page_in_seg/4;
328 for (i = 0; i<112; i+=4)
329 {
330 printf("0x%08x 0x%08x 0x%08x 0x%08x",
331 page_buf[i],page_buf[i+1],page_buf[i+2],page_buf[i+3]);
332 }
333 */
334 303
335 while (!button_read_device()) {}; 304 nand_read_raw(bank, page,
305 sec_in_page * (SECTOR_SIZE+16),
306 SECTOR_SIZE, buf);
336 307
337 lcd_clear_display(); 308 /* TODO: Read the 16 spare bytes, perform ECC correction */
338 } 309
339 row++; 310 return true;
340 }
341} 311}
342 312
343 313
@@ -345,7 +315,7 @@ static void nand_get_chip_info(void)
345{ 315{
346 bool found = false; 316 bool found = false;
347 unsigned char manuf_id; 317 unsigned char manuf_id;
348 unsigned char id_buf[5]; 318 unsigned char id_buf[8];
349 319
350 /* Read chip id from bank 0 */ 320 /* Read chip id from bank 0 */
351 nand_read_id(0, id_buf); 321 nand_read_id(0, id_buf);
@@ -363,7 +333,7 @@ static void nand_get_chip_info(void)
363 page_size = 2048; 333 page_size = 2048;
364 spare_size = 64; 334 spare_size = 64;
365 pages_per_block = 128; 335 pages_per_block = 128;
366 total_blocks = 8192; 336 blocks_per_bank = 8192;
367 col_cycles = 2; 337 col_cycles = 2;
368 row_cycles = 3; 338 row_cycles = 3;
369 339
@@ -375,7 +345,7 @@ static void nand_get_chip_info(void)
375 page_size = 4096; 345 page_size = 4096;
376 spare_size = 128; 346 spare_size = 128;
377 pages_per_block = 128; 347 pages_per_block = 128;
378 total_blocks = 8192; 348 blocks_per_bank = 8192;
379 col_cycles = 2; 349 col_cycles = 2;
380 row_cycles = 3; 350 row_cycles = 3;
381 351
@@ -391,10 +361,12 @@ static void nand_get_chip_info(void)
391 id_buf[0],id_buf[1],id_buf[2],id_buf[3],id_buf[4]); 361 id_buf[0],id_buf[1],id_buf[2],id_buf[3],id_buf[4]);
392 } 362 }
393 363
394 total_pages = total_blocks * pages_per_block; 364 pages_per_bank = blocks_per_bank * pages_per_block;
365 bytes_per_segment = page_size * pages_per_block * 4;
366 sectors_per_page = page_size / SECTOR_SIZE;
367 sectors_per_segment = bytes_per_segment / SECTOR_SIZE;
395 368
396 /* Establish how many banks are present */ 369 /* Establish how many banks are present */
397
398 nand_read_id(1, id_buf); 370 nand_read_id(1, id_buf);
399 371
400 if (id_buf[0] == manuf_id) 372 if (id_buf[0] == manuf_id)
@@ -433,7 +405,99 @@ static void nand_get_chip_info(void)
433 /* Bank 1 returned differing id - assume it is junk */ 405 /* Bank 1 returned differing id - assume it is junk */
434 total_banks = 1; 406 total_banks = 1;
435 } 407 }
408
409 /* Check block 0, page 0 for "BMPM" string & total_banks byte. If this is
410 confirmed for all D2s we can remove the above code & nand_read_uid(). */
411
412 nand_read_raw(0, /* bank */
413 0, /* page */
414 page_size, /* offset */
415 8, id_buf);
416
417 if (strncmp(id_buf, "BMPM", 4)) panicf("BMPM tag not present");
418 if (id_buf[4] != total_banks) panicf("BMPM total_banks mismatch");
419}
420
421
422/* TEMP testing function */
423
424#ifdef BOOTLOADER
425#include "lcd.h"
426
427extern int line;
428unsigned int buf[(MAX_PAGE_SIZE + MAX_SPARE_SIZE) / 4];
429
430static void nand_test(void)
431{
432 int i;
433 unsigned int seq_segments = 0;
434#if 0
435 int chip,page;
436#endif
437
438 printf("%d banks", total_banks);
439 printf("* %d pages", pages_per_bank);
440 printf("* %d bytes per page", page_size);
441
442 i = 0;
443 while (segment_location[i] != -1
444 && i++ < (blocks_per_bank * total_banks / 4))
445 {
446 seq_segments++;
447 }
448 printf("%d sequential segments found (%dMb)", seq_segments,
449 (seq_segments*bytes_per_segment)>>20);
450
451 while (!button_read_device()) {};
452 while (button_read_device()) {};
453
454#if 0
455 /* Read & display sequential pages */
456 for (chip = 0; chip < total_banks; chip++)
457 {
458 for (page = 0x0; page < 0x100; page++)
459 {
460 nand_read_raw(chip, page, 0, page_size+spare_size, buf);
461
462 for (i = 0; i < (page_size+spare_size)/4; i += 132)
463 {
464 int j,interesting = 0;
465 line = 0;
466 printf("c:%d p:%lx i:%d", chip, page, i);
467
468 for (j=i; j<(i+131); j++)
469 {
470 if (buf[j] != 0xffffffff) interesting = 1;
471 }
472
473 if (interesting)
474 {
475 for (j=i; j<(i+63); j+=4)
476 {
477 printf("%lx %lx %lx %lx",
478 buf[j], buf[j+1], buf[j+2], buf[j+3]);
479 }
480 printf("--->");
481 while (!button_read_device()) {};
482 while (button_read_device()) {};
483
484 line = 1;
485 printf("<---");
486 for (j=j; j<(i+131); j+=4)
487 {
488 printf("%lx %lx %lx %lx",
489 buf[j], buf[j+1], buf[j+2], buf[j+3]);
490 }
491 while (!button_read_device()) {};
492 while (button_read_device()) {};
493 reset_screen();
494 }
495 }
496 }
497 }
498#endif
436} 499}
500#endif
437 501
438 502
439/* API Functions */ 503/* API Functions */
@@ -446,10 +510,40 @@ void ata_led(bool onoff)
446int ata_read_sectors(IF_MV2(int drive,) unsigned long start, int incount, 510int ata_read_sectors(IF_MV2(int drive,) unsigned long start, int incount,
447 void* inbuf) 511 void* inbuf)
448{ 512{
449 #warning function not implemented 513#ifdef HAVE_MULTIVOLUME
450 (void)start; 514 (void)drive; /* unused for now */
451 (void)incount; 515#endif
452 (void)inbuf; 516 mutex_lock(&ata_mtx);
517
518 while (incount > 0)
519 {
520 int done = 0;
521 int segment = start / sectors_per_segment;
522 int secmod = start % sectors_per_segment;
523
524 while (incount > 0 && secmod < sectors_per_segment)
525 {
526 if (!nand_read_sector(segment, secmod, inbuf))
527 {
528 mutex_unlock(&ata_mtx);
529 return -1;
530 }
531
532 inbuf += SECTOR_SIZE;
533 incount--;
534 secmod++;
535 done++;
536 }
537
538 if (done < 0)
539 {
540 mutex_unlock(&ata_mtx);
541 return -1;
542 }
543 start += done;
544 }
545
546 mutex_unlock(&ata_mtx);
453 return 0; 547 return 0;
454} 548}
455 549
@@ -471,13 +565,13 @@ void ata_spindown(int seconds)
471 565
472bool ata_disk_is_active(void) 566bool ata_disk_is_active(void)
473{ 567{
474 #warning function not implemented 568 /* null */
475 return 0; 569 return 0;
476} 570}
477 571
478void ata_sleep(void) 572void ata_sleep(void)
479{ 573{
480 #warning function not implemented 574 /* null */
481} 575}
482 576
483void ata_spin(void) 577void ata_spin(void)
@@ -488,13 +582,13 @@ void ata_spin(void)
488/* Hardware reset protocol as specified in chapter 9.1, ATA spec draft v5 */ 582/* Hardware reset protocol as specified in chapter 9.1, ATA spec draft v5 */
489int ata_hard_reset(void) 583int ata_hard_reset(void)
490{ 584{
491 #warning function not implemented 585 /* null */
492 return 0; 586 return 0;
493} 587}
494 588
495int ata_soft_reset(void) 589int ata_soft_reset(void)
496{ 590{
497 #warning function not implemented 591 /* null */
498 return 0; 592 return 0;
499} 593}
500 594
@@ -506,20 +600,99 @@ void ata_enable(bool on)
506 600
507int ata_init(void) 601int ata_init(void)
508{ 602{
509 if (!initialized) 603 int i, bank, page;
510 { 604 unsigned int spare_buf[4];
511 /* Get chip characteristics and number of banks */ 605
512 nand_get_chip_info(); 606 if (initialized) return 0;
513 607
514 /* TODO: Scan all banks for bad blocks */ 608 /* Get chip characteristics and number of banks */
609 nand_get_chip_info();
515 610
516 /* TODO: Build physical->logical address translation */ 611 for (i = 0; i < (MAX_BLOCKS_PER_BANK * MAX_BANKS / 4); i++)
517 612 {
518 initialized = true; 613 segment_location[i] = -1;
614 }
615
616 /* Scan banks to build up block translation table */
617 for (bank = 0; bank < total_banks; bank++)
618 {
619 for (page = 0; page < pages_per_bank/2; page += pages_per_block*2)
620 {
621 unsigned char segment_flag;
622 unsigned char stored_flag;
623 unsigned short segment_id;
624
625 unsigned char* buf_ptr = (unsigned char*)spare_buf;
626
627 /* Read spare bytes from first sector of each segment */
628 nand_read_raw(bank, page,
629 SECTOR_SIZE, /* offset */
630 16, spare_buf);
631
632 segment_id = (buf_ptr[6] << 8) | buf_ptr[7];
633 segment_flag = buf_ptr[4];
634
635 stored_flag = (segment_location[segment_id] >> 24) & 0xf;
636
637#if defined(BOOTLOADER) && 0
638 if (segment_flag == 0x15)
639 {
640 printf("Segment %lx: c:%lx p:%lx, type:%lx, stored:x%lx",
641 segment_id, bank, page, segment_flag, stored_flag);
642 while (!button_read_device()) {};
643 while (button_read_device()) {};
644 }
645#endif
646
647 if (segment_flag == 0x13 || segment_flag == 0x17)
648 {
649 if (segment_id < (blocks_per_bank * total_banks / 4))
650 {
651#if defined(BOOTLOADER) && 0
652 if (segment_location[segment_id] != -1 && stored_flag != 0x3)
653 {
654 int orig_bank = segment_location[segment_id] >> 28;
655 int orig_page = segment_location[segment_id] & 0xFFFFFF;
656
657 printf("Segment %d already set! (stored flag:x%lx)",
658 segment_id, stored_flag);
659
660 printf("0x%08x 0x%08x 0x%08x 0x%08x",
661 spare_buf[0],spare_buf[1],spare_buf[2],spare_buf[3]);
662
663 nand_read_raw(orig_bank, orig_page,
664 SECTOR_SIZE,
665 16, spare_buf);
666
667 printf("0x%08x 0x%08x 0x%08x 0x%08x",
668 spare_buf[0],spare_buf[1],spare_buf[2],spare_buf[3]);
669 }
670#endif
671 /* Write bank, block type & physical address into table */
672 segment_location[segment_id]
673 = page | (bank << 28) | ((segment_flag & 0xf) << 24);
674 }
675 else
676 {
677 panicf("Invalid segment id:%d found", segment_id);
678 }
679 }
680 }
519 } 681 }
682
683 initialized = true;
520 684
685#ifdef BOOTLOADER
521 /* TEMP - print out some diagnostics */ 686 /* TEMP - print out some diagnostics */
522 nand_test(); 687 nand_test();
688#endif
523 689
524 return 0; 690 return 0;
525} 691}
692
693
694/* TEMP: This will return junk, it's here for compilation only */
695unsigned short* ata_get_identify(void)
696{
697 return (unsigned short*)0x21000000; /* Unused DRAM */
698}