From addb5228ece29ce1a17591aa3c8078566ce1dbe6 Mon Sep 17 00:00:00 2001 From: Rob Purchase Date: Wed, 13 Feb 2008 23:50:44 +0000 Subject: D2: Further work-in-progress on the NAND driver. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@16308 a1c6a512-1295-4272-9138-f99709370657 --- bootloader/telechips.c | 74 ++++- firmware/target/arm/tcc780x/ata-nand-tcc780x.c | 379 ++++++++++++++++++------- 2 files changed, 345 insertions(+), 108 deletions(-) diff --git a/bootloader/telechips.c b/bootloader/telechips.c index f5669d0bbf..1918465aa2 100644 --- a/bootloader/telechips.c +++ b/bootloader/telechips.c @@ -54,14 +54,29 @@ void* main(void) int button; int power_count = 0; int count = 0; - int i; bool do_power_off = false; + +#if defined(COWON_D2) + int i,rc,fd,len; + int* buf = (int*)0x21000000; /* Unused DRAM */ +#endif + power_init(); + lcd_init(); system_init(); + +#if defined(COWON_D2) + kernel_init(); +#endif + adc_init(); - lcd_init(); + button_init(); + backlight_init(); + font_init(); + lcd_setfont(FONT_SYSFIXED); + #if defined(COWON_D2) lcd_enable(true); #endif @@ -69,7 +84,50 @@ void* main(void) _backlight_on(); #if defined(COWON_D2) - ata_init(); + printf("ATA"); + rc = ata_init(); + if(rc) + { + reset_screen(); + error(EATA, rc); + } + + printf("mount"); + rc = disk_mount_all(); + if (rc<=0) + { + error(EDISK,rc); + } + +#if 0 + printf("opening test file..."); + + fd = open("/test.bin", O_RDONLY); + if (fd < 0) panicf("could not open test file"); + + len = filesize(fd); + printf("Length: %x", len); + + lseek(fd, 0, SEEK_SET); + read(fd, buf, len); + close(fd); + + printf("testing contents..."); + + i = 0; + while (buf[i] == i && i<(len/4)) { i++; } + + if (i < len/4) + { + printf("mismatch at %x [0x%x]", i, buf[i]); + } + else + { + printf("passed!"); + } + while (!button_read_device()) {}; + while (button_read_device()) {}; +#endif #endif while(!do_power_off) { @@ -103,8 +161,9 @@ void* main(void) printf("ADC%d: 0x%04x",i,adc_read(i)); } - /* TODO: Establish how the touchscreen driver is going to work. - Since it needs I2C read/write, it can't easily go on a tick task */ + /* TODO: Move this stuff out to a touchscreen driver and establish + how such a beast is going to work. Since it needs I2C read/write, + it can't easily go on an interrupt-based tick task. */ { unsigned char buf[] = { 0x2f, (0xE<<1) | 1, /* ADC start for X+Y */ 0, 0, 0 }; @@ -115,6 +174,11 @@ void* main(void) y = (buf[4] << 2) | ((buf[3] & 0xC) >> 2); printf("X: 0x%03x Y: 0x%03x",x,y); + x = (x*LCD_WIDTH) / 1024; + y = (y*LCD_HEIGHT) / 1024; + lcd_hline(x-5, x+5, y); + lcd_vline(x, y-5, y+5); + buf[0] = 0x2f; buf[1] = (0xF<<1) | 1; /* ADC start for P1+P2 */ i2c_write(0x10, buf, 2); 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; static long next_yield = 0; #define MIN_YIELD_PERIOD 2000 +static struct mutex ata_mtx NOCACHEBSS_ATTR; + +#define SECTOR_SIZE 512 + /* TCC780x NAND Flash Controller */ #define NFC_CMD (*(volatile unsigned long *)0xF0053000) @@ -59,18 +63,27 @@ static long next_yield = 0; static int page_size = 0; static int spare_size = 0; static int pages_per_block = 0; -static int total_blocks = 0; -static int total_pages = 0; +static int blocks_per_bank = 0; +static int pages_per_bank = 0; static int row_cycles = 0; static int col_cycles = 0; static int total_banks = 0; +static int sectors_per_page = 0; +static int bytes_per_segment = 0; +static int sectors_per_segment = 0; -/* Static page buffer */ +/* Maximum values for static buffers */ -#define MAX_PAGE_SIZE 4096 -#define MAX_SPARE_SIZE 128 +#define MAX_PAGE_SIZE 4096 +#define MAX_SPARE_SIZE 128 +#define MAX_BLOCKS_PER_BANK 8192 +#define MAX_BANKS 4 -static int page_buf[(MAX_PAGE_SIZE+MAX_SPARE_SIZE)/4]; +/* + Block translation table - maps logical Segment Number to physical page address + Format: 0xBTPPPPPP (B = Bank; T = Block Type flag; P = Page Address) + */ +static int segment_location[MAX_BLOCKS_PER_BANK * MAX_BANKS / 4]; static void nand_chip_select(int chip) @@ -201,9 +214,13 @@ static void nand_read_uid(int chip, unsigned int* uid_buf) /* NB: size must be divisible by 4 due to 32-bit read */ -static void nand_read(int chip, int row, int column, int size) +static void nand_read_raw(int chip, int row, int column, int size, void* buf) { int i; + + /* Currently this relies on a word-aligned input buffer */ + unsigned int* int_buf = (unsigned int*)buf; + if ((unsigned int)buf & 3) panicf("nand_read_raw() non-aligned input buffer"); /* Enable NFC bus clock */ BCLKCTR |= DEV_NAND; @@ -245,7 +262,7 @@ static void nand_read(int chip, int row, int column, int size) /* Read data into page buffer */ for (i = 0; i < (size/4); i++) { - page_buf[i] = NFC_WDATA; + int_buf[i] = NFC_WDATA; } nand_chip_select(-1); @@ -255,89 +272,42 @@ static void nand_read(int chip, int row, int column, int size) } -/* TEMP testing function */ -#include "lcd.h" - -extern int line; -static unsigned char str_buf[MAX_PAGE_SIZE]; - -static void nand_test(void) +/* NB: Output buffer must currently be word-aligned */ +static bool nand_read_sector(int segment, int sector, void* buf) { - int i,j,row; - int pages_per_mb = 1048576/page_size; - - printf("%d banks", total_banks); - printf("* %d pages", total_pages); - printf("* %d bytes per page", page_size); + int physaddr = segment_location[segment]; + int bank = physaddr >> 28; + int page = physaddr & 0xffffff; - while (!button_read_device()) {}; + int page_in_seg = sector / sectors_per_page; + int sec_in_page = sector % sectors_per_page; + + /* TODO: Check if there are any 0x15 pages referring to this segment/sector + combination. If present we need to read that data instead. */ - /* Now for fun, scan the raw pages for 'TAG' and display the contents */ + if (physaddr == -1) return false; - row = 0; - while (row < total_pages) + if (page_in_seg & 1) { - bool found = false; - unsigned char* buf_ptr = (unsigned char*)page_buf; - - line = 0; - - if (row % pages_per_mb == 0) printf("%dMb", row/pages_per_mb); - - /* Read a page from chip 0 */ - nand_read(0, row, 0, page_size); - - for (j = 0; j < page_size; j++) - { - if (buf_ptr[j] == 'T' && buf_ptr[j+1] == 'A' && buf_ptr[j+2] == 'G') - found = true; - } - - if (found) - { - unsigned char* str_ptr = str_buf; - - printf("Row %d:", row); - - /* Copy ascii-readable parts out to a string */ - for (i = 0; i < page_size; i++) - { - str_buf[i] = ' '; - if (buf_ptr[i] > 31 && buf_ptr[i] < 128) - { - *str_ptr++ = buf_ptr[i]; - } - } - - str_ptr = str_buf; + /* Data is located in block+1 */ + page += pages_per_block; + } - /* Nasty piece of code to display the text in a readable manner */ - for (i = 1; i < 30; i++) - { - for (j = 0; j < 48; j++) - { - /* In the absence of a putc() we have this mess... */ - unsigned char buf2[2]; - buf2[0] = *str_ptr++; - buf2[1] = '\0'; - lcd_puts(j,i,buf2); - } - } + if (page_in_seg & 2) + { + /* Data is located in second plane */ + page += (blocks_per_bank/2) * pages_per_block; + } - /* Alternate hex display code - for (i = 0; i<112; i+=4) - { - printf("0x%08x 0x%08x 0x%08x 0x%08x", - page_buf[i],page_buf[i+1],page_buf[i+2],page_buf[i+3]); - } - */ + page += page_in_seg/4; - while (!button_read_device()) {}; + nand_read_raw(bank, page, + sec_in_page * (SECTOR_SIZE+16), + SECTOR_SIZE, buf); - lcd_clear_display(); - } - row++; - } + /* TODO: Read the 16 spare bytes, perform ECC correction */ + + return true; } @@ -345,7 +315,7 @@ static void nand_get_chip_info(void) { bool found = false; unsigned char manuf_id; - unsigned char id_buf[5]; + unsigned char id_buf[8]; /* Read chip id from bank 0 */ nand_read_id(0, id_buf); @@ -363,7 +333,7 @@ static void nand_get_chip_info(void) page_size = 2048; spare_size = 64; pages_per_block = 128; - total_blocks = 8192; + blocks_per_bank = 8192; col_cycles = 2; row_cycles = 3; @@ -375,7 +345,7 @@ static void nand_get_chip_info(void) page_size = 4096; spare_size = 128; pages_per_block = 128; - total_blocks = 8192; + blocks_per_bank = 8192; col_cycles = 2; row_cycles = 3; @@ -391,10 +361,12 @@ static void nand_get_chip_info(void) id_buf[0],id_buf[1],id_buf[2],id_buf[3],id_buf[4]); } - total_pages = total_blocks * pages_per_block; + pages_per_bank = blocks_per_bank * pages_per_block; + bytes_per_segment = page_size * pages_per_block * 4; + sectors_per_page = page_size / SECTOR_SIZE; + sectors_per_segment = bytes_per_segment / SECTOR_SIZE; /* Establish how many banks are present */ - nand_read_id(1, id_buf); if (id_buf[0] == manuf_id) @@ -433,7 +405,99 @@ static void nand_get_chip_info(void) /* Bank 1 returned differing id - assume it is junk */ total_banks = 1; } + + /* Check block 0, page 0 for "BMPM" string & total_banks byte. If this is + confirmed for all D2s we can remove the above code & nand_read_uid(). */ + + nand_read_raw(0, /* bank */ + 0, /* page */ + page_size, /* offset */ + 8, id_buf); + + if (strncmp(id_buf, "BMPM", 4)) panicf("BMPM tag not present"); + if (id_buf[4] != total_banks) panicf("BMPM total_banks mismatch"); +} + + +/* TEMP testing function */ + +#ifdef BOOTLOADER +#include "lcd.h" + +extern int line; +unsigned int buf[(MAX_PAGE_SIZE + MAX_SPARE_SIZE) / 4]; + +static void nand_test(void) +{ + int i; + unsigned int seq_segments = 0; +#if 0 + int chip,page; +#endif + + printf("%d banks", total_banks); + printf("* %d pages", pages_per_bank); + printf("* %d bytes per page", page_size); + + i = 0; + while (segment_location[i] != -1 + && i++ < (blocks_per_bank * total_banks / 4)) + { + seq_segments++; + } + printf("%d sequential segments found (%dMb)", seq_segments, + (seq_segments*bytes_per_segment)>>20); + + while (!button_read_device()) {}; + while (button_read_device()) {}; + +#if 0 + /* Read & display sequential pages */ + for (chip = 0; chip < total_banks; chip++) + { + for (page = 0x0; page < 0x100; page++) + { + nand_read_raw(chip, page, 0, page_size+spare_size, buf); + + for (i = 0; i < (page_size+spare_size)/4; i += 132) + { + int j,interesting = 0; + line = 0; + printf("c:%d p:%lx i:%d", chip, page, i); + + for (j=i; j<(i+131); j++) + { + if (buf[j] != 0xffffffff) interesting = 1; + } + + if (interesting) + { + for (j=i; j<(i+63); j+=4) + { + printf("%lx %lx %lx %lx", + buf[j], buf[j+1], buf[j+2], buf[j+3]); + } + printf("--->"); + while (!button_read_device()) {}; + while (button_read_device()) {}; + + line = 1; + printf("<---"); + for (j=j; j<(i+131); j+=4) + { + printf("%lx %lx %lx %lx", + buf[j], buf[j+1], buf[j+2], buf[j+3]); + } + while (!button_read_device()) {}; + while (button_read_device()) {}; + reset_screen(); + } + } + } + } +#endif } +#endif /* API Functions */ @@ -446,10 +510,40 @@ void ata_led(bool onoff) int ata_read_sectors(IF_MV2(int drive,) unsigned long start, int incount, void* inbuf) { - #warning function not implemented - (void)start; - (void)incount; - (void)inbuf; +#ifdef HAVE_MULTIVOLUME + (void)drive; /* unused for now */ +#endif + mutex_lock(&ata_mtx); + + while (incount > 0) + { + int done = 0; + int segment = start / sectors_per_segment; + int secmod = start % sectors_per_segment; + + while (incount > 0 && secmod < sectors_per_segment) + { + if (!nand_read_sector(segment, secmod, inbuf)) + { + mutex_unlock(&ata_mtx); + return -1; + } + + inbuf += SECTOR_SIZE; + incount--; + secmod++; + done++; + } + + if (done < 0) + { + mutex_unlock(&ata_mtx); + return -1; + } + start += done; + } + + mutex_unlock(&ata_mtx); return 0; } @@ -471,13 +565,13 @@ void ata_spindown(int seconds) bool ata_disk_is_active(void) { - #warning function not implemented + /* null */ return 0; } void ata_sleep(void) { - #warning function not implemented + /* null */ } void ata_spin(void) @@ -488,13 +582,13 @@ void ata_spin(void) /* Hardware reset protocol as specified in chapter 9.1, ATA spec draft v5 */ int ata_hard_reset(void) { - #warning function not implemented + /* null */ return 0; } int ata_soft_reset(void) { - #warning function not implemented + /* null */ return 0; } @@ -506,20 +600,99 @@ void ata_enable(bool on) int ata_init(void) { - if (!initialized) - { - /* Get chip characteristics and number of banks */ - nand_get_chip_info(); + int i, bank, page; + unsigned int spare_buf[4]; + + if (initialized) return 0; - /* TODO: Scan all banks for bad blocks */ + /* Get chip characteristics and number of banks */ + nand_get_chip_info(); - /* TODO: Build physical->logical address translation */ - - initialized = true; + for (i = 0; i < (MAX_BLOCKS_PER_BANK * MAX_BANKS / 4); i++) + { + segment_location[i] = -1; + } + + /* Scan banks to build up block translation table */ + for (bank = 0; bank < total_banks; bank++) + { + for (page = 0; page < pages_per_bank/2; page += pages_per_block*2) + { + unsigned char segment_flag; + unsigned char stored_flag; + unsigned short segment_id; + + unsigned char* buf_ptr = (unsigned char*)spare_buf; + + /* Read spare bytes from first sector of each segment */ + nand_read_raw(bank, page, + SECTOR_SIZE, /* offset */ + 16, spare_buf); + + segment_id = (buf_ptr[6] << 8) | buf_ptr[7]; + segment_flag = buf_ptr[4]; + + stored_flag = (segment_location[segment_id] >> 24) & 0xf; + +#if defined(BOOTLOADER) && 0 + if (segment_flag == 0x15) + { + printf("Segment %lx: c:%lx p:%lx, type:%lx, stored:x%lx", + segment_id, bank, page, segment_flag, stored_flag); + while (!button_read_device()) {}; + while (button_read_device()) {}; + } +#endif + + if (segment_flag == 0x13 || segment_flag == 0x17) + { + if (segment_id < (blocks_per_bank * total_banks / 4)) + { +#if defined(BOOTLOADER) && 0 + if (segment_location[segment_id] != -1 && stored_flag != 0x3) + { + int orig_bank = segment_location[segment_id] >> 28; + int orig_page = segment_location[segment_id] & 0xFFFFFF; + + printf("Segment %d already set! (stored flag:x%lx)", + segment_id, stored_flag); + + printf("0x%08x 0x%08x 0x%08x 0x%08x", + spare_buf[0],spare_buf[1],spare_buf[2],spare_buf[3]); + + nand_read_raw(orig_bank, orig_page, + SECTOR_SIZE, + 16, spare_buf); + + printf("0x%08x 0x%08x 0x%08x 0x%08x", + spare_buf[0],spare_buf[1],spare_buf[2],spare_buf[3]); + } +#endif + /* Write bank, block type & physical address into table */ + segment_location[segment_id] + = page | (bank << 28) | ((segment_flag & 0xf) << 24); + } + else + { + panicf("Invalid segment id:%d found", segment_id); + } + } + } } + + initialized = true; +#ifdef BOOTLOADER /* TEMP - print out some diagnostics */ nand_test(); +#endif return 0; } + + +/* TEMP: This will return junk, it's here for compilation only */ +unsigned short* ata_get_identify(void) +{ + return (unsigned short*)0x21000000; /* Unused DRAM */ +} -- cgit v1.2.3