+/*
+famicom ROM cartridge utility - unagi
+flash memory driver
+
+Copyright (C) 2008 ±·³«È¯¶¨Æ±Áȹç
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+flashmemory.c ¤À¤±¤Î·Ù¹ð
+¤³¤Î¥½¡¼¥¹¥³¡¼¥É¤ò»²¹Í¡¢Å¾ÍѤ·¤Æ¥·¥§¥¢¥¦¥§¥¢¤Ê¤É¤ÇÍø±×¤òÆÀ¤Ê¤¤¤³¤È¡£
+ȽÌÀ¤·¤¿¾ì¹ç¤Ï LGPL ¤¬Å¬ÍѤµ¤ì¡¢³ºÅö²Õ½ê¤Î¥½¡¼¥¹¤ò¸ø³«¤¹¤ëɬÍפ¬¤¢¤ë¡£
+*/
+#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <windows.h>
+#include "type.h"
+#include "header.h"
+#include "flashmemory.h"
+/*
+driver for Winbond W29C020, W49F002
+*/
/*
-Winbond W29C020, W49F002 test
+JEDEC flash memory command
+http://www.sst.com/downloads/software_driver/SST49LF002A.txt
*/
struct flash_task{
long address, data;
};
enum{
- flash_task_end = 0x46494649
-};
-
-static const int FLASH_CPU_WRITE = (
- (1 << BITNUM_PPU_OUTPUT) |
- (1 << BITNUM_PPU_RW) |
- (1 << BITNUM_PPU_SELECT) |
- (1 << BITNUM_WRITEDATA_OUTPUT) |
- (0 << BITNUM_WRITEDATA_LATCH) |
- (0 << BITNUM_CPU_M2) |
- (1 << BITNUM_CPU_RW)
-);
-
-static void flash_cpu_write(long address, long data)
-{
- int c = FLASH_CPU_WRITE;
- //Á´¤Æ¤Î¥Ð¥¹¤ò»ß¤á¤ë
- data_port_latch(DATA_SELECT_CONTROL, c);
- address_set(address | ADDRESS_MASK_A15, ADDRESS_SET);
- data_port_set(c, data);
-/*
-W29C020
-During the byte-load cycle, the addresses are latched by the falling
-edge of either CE or WE,whichever occurs last. The data are latched
-by the rising edge of either CE or WE, whicheveroccurs first.
-*/
+ ADDRESS_0000 = 0,
+ ADDRESS_2AAA = 0x2aaa,
+ ADDRESS_5555 = 0x5555,
+ FLASH_COMMAND_END
+};
+static const struct flash_task PRODUCTID_ENTRY[] = {
+ {ADDRESS_5555, 0xaa},
+ {ADDRESS_2AAA, 0x55},
+ {ADDRESS_5555, 0x90},
+ {FLASH_COMMAND_END, 0}
+};
+static const struct flash_task PRODUCTID_EXIT[] = {
+ {ADDRESS_5555, 0xaa},
+ {ADDRESS_2AAA, 0x55},
+ {ADDRESS_5555, 0xf0},
+ {FLASH_COMMAND_END, 0}
+};
+static const struct flash_task PROTECT_DISABLE[] = {
+ {ADDRESS_5555, 0xaa},
+ {ADDRESS_2AAA, 0x55},
+ {ADDRESS_5555, 0xa0},
+ {FLASH_COMMAND_END, 0}
+};
+static const struct flash_task PROTECT_ENABLE[] = {
+ {ADDRESS_5555, 0xaa},
+ {ADDRESS_2AAA, 0x55},
+ {ADDRESS_5555, 0x80},
+ {ADDRESS_5555, 0xaa},
+ {ADDRESS_2AAA, 0x55},
+ {ADDRESS_5555, 0x20},
+ {FLASH_COMMAND_END, 0}
+};
+static const struct flash_task ERASE_CHIP[] = {
+ {ADDRESS_5555, 0xaa},
+ {ADDRESS_2AAA, 0x55},
+ {ADDRESS_5555, 0x80},
+ {ADDRESS_5555, 0xaa},
+ {ADDRESS_2AAA, 0x55},
+ {ADDRESS_5555, 0x10},
+ {FLASH_COMMAND_END, 0}
+};
+
+static const struct flash_task ERASE_SECTOR[] = {
+ {ADDRESS_5555, 0xaa},
+ {ADDRESS_2AAA, 0x55},
+ {ADDRESS_5555, 0x80},
+ {ADDRESS_5555, 0xaa},
+ {ADDRESS_2AAA, 0x55},
+ //¤³¤Î¤¢¤È sectoraddress ¤Ë 0x30 ¤ò write
+ {FLASH_COMMAND_END, 0}
+};
+
+static const struct flash_task PP[] = {
+ {ADDRESS_5555, 0xaa},
+ {ADDRESS_2AAA, 0x55},
+ {ADDRESS_5555, 0x80},
+ {ADDRESS_5555, 0xaa},
+ {ADDRESS_2AAA, 0x55},
+ {ADDRESS_5555, 0x60},
+ {FLASH_COMMAND_END, 0}
+};
+
+static void command_set(const struct flash_order *d, const struct flash_task *t)
+{
+ while(t->address != FLASH_COMMAND_END){
+ long logical_address = 0;
+ switch(t->address){
+ case ADDRESS_0000: //bank ¤Ë¤è¤Ã¤Æ¤ÏÀßÄê¤Ç¤¤Ê¤¤¤«¤â?
+ logical_address = d->command_0000;
+ break;
+ case ADDRESS_2AAA:
+ logical_address = d->command_2aaa;
+ break;
+ case ADDRESS_5555:
+ logical_address = d->command_5555;
+ break;
+ default:
+ assert(0); //unknown task address
+ }
+ d->flash_write(logical_address, t->data);
+ t++;
+ }
+}
+
/*
-W49F002
-#CS or #WE ¤¬¹ß¤ê¤¿¤È¤¤Ë address latch
-#CS or #WE ¤¬¾å¤¬¤Ã¤¿¤È¤¤Ë data latch
+---- product ID check ----
*/
- c = bit_clear(c, BITNUM_WRITEDATA_OUTPUT);
- //WE down
- c = bit_clear(c, BITNUM_CPU_RW);
-// c = bit_clear(c, BITNUM_CPU_M2);
- data_port_latch(DATA_SELECT_CONTROL, c);
- //CS down
- address_set(address & ADDRESS_MASK_A0toA14, ADDRESS_SET);
- //CS up
- address_set(address | ADDRESS_MASK_A15, ADDRESS_SET);
- //WE up
- data_port_latch(DATA_SELECT_CONTROL, FLASH_CPU_WRITE);
+#define dprintf if(DEBUG==1) printf
+static int productid_check(const struct flash_order *d, const struct flash_driver *f)
+{
+ u8 data[3];
+ command_set(d, PRODUCTID_ENTRY);
+ d->read(d->command_0000, 3, data);
+ command_set(d, PRODUCTID_EXIT);
+ if(f->id_manufacurer != data[0]){
+ return NG;
+ }
+ if(f->id_device != data[1]){
+ return NG;
+ }
+ return OK;
}
+static int productid_sram(const struct flash_order *d, const struct flash_driver *f)
+{
+ return OK;
+}
/*
-memory detail
-address 0x000000-0x03ffff
-VRC6 memory bank
-bank0 page0, liner 0x00000-0x03fff, cpu address $8000-$bfff
-bank1 page2, liner 0x04000-0x05fff, cpu address $c000-$dfff
-fix liner 0x3e000-0x3ffff, cpu address $e000-$ffff
+---- toggle check ----
+databit6
*/
-static void cpu_page_address_get(long liner, long *page, long *offset)
+const int CHECK_RETRY_MAX = 0x10000;
+static int toggle_check_d6(const struct flash_order *d, long address)
{
- *page = liner / 0x4000;
- *offset = liner; // & 0x3fff;
+ u8 predata;
+ int retry = 0;
+ d->read(address, 1, &predata); //read DQ6
+ predata &= 0x40;
+ while(retry < CHECK_RETRY_MAX){
+ u8 data;
+ d->read(address, 1, &data); //read DQ6 again
+ data &= 0x40;
+ if(predata == data){
+ return OK;
+ }
+ predata = data;
+ retry++;
+ }
+ return NG;
}
-static void cpu_liner_write_task(long liner, long data)
+static int toggle_check_d2d5d6(const struct flash_order *d, long address)
{
- long page, offset;
- cpu_page_address_get(liner, &page, &offset);
- flash_cpu_write(offset, data); //bank0 write
+ u8 predata;
+ int retry = 0;
+ d->read(address, 1, &predata);
+ predata &= 0x40;
+ do{
+ u8 data;
+ d->read(address, 1, &data);
+ //DQ6 toggle check
+ if(predata == (data & 0x40)){
+ return OK;
+ }
+ //DQ5 == 0 ¤Ê¤é¤ä¤ê¤Ê¤ª¤·
+ if(data & 0x20){
+ //recheck toggle bit, read twice
+ u8 t[2];
+ d->read(address, 1, &t[0]);
+ d->read(address, 1, &t[1]);
+ if((t[0] & 0x40) == (t[1] & 0x40)){
+ return OK;
+ }
+ //Program/Erase operation not complete, write reset command.
+ return NG;
+ }
+ if((retry & 0x0f) == 0){
+ dprintf("toggle out %06x \n", (int) address);
+ }
+ retry++;
+ }while(retry < CHECK_RETRY_MAX);
+ return NG;
}
/*
-PROTECT_ENABLE ¤Ï W29C020 ÀìÍÑ
+---- polling check ----
+databit7
*/
-const struct flash_task PROTECT_DISABLE[] = {
- {0x5555, 0xaa},
- {0x2aaa, 0x55},
- {0x5555, 0xa0},
- {flash_task_end, 0}
-};
-const struct flash_task PROTECT_ENABLE[] = {
- {0x5555, 0xaa},
- {0x2aaa, 0x55},
- {0x5555, 0x80},
- {0x5555, 0xaa},
- {0x2aaa, 0x55},
- {0x5555, 0x20},
- {flash_task_end, 0}
-};
-const struct flash_task ERASE[] = {
- {0x5555, 0xaa},
- {0x2aaa, 0x55},
- {0x5555, 0x80},
- {0x5555, 0xaa},
- {0x2aaa, 0x55},
- {0x5555, 0x10},
- {flash_task_end, 0}
-};
-
-static void task_set(const struct flash_task *t)
-{
- while(t->address != flash_task_end){
- cpu_liner_write_task(t->address, t->data);
- t++;
+static int polling_check_d7(const struct flash_order *d, long address, u8 truedata)
+{
+ int retry = 0;
+
+ truedata &= 0x80;
+ while(retry < CHECK_RETRY_MAX){
+ u8 data;
+ d->read(address, 1, &data);
+ data &= 0x80;
+ if(truedata == data){
+ return OK;
+ }
+ retry++;
}
+ return NG;
}
-enum{WAIT_COUNT = 0x100};
-static void cpu_liner_write_data(long liner, const u8 *data, long length)
+static int polling_check_d5d7(const struct flash_order *d, long address, u8 truedata)
{
- long page, offset;
+ int retry = 0;
- cpu_page_address_get(liner, &page, &offset);
+ truedata &= 0x80;
+ do{
+ u8 data;
+ d->read(address, 1, &data);
+ if(truedata == (data & 0x80)){
+ return OK;
+ }
+ if(data & 0x20){
+ d->read(address, 1, &data);
+ if(truedata == (data & 0x80)){
+ return OK;
+ }
+ dprintf("%s error", __FUNCTION__);
+ return NG;
+ }
+ retry++;
+ }while(retry < CHECK_RETRY_MAX);
+ return NG;
+}
+/*
+---- erase ----
+*/
+static void flash_erase_chip_2aaa(const struct flash_order *d)
+{
+ command_set(d, ERASE_CHIP);
+ toggle_check_d6(d, d->command_2aaa);
+ Sleep(200); //Tec 0.2 sec
+}
+
+static void flash_erase_chip_02aa(const struct flash_order *d)
+{
+ u8 data;
+ d->read(d->command_2aaa, 1, &data);
+ command_set(d, ERASE_CHIP);
+ if(0){
+ toggle_check_d2d5d6(d, d->command_2aaa);
+
+ }else{
+ polling_check_d5d7(d, d->command_2aaa, data);
+ }
+ Sleep(8000); //chip erase time 8sec, max64sec
+}
+
+#if DEBUG==1
+static void sram_erase(const struct flash_order *d)
+{
+ //bank ÀÚ¤êÂؤ¨¤¬È¼¤¦¤Î¤Ç¼ÂÁõ¤Ç¤¤Ê¤¤
+}
+#endif
+
+/*
+---- program ----
+*/
+static int program_byte(const struct flash_order *d, long address, const u8 *data, long length)
+{
+ int retry = 0;
while(length != 0){
if(*data != 0xff){
- task_set(PROTECT_DISABLE);
- flash_cpu_write(offset, *data);
+ fflush(stdout);
+ command_set(d, PROTECT_DISABLE);
+ d->flash_write(address, *data);
+ if(toggle_check_d6(d, address) == NG){
+ dprintf("%s NG\n", __FUNCTION__);
+ return NG;
+ }
}
- u8 predata = *data & 0x40;
- while(1){
- u8 dummy;
- hk_cpu_read(0x8000 + offset, 1, &dummy);
- dummy &= 0x40;
- if(dummy != predata){
- predata = dummy;
- continue;
+ u8 dummy;
+ d->read(address, 1, &dummy);
+ if(*data == dummy){
+ address++;
+ data++;
+ length--;
+ retry = 0;
+ }else if(retry > 8){
+ dprintf("%s %06x error\n", __FUNCTION__, (int) address);
+ address++;
+ data++;
+ length--;
+ retry = 0;
+ if(DEBUG == 0){
+ return NG;
}
- printf("offset %06x ok\n", (int) offset);
- fflush(stdout);
- break;
+ }else{
+ retry++;
}
- offset++;
+ }
+ return OK;
+}
+
+static int program_pagewrite(const struct flash_order *d, long address, const u8 *data, long length)
+{
+ const long toggle_address = address ;
+ command_set(d, PROTECT_DISABLE);
+ while(length != 0){
+ d->flash_write(address, *data);
+ address++;
data++;
length--;
}
+ int ret = toggle_check_d6(d, toggle_address);
+ if(0){
+ data--;
+ polling_check_d7(d, address - 1, *data);
+ }
+
+ return ret;
+}
+
+/*
+¸ÇͥǥХ¤¥¹¥É¥é¥¤¥Ð
+*/
+static void w49f002_init(const struct flash_order *d)
+{
+/*
+byte program mode ¤Ç¤Ï 1->0 ¤Ë¤¹¤ë¤À¤±¡£ 0->1 ¤Ï erase ¤Î¤ß¡£
+¤è¤Ã¤Æ½é´ü²½»þ¤Ë¤Ï erase ¤ò¼Â¹Ô¤¹¤ë
+*/
+ flash_erase_chip_2aaa(d);
+}
+
+static void w49f002_write(const struct flash_order *d, long address, long length, const struct memory *m)
+{
+ program_byte(d, address, m->data, length);
+// dprintf("write %s 0x%06x done\n", m->name, (int) m->offset);
}
-static void compare(const u8 *data, long length, long offset)
+static void am29f040_init(const struct flash_order *d)
{
- u8 *romdata, *r;
- romdata = malloc(length);
- hk_cpu_read(0x8000, length, romdata);
- r = romdata;
+ flash_erase_chip_02aa(d);
+}
+
+static void am29f040_write(const struct flash_order *d, long address, long length, const struct memory *m)
+{
+ const u8 *data;
+ data = m->data;
while(length != 0){
- if(*r != *data){
- printf("%06x\n", (int)offset);
+ if(*data != 0xff){
+ command_set(d, PROTECT_DISABLE);
+ d->flash_write(address, *data);
+ if(toggle_check_d2d5d6(d, address) == NG){
+ dprintf("%s NG\n", __FUNCTION__);
+ return;
+ }
}
- r++;
+ address++;
data++;
- offset++;
length--;
}
- free(romdata);
}
-void flash_write(const u8 *data, long length, long banksize)
+static void init_nop(const struct flash_order *d)
{
- //pause(10);
- /*
- W29C020
- sequentially load up to 128 byte of page data
- */
- hk_cpu_write(0x8000, 0); //bank0 set
- hk_cpu_write(0xc000, 2); //bank1 set
- task_set(ERASE);
- Sleep(200); //Tec 0.2 sec
- long address = 0;
+/*
+page write mode ¤Ç¤Ï¤È¤¯¤Ë¤Ê¤·
+*/
+}
+
+static void w29c040_write(const struct flash_order *d, long address, long length, const struct memory *m)
+{
+ u8 *cmp;
+ int ngblock = 0;
+ int retry = 0;
+ assert(d->pagesize != 0);
+ cmp = malloc(d->pagesize);
+ do{
+ long a = address;
+ long i = length;
+ long offset = m->offset;
+ const u8 *dd;
+
+ dd = m->data;
+ ngblock = 0;
+ while(i != 0){
+ d->read(a, d->pagesize, cmp);
+ if(memcmp(cmp, dd, d->pagesize) != 0){
+ ngblock++;
+ dprintf("write %s 0x%06x\n", m->name, (int) offset);
+ int result = program_pagewrite(d, a, dd, d->pagesize);
+ if(result == NG){
+ printf("%s: write error\n", __FUNCTION__);
+ free(cmp);
+ return;
+ }
+ }
+ a += d->pagesize;
+ dd += d->pagesize;
+ offset += d->pagesize;
+ i -= d->pagesize;
+ }
+ dprintf("%s 0x%06x, ngblock %d\n", m->name, (int) m->offset, ngblock);
+ if(retry >= 3 && ngblock >= 16){
+ dprintf("skip\n");
+ break;
+ }
+ else if(retry > 12){
+ dprintf("skip\n");
+ break;
+ }
+ retry++;
+ fflush(stdout);
+ }while(ngblock != 0);
+
+ free(cmp);
+}
+
+static void sram_write(const struct flash_order *d, long address, long length, const struct memory *m)
+{
+ const u8 *data;
+ data = m->data;
while(length != 0){
- cpu_liner_write_data(address, data, banksize);
- compare(data, banksize, address);
- address += banksize;
- data += banksize;
- length -= banksize;
+ d->flash_write(address, *data);
+ address++;
+ data++;
+ length--;
}
- /*
- pause 10ms
- */
- /*task_set(PROTECT_ENABLE);
- pause(10);*/
}
+static void dummy_write(const struct flash_order *d, long address, long length, const struct memory *m)
+{
+}
+
+/*
+¥Ç¥Ð¥¤¥¹¥ê¥¹¥È
+*/
+//0x80 °Ê¹ß¤ÏËÜÅö¤Î¥Ç¥Ð¥¤¥¹½ÅÊ£¤·¤Ê¤¤¤È»×¤¦. 狼 JEDEC ¤Î¤È¤³¤ò¤·¤é¤Ù¤Æ.
+static const struct flash_driver DRIVER_SRAM256K = {
+ .name = "SRAM256K",
+ .capacity = 0x8000,
+ .pagesize = 0,
+ .id_manufacurer = FLASH_ID_DEVICE_SRAM,
+ .id_device = FLASH_ID_DEVICE_SRAM,
+ .productid_check = productid_sram,
+#if DEBUG==1
+ .erase = sram_erase,
+#endif
+ .init = init_nop,
+ .write = sram_write
+};
+
+static const struct flash_driver DRIVER_DUMMY = {
+ .name = "dummy",
+ .capacity = 0x40000,
+ .pagesize = 0,
+ .id_manufacurer = FLASH_ID_DEVICE_DUMMY,
+ .id_device = FLASH_ID_DEVICE_DUMMY,
+ .productid_check = productid_sram,
+#if DEBUG==1
+ .erase = sram_erase,
+#endif
+ .init = init_nop,
+ .write = dummy_write
+};
+
+static const struct flash_driver DRIVER_W29C020 = {
+ .name = "W29C020",
+ .capacity = 0x40000,
+ .pagesize = 0x80,
+ .id_manufacurer = 0xda,
+ .id_device = 0x45,
+ .productid_check = productid_check,
+#if DEBUG==1
+ .erase = flash_erase_chip_2aaa,
+#endif
+ .init = init_nop,
+ .write = w29c040_write
+};
+
+static const struct flash_driver DRIVER_W29C040 = {
+ .name = "W29C040",
+ .capacity = 0x80000,
+ .pagesize = 0x100,
+ .id_manufacurer = 0xda,
+ .id_device = 0x46,
+ .productid_check = productid_check,
+#if DEBUG==1
+ .erase = flash_erase_chip_2aaa,
+#endif
+ .init = init_nop,
+ .write = w29c040_write
+};
+
+static const struct flash_driver DRIVER_W49F002 = {
+ .name = "W49F002",
+ .capacity = 0x40000,
+ .pagesize = 0,
+ .id_manufacurer = 0xda,
+ .id_device = 0xae,
+ .productid_check = productid_check,
+#if DEBUG==1
+ .erase = flash_erase_chip_2aaa,
+#endif
+ .init = w49f002_init,
+ .write = w49f002_write
+};
+
+/*
+MANUFATUTER ID 0x7f1c
+EN29F002T DEVICE ID 0x7f92
+EN29F002B DEVICE ID 0x7f97
+
+command address ¤¬ 0x00555, 0x00aaa ¤Ë¤Ê¤Ã¤Æ¤ë
+*/
+static const struct flash_driver DRIVER_EN29F002T = {
+ .name = "EN29F002T",
+ .capacity = 0x40000,
+ .pagesize = 0,
+ .id_manufacurer = 0x1c,
+ .id_device = 0x92,
+ .productid_check = productid_check,
+#if DEBUG==1
+ .erase = flash_erase_chip_02aa,
+#endif
+ .init = am29f040_init,
+ .write = am29f040_write
+};
+static const struct flash_driver DRIVER_AM29F040B = {
+ .name = "AM29F040B",
+ .capacity = 0x80000,
+ .pagesize = 0,
+ .id_manufacurer = 0x01,
+ .id_device = 0xa4,
+ .productid_check = productid_check,
+#if DEBUG==1
+ .erase = flash_erase_chip_02aa,
+#endif
+ .init = am29f040_init,
+ .write = am29f040_write
+};
+
+static const struct flash_driver *DRIVER_LIST[] = {
+ &DRIVER_W29C020, &DRIVER_W29C040,
+ &DRIVER_W49F002, &DRIVER_EN29F002T, &DRIVER_AM29F040B,
+ &DRIVER_SRAM256K,
+ &DRIVER_DUMMY,
+ NULL
+};
+
+const struct flash_driver *flash_driver_get(const char *name)
+{
+ const struct flash_driver **d;
+ d = DRIVER_LIST;
+ while(*d != NULL){
+ if(strcmp(name, (*d)->name) == 0){
+ return *d;
+ }
+ d++;
+ }
+ return NULL;
+}