diff -u -r -N src/mtd/Makefile.am src/mtd/Makefile.am --- src/mtd/Makefile.am Sun Sep 26 15:59:41 2004 +++ src/mtd/Makefile.am Mon Dec 6 12:03:06 2004 @@ -10,4 +10,6 @@ libmtd_a_SOURCES = \ flash.c \ sst39vf160.c \ - mbm29lv650.c + mbm29lv650.c \ + am29lv800bb.c + diff -u -r -N src/mtd/Makefile.in src/mtd/Makefile.in --- src/mtd/Makefile.in Sun Sep 26 15:59:41 2004 +++ src/mtd/Makefile.in Mon Dec 6 12:07:19 2004 @@ -84,7 +84,8 @@ libmtd_a_SOURCES = \ flash.c \ sst39vf160.c \ - mbm29lv650.c + mbm29lv650.c \ + am29lv800bb.c subdir = src/mtd mkinstalldirs = $(SHELL) $(top_srcdir)/tools/mkinstalldirs @@ -95,7 +96,7 @@ libmtd_a_AR = $(AR) cru libmtd_a_LIBADD = am_libmtd_a_OBJECTS = flash.$(OBJEXT) sst39vf160.$(OBJEXT) \ - mbm29lv650.$(OBJEXT) + mbm29lv650.$(OBJEXT) am29lv800bb.$(OBJEXT) libmtd_a_OBJECTS = $(am_libmtd_a_OBJECTS) DEFS = @DEFS@ @@ -106,7 +107,7 @@ depcomp = $(SHELL) $(top_srcdir)/tools/depcomp am__depfiles_maybe = depfiles @AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/flash.Po ./$(DEPDIR)/mbm29lv650.Po \ -@AMDEP_TRUE@ ./$(DEPDIR)/sst39vf160.Po +@AMDEP_TRUE@ ./$(DEPDIR)/sst39vf160.Po ./$(DEPDIR)/am29lv800bb.Po COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) @@ -144,6 +145,7 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flash.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbm29lv650.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sst39vf160.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/am29lv800bb.Po@am__quote@ distclean-depend: -rm -rf ./$(DEPDIR) diff -u -r -N src/mtd/am29lv800bb.c src/mtd/am29lv800bb.c --- src/mtd/am29lv800bb.c Thu Jan 1 12:00:00 1970 +++ src/mtd/am29lv800bb.c Mon Dec 6 12:00:42 2004 @@ -0,0 +1,280 @@ +/* + * mtd/am29lv800bb.c : implements the operations to mbm29lv650 flash chip. + * + * Copyright (C) 2003, Rongkai Zhan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "jtager.h" +#include "target.h" +#include "flash.h" + +#define MANU_ID 0x0001 /* AMD Manufacturer ID code */ +#define DEVICE_ID 0x225B /* am29lv800bb device code */ + +#define SECTOR_SIZE (32 * 1024 * 2) /* 32K words = 64K Bytes */ +#define CHIP_SIZE (1024 * 1024 * 1) /* 512K words = 1M Bytes */ +#define CHIP_BASE (am29lv800bb.base) +#define SECTOR_MASK (SECTOR_SIZE - 1) + +#define CMD_ADDR1 (am29lv800bb.base + (0x5555 << 1)) +#define CMD_ADDR2 (am29lv800bb.base + (0x2aaa << 1)) + +#define DQ5_MASK 0x20 +#define DQ6_MASK 0x40 +#define DQ7_MASK 0x80 + +flash_t am29lv800bb; + +static int chip_probe(u32 base) +{ + u16 id1 = 0, id2 = 0; + u16 softid_cmd[4] = {0xAAAA, 0x5555, 0x9090, 0xF0F0}; + u16 old1, old2; + u32 old_base = CHIP_BASE; + int ret; + + CHIP_BASE = base; /* update chip base address */ + + /* save the contents of these two memory units */ + old1 = read16(CMD_ADDR1); + old2 = read16(CMD_ADDR2); + + /* write ID commands */ + write16(softid_cmd[0], CMD_ADDR1); + write16(softid_cmd[1], CMD_ADDR2); + write16(softid_cmd[2], CMD_ADDR1); + + /* read MANU_ID & DEVICE_ID */ + id1 = read16(base + (0x00 << 1)); + id2 = read16(base + (0x01 << 1)); + + /* check Manufacture ID and Device ID */ + if ((id1 == MANU_ID) && (id2 == DEVICE_ID)) + ret = 0; + else + ret = -1; + + /* Software ID Exit / CFI Exit */ + write16(softid_cmd[0], CMD_ADDR1); + write16(softid_cmd[1], CMD_ADDR2); + write16(softid_cmd[3], CMD_ADDR1); + + if (ret) { + CHIP_BASE = old_base; + write16(old1, CMD_ADDR1); + write16(old2, CMD_ADDR2); + } + return ret; +} + +/* + * DQ6 is still toggling bit? + */ +static int chip_is_busy(u32 addr) +{ + u16 oldval, newval; + u32 count = 0; + int ret = -1, timeout = 0; + + addr = addr16_align(addr); + + while (1) { + oldval = read16(addr); + newval = read16(addr); + if ((oldval & DQ6_MASK) == (newval & DQ6_MASK)) { + /* DQ6 stops toggle */ + ret = 0; + break; + } + + /* DQ5 = 1? */ + if (newval & DQ5_MASK) { + /* exceed timing limits */ + timeout = 1; + break; + } + + count++; + if (count > 0xF0000000) { + timeout = 1; + break; + } + } + + if (timeout) { + /* + * oh, this is the last chance for success. + */ + oldval = read16(addr); + newval = read16(addr); + if ((oldval & DQ6_MASK) == (newval & DQ6_MASK)) + ret = 0; + else + ret = -1; + } + return ret; +} + +/* + * DQ7 Data Polling + */ +static int __attribute__((__unused__)) data_polling(u32 addr, u16 true_data) +{ + u16 value; + int ret; + u32 timeout = 0x10000000; + + ret = -1; + while (timeout) { + value = read16(addr); + if ((value & DQ7_MASK) == (true_data & DQ7_MASK)) { + ret = 0; + break; + } + timeout--; + } + return ret; +} /* end of data_polling(...) */ + +/** + * erase_sector - erase one sector + * @addr: any address in the sector to be erased. + * + * Return value: If success, return 0. Otherwise, return -1. + */ +static int erase_sector(u32 addr) +{ + u16 erase_sector_cmd[6] = {0xAAAA, 0x5555, 0x8080, + 0xAAAA, 0x5555, 0x3030}; + u32 sector_addr = addr & (~((u32)SECTOR_SIZE - 1)); + int ret; + + write16(erase_sector_cmd[0], CMD_ADDR1); + write16(erase_sector_cmd[1], CMD_ADDR2); + write16(erase_sector_cmd[2], CMD_ADDR1); + write16(erase_sector_cmd[3], CMD_ADDR1); + write16(erase_sector_cmd[4], CMD_ADDR2); + write16(erase_sector_cmd[5], sector_addr); + + /* chip is busy ? */ + sector_addr += (SECTOR_SIZE - 2); + ret = chip_is_busy(sector_addr); + return ret; +} /* end of erase_sector(...) */ + +static int erase_chip(void) +{ + u16 erase_chip_cmd[6] = {0xAAAA, 0x5555, 0x8080, + 0xAAAA, 0x5555, 0x1010}; + u32 base, wait = 500000; + int ret; + + write16(erase_chip_cmd[0], CMD_ADDR1); + write16(erase_chip_cmd[1], CMD_ADDR2); + write16(erase_chip_cmd[2], CMD_ADDR1); + write16(erase_chip_cmd[3], CMD_ADDR1); + write16(erase_chip_cmd[4], CMD_ADDR2); + write16(erase_chip_cmd[5], CMD_ADDR1); + + /* + * Chip erase is a long way, we just wait for a while. + */ + while (wait) + wait--; + + base = CHIP_BASE + (CHIP_SIZE - 2); + ret = chip_is_busy(base); + + return ret; +} /* end of erase_chip() */ + +/** + * flash_write - write flash memory with the source data from target memory or host buffer + * @start_address: The flash start address to be written. + * @bytenr: how many bytes are to be written. + * @source_address: The address of source data. + * @from_host: If from_host == 0, we read the source data from a target memory block + * If from_host != 0, we read the source data from a host buffer. + */ +static int flash_write(u32 start_address, u32 bytenr, u32 source_address, int from_host) +{ + u16 pgm_cmd[3] = {0xAAAA, 0x5555, 0xA0A0}; + u32 current_addr; + int i, retry, ret; + u16 data; + + if ((start_address < CHIP_BASE) + || (start_address + bytenr - 1) > (CHIP_BASE + CHIP_SIZE - 1)) + return -1; /* out of range */ + + /* align address with the 16-bit word boundary */ + current_addr = addr16_align(start_address); + + for (i = 0; i < (bytenr/2); i++) { + if (from_host) + data = *(u16 *)source_address; /* read data from host buffer */ + else + data = read16(source_address); + source_address += 2; + if (data == 0xFFFF) { + current_addr += 2; + continue; + } + retry = 0; +retry_pgm: + write16(pgm_cmd[0], CMD_ADDR1); + write16(pgm_cmd[1], CMD_ADDR2); + write16(pgm_cmd[2], CMD_ADDR1); + write16(data, current_addr); + + ret = chip_is_busy(current_addr); + if (ret && retry < 3) { + retry++; + goto retry_pgm; + } else if (ret) { + break; /* failure */ + } + + current_addr += 2; + if ((i + 1)%(2*1024) == 0) { + printf("."); + fflush(stdout); + } + } /* end of for (i = 0; ... ) */ + + return ret; +} /* end of flash_write(...) */ + +flash_t am29lv800bb = { + .name = "am29lv800bb", + .mfr_id = MANU_ID, + .dev_id = DEVICE_ID, + .base = 0x0L, /* start physical address */ + .chip_size = CHIP_SIZE, + .sector_size = SECTOR_SIZE, + .bus_width = 16, + + .probe = chip_probe, + .erase_sector = erase_sector, + .erase_chip = erase_chip, + .write = flash_write, +}; diff -u -r -N src/mtd/flash.c src/mtd/flash.c --- src/mtd/flash.c Sun Oct 17 21:04:28 2004 +++ src/mtd/flash.c Mon Dec 6 12:01:11 2004 @@ -31,11 +31,13 @@ extern flash_t sst39vf160; extern flash_t mbm29lv650; +extern flash_t am29lv800bb; /* all supported flash chips */ flash_t * flashes[] = { &mbm29lv650, &sst39vf160, + &am29lv800bb, NULL /* the last element must be NULL */ };