diff --git a/flashrom-r710-r893.diff b/flashrom-r710-r893.diff new file mode 100644 index 0000000..0e86b59 --- /dev/null +++ b/flashrom-r710-r893.diff @@ -0,0 +1,18166 @@ +diff --git a/82802ab.c b/82802ab.c +index 28fa177..dfe81e8 100644 +--- a/82802ab.c ++++ b/82802ab.c +@@ -33,13 +33,13 @@ + // I need that Berkeley bit-map printer + void print_82802ab_status(uint8_t status) + { +- printf("%s", status & 0x80 ? "Ready:" : "Busy:"); +- printf("%s", status & 0x40 ? "BE SUSPEND:" : "BE RUN/FINISH:"); +- printf("%s", status & 0x20 ? "BE ERROR:" : "BE OK:"); +- printf("%s", status & 0x10 ? "PROG ERR:" : "PROG OK:"); +- printf("%s", status & 0x8 ? "VP ERR:" : "VPP OK:"); +- printf("%s", status & 0x4 ? "PROG SUSPEND:" : "PROG RUN/FINISH:"); +- printf("%s", status & 0x2 ? "WP|TBL#|WP#,ABORT:" : "UNLOCK:"); ++ printf_debug("%s", status & 0x80 ? "Ready:" : "Busy:"); ++ printf_debug("%s", status & 0x40 ? "BE SUSPEND:" : "BE RUN/FINISH:"); ++ printf_debug("%s", status & 0x20 ? "BE ERROR:" : "BE OK:"); ++ printf_debug("%s", status & 0x10 ? "PROG ERR:" : "PROG OK:"); ++ printf_debug("%s", status & 0x8 ? "VP ERR:" : "VPP OK:"); ++ printf_debug("%s", status & 0x4 ? "PROG SUSPEND:" : "PROG RUN/FINISH:"); ++ printf_debug("%s", status & 0x2 ? "WP|TBL#|WP#,ABORT:" : "UNLOCK:"); + } + + int probe_82802ab(struct flashchip *flash) +@@ -47,14 +47,11 @@ int probe_82802ab(struct flashchip *flash) + chipaddr bios = flash->virtual_memory; + uint8_t id1, id2; + +-#if 0 +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0x90, bios + 0x5555); +-#endif +- +- chip_writeb(0xff, bios); ++ /* Reset to get a clean state */ ++ chip_writeb(0xFF, bios); + programmer_delay(10); ++ ++ /* Enter ID mode */ + chip_writeb(0x90, bios); + programmer_delay(10); + +@@ -62,13 +59,11 @@ int probe_82802ab(struct flashchip *flash) + id2 = chip_readb(bios + 0x01); + + /* Leave ID mode */ +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0xF0, bios + 0x5555); ++ chip_writeb(0xFF, bios); + + programmer_delay(10); + +- printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __FUNCTION__, id1, id2); ++ printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2); + + if (id1 != flash->manufacture_id || id2 != flash->model_id) + return 0; +@@ -81,7 +76,6 @@ int probe_82802ab(struct flashchip *flash) + uint8_t wait_82802ab(chipaddr bios) + { + uint8_t status; +- uint8_t id1, id2; + + chip_writeb(0x70, bios); + if ((chip_readb(bios) & 0x80) == 0) { // it's busy +@@ -90,49 +84,38 @@ uint8_t wait_82802ab(chipaddr bios) + + status = chip_readb(bios); + +- // put another command to get out of status register mode +- +- chip_writeb(0x90, bios); +- programmer_delay(10); +- +- id1 = chip_readb(bios); +- id2 = chip_readb(bios + 0x01); +- +- // this is needed to jam it out of "read id" mode +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0xF0, bios + 0x5555); ++ /* Reset to get a clean state */ ++ chip_writeb(0xFF, bios); + + return status; + } + +-int erase_82802ab_block(struct flashchip *flash, int offset) ++int erase_82802ab_block(struct flashchip *flash, unsigned int page, unsigned int pagesize) + { +- chipaddr bios = flash->virtual_memory + offset; +- chipaddr wrprotect = flash->virtual_registers + offset + 2; ++ chipaddr bios = flash->virtual_memory; ++ chipaddr wrprotect = flash->virtual_registers + page + 2; + uint8_t status; + + // clear status register +- chip_writeb(0x50, bios); +- //printf("Erase at %p\n", bios); ++ chip_writeb(0x50, bios + page); ++ + // clear write protect +- //printf("write protect is at %p\n", (wrprotect)); +- //printf("write protect is 0x%x\n", *(wrprotect)); + chip_writeb(0, wrprotect); +- //printf("write protect is 0x%x\n", *(wrprotect)); + + // now start it +- chip_writeb(0x20, bios); +- chip_writeb(0xd0, bios); ++ chip_writeb(0x20, bios + page); ++ chip_writeb(0xd0, bios + page); + programmer_delay(10); ++ + // now let's see what the register is +- status = wait_82802ab(flash->virtual_memory); +- //print_82802ab_status(status); +- if (check_erased_range(flash, offset, flash->page_size)) { ++ status = wait_82802ab(bios); ++ print_82802ab_status(status); ++ ++ if (check_erased_range(flash, page, pagesize)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } +- printf("DONE BLOCK 0x%x\n", offset); ++ printf("DONE BLOCK 0x%x\n", page); + + return 0; + } +@@ -145,7 +128,7 @@ int erase_82802ab(struct flashchip *flash) + printf("total_size is %d; flash->page_size is %d\n", + total_size, flash->page_size); + for (i = 0; i < total_size; i += flash->page_size) +- if (erase_82802ab_block(flash, i)) { ++ if (erase_82802ab_block(flash, i, flash->page_size)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } +@@ -199,7 +182,7 @@ int write_82802ab(struct flashchip *flash, uint8_t *buf) + } + + /* erase block by block and write block by block; this is the most secure way */ +- if (erase_82802ab_block(flash, i * page_size)) { ++ if (erase_82802ab_block(flash, i * page_size, page_size)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } +@@ -207,7 +190,6 @@ int write_82802ab(struct flashchip *flash, uint8_t *buf) + bios + i * page_size, page_size); + } + printf("\n"); +- protect_jedec(bios); + free(tmpbuf); + + return 0; +diff --git a/Makefile b/Makefile +index 881c8f2..62c6a74 100644 +--- a/Makefile ++++ b/Makefile +@@ -25,7 +25,7 @@ INSTALL = install + DIFF = diff + PREFIX ?= /usr/local + MANDIR ?= $(PREFIX)/share/man +-CFLAGS ?= -Os -Wall -Werror ++CFLAGS ?= -Os -Wall -Werror -Wshadow + EXPORTDIR ?= . + + OS_ARCH = $(shell uname) +@@ -33,24 +33,23 @@ ifneq ($(OS_ARCH), SunOS) + STRIP_ARGS = -s + endif + ifeq ($(OS_ARCH), Darwin) +-CFLAGS += -I/usr/local/include +-LDFLAGS += -framework IOKit -framework DirectIO -L/usr/local/lib ++CPPFLAGS += -I/opt/local/include -I/usr/local/include ++LDFLAGS += -framework IOKit -framework DirectIO -L/opt/local/lib -L/usr/local/lib + endif + ifeq ($(OS_ARCH), FreeBSD) +-CFLAGS += -I/usr/local/include ++CPPFLAGS += -I/usr/local/include + LDFLAGS += -L/usr/local/lib + endif + +-LIBS += -lpci -lz ++CHIP_OBJS = jedec.o stm50flw0x0x.o w39v080fa.o sharplhf00l04.o w29ee011.o \ ++ sst28sf040.o m29f400bt.o 82802ab.o pm49fl00x.o \ ++ sst49lfxxxc.o sst_fwhub.o flashchips.o spi.o + +-OBJS = chipset_enable.o board_enable.o udelay.o jedec.o stm50flw0x0x.o \ +- sst28sf040.o am29f040b.o mx29f002.o m29f400bt.o pm29f002.o \ +- w49f002u.o 82802ab.o pm49fl00x.o sst49lf040.o en29f002a.o \ +- sst49lfxxxc.o sst_fwhub.o layout.o cbtable.o flashchips.o physmap.o \ +- flashrom.o w39v080fa.o sharplhf00l04.o w29ee011.o spi.o it87spi.o \ +- ichspi.o w39v040c.o sb600spi.o wbsio_spi.o m29f002.o internal.o \ +- dummyflasher.o pcidev.o nic3com.o satasii.o ft2232_spi.o \ +- print.o ++LIB_OBJS = layout.o ++ ++CLI_OBJS = flashrom.o cli_classic.o cli_output.o print.o ++ ++PROGRAMMER_OBJS = udelay.o programmer.o + + all: pciutils features dep $(PROGRAM) + +@@ -58,7 +57,7 @@ all: pciutils features dep $(PROGRAM) + # of the checked out flashrom files. + # Note to packagers: Any tree exported with "make export" or "make tarball" + # will not require subversion. The downloadable snapshots are already exported. +-SVNVERSION := 710 ++SVNVERSION := 893 + + RELEASE := 0.9.1 + VERSION := $(RELEASE)-r$(SVNVERSION) +@@ -66,23 +65,145 @@ RELEASENAME ?= $(VERSION) + + SVNDEF := -D'FLASHROM_VERSION="$(VERSION)"' + ++# Always enable internal/onboard support for now. ++CONFIG_INTERNAL ?= yes ++ + # Always enable serprog for now. Needs to be disabled on Windows. +-CONFIG_SERPROG = yes ++CONFIG_SERPROG ?= yes ++ ++# Bitbanging SPI infrastructure is not used yet. ++CONFIG_BITBANG_SPI ?= no ++ ++# Always enable 3Com NICs for now. ++CONFIG_NIC3COM ?= yes ++ ++# Disable NVIDIA graphics cards for now, write/erase don't work properly. ++CONFIG_GFXNVIDIA ?= no ++ ++# Always enable SiI SATA controllers for now. ++CONFIG_SATASII ?= yes ++ ++# Always enable FT2232 SPI dongles for now. ++CONFIG_FT2232SPI ?= yes ++ ++# Always enable dummy tracing for now. ++CONFIG_DUMMY ?= yes ++ ++# Always enable Dr. Kaiser for now. ++CONFIG_DRKAISER ?= yes ++ ++# Always enable Bus Pirate SPI for now. ++CONFIG_BUSPIRATESPI ?= yes ++ ++# Disable Dediprog SF100 until support is complete and tested. ++CONFIG_DEDIPROG ?= no ++ ++# Disable wiki printing by default. It is only useful if you have wiki access. ++CONFIG_PRINT_WIKI ?= no ++ ++ifeq ($(CONFIG_INTERNAL), yes) ++FEATURE_CFLAGS += -D'INTERNAL_SUPPORT=1' ++PROGRAMMER_OBJS += chipset_enable.o board_enable.o cbtable.o dmi.o it87spi.o ichspi.o sb600spi.o wbsio_spi.o ++NEED_PCI := yes ++endif + + ifeq ($(CONFIG_SERPROG), yes) + FEATURE_CFLAGS += -D'SERPROG_SUPPORT=1' +-OBJS += serprog.o ++PROGRAMMER_OBJS += serprog.o + ifeq ($(OS_ARCH), SunOS) + LIBS += -lsocket + endif + endif + ++ifeq ($(CONFIG_BITBANG_SPI), yes) ++FEATURE_CFLAGS += -D'BITBANG_SPI_SUPPORT=1' ++PROGRAMMER_OBJS += bitbang_spi.o ++endif ++ ++ifeq ($(CONFIG_NIC3COM), yes) ++FEATURE_CFLAGS += -D'NIC3COM_SUPPORT=1' ++PROGRAMMER_OBJS += nic3com.o ++NEED_PCI := yes ++endif ++ ++ifeq ($(CONFIG_GFXNVIDIA), yes) ++FEATURE_CFLAGS += -D'GFXNVIDIA_SUPPORT=1' ++PROGRAMMER_OBJS += gfxnvidia.o ++NEED_PCI := yes ++endif ++ ++ifeq ($(CONFIG_SATASII), yes) ++FEATURE_CFLAGS += -D'SATASII_SUPPORT=1' ++PROGRAMMER_OBJS += satasii.o ++NEED_PCI := yes ++endif ++ ++ifeq ($(CONFIG_FT2232SPI), yes) ++FTDILIBS := $(shell pkg-config --libs libftdi 2>/dev/null || printf "%s" "-lftdi -lusb") ++# This is a totally ugly hack. + FEATURE_CFLAGS += $(shell LC_ALL=C grep -q "FTDISUPPORT := yes" .features && printf "%s" "-D'FT2232_SPI_SUPPORT=1'") ++FEATURE_LIBS += $(shell LC_ALL=C grep -q "FTDISUPPORT := yes" .features && printf "%s" "$(FTDILIBS)") ++PROGRAMMER_OBJS += ft2232_spi.o ++endif ++ ++ifeq ($(CONFIG_DUMMY), yes) ++FEATURE_CFLAGS += -D'DUMMY_SUPPORT=1' ++PROGRAMMER_OBJS += dummyflasher.o ++endif + +-FEATURE_LIBS += $(shell LC_ALL=C grep -q "FTDISUPPORT := yes" .features && printf "%s" "-lftdi") ++ifeq ($(CONFIG_DRKAISER), yes) ++FEATURE_CFLAGS += -D'DRKAISER_SUPPORT=1' ++PROGRAMMER_OBJS += drkaiser.o ++NEED_PCI := yes ++endif ++ ++ifeq ($(CONFIG_BUSPIRATESPI), yes) ++FEATURE_CFLAGS += -D'BUSPIRATE_SPI_SUPPORT=1' ++PROGRAMMER_OBJS += buspirate_spi.o ++endif ++ ++ifeq ($(CONFIG_DEDIPROG), yes) ++FEATURE_CFLAGS += -D'DEDIPROG_SUPPORT=1' ++FEATURE_LIBS += -lusb ++PROGRAMMER_OBJS += dediprog.o ++endif ++ ++# Ugly, but there's no elif/elseif. ++ifeq ($(CONFIG_SERPROG), yes) ++LIB_OBJS += serial.o ++else ++ifeq ($(CONFIG_BUSPIRATESPI), yes) ++LIB_OBJS += serial.o ++endif ++endif ++ ++ifeq ($(NEED_PCI), yes) ++LIBS += -lpci ++FEATURE_CFLAGS += -D'NEED_PCI=1' ++PROGRAMMER_OBJS += pcidev.o physmap.o internal.o #FIXME: We need to move stuff ++ # from internal.c and pcidev.c to pci.c ++ # internal.c needs to be split ++ # into internal-programmer-only stuff ++ # and a support lib for all internal+pci ++ # based stuff. ++ifeq ($(OS_ARCH), NetBSD) ++LIBS += -lpciutils # The libpci we want. ++LIBS += -l$(shell uname -m) # For (i386|x86_64)_iopl(2). ++endif ++endif ++ ++ifeq ($(CONFIG_PRINT_WIKI), yes) ++FEATURE_CFLAGS += -D'PRINT_WIKI_SUPPORT=1' ++CLI_OBJS += print_wiki.o ++endif ++ ++# We could use PULLED_IN_LIBS, but that would be ugly. ++FEATURE_LIBS += $(shell LC_ALL=C grep -q "NEEDLIBZ := yes" .libdeps && printf "%s" "-lz") ++ ++OBJS = $(CHIP_OBJS) $(CLI_OBJS) $(PROGRAMMER_OBJS) $(LIB_OBJS) + + $(PROGRAM): $(OBJS) +- $(CC) $(LDFLAGS) -o $(PROGRAM) $(OBJS) $(LIBS) $(FEATURE_LIBS) ++ $(CC) $(LDFLAGS) -o $(PROGRAM) $(OBJS) $(FEATURE_LIBS) $(LIBS) + + # TAROPTIONS reduces information leakage from the packager's system. + # If other tar programs support command line arguments for setting uid/gid of +@@ -96,7 +217,7 @@ clean: + rm -f $(PROGRAM) *.o + + distclean: clean +- rm -f .dependencies .features ++ rm -f .dependencies .features .libdeps + + dep: + @$(CC) $(CPPFLAGS) $(SVNDEF) -MM *.c > .dependencies +@@ -108,26 +229,51 @@ compiler: + @printf "Checking for a C compiler... " + @$(shell ( echo "int main(int argc, char **argv)"; \ + echo "{ return 0; }"; ) > .test.c ) +- @$(CC) $(CFLAGS) $(LDFLAGS) .test.c -o .test >/dev/null && \ ++ @$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .test.c -o .test >/dev/null && \ + echo "found." || ( echo "not found."; \ + rm -f .test.c .test; exit 1) + @rm -f .test.c .test + ++ifeq ($(NEED_PCI), yes) + pciutils: compiler +- @printf "Checking for pciutils and zlib... " ++ @printf "Checking for libpci headers... " + @$(shell ( echo "#include "; \ + echo "struct pci_access *pacc;"; \ + echo "int main(int argc, char **argv)"; \ + echo "{ pacc = pci_alloc(); return 0; }"; ) > .test.c ) +- @$(CC) $(CFLAGS) $(LDFLAGS) .test.c -o .test $(LIBS) >/dev/null 2>&1 && \ +- echo "found." || ( echo "not found."; echo; \ +- echo "Please install pciutils-devel and zlib-devel."; \ +- echo "See README for more information."; echo; \ +- rm -f .test.c .test; exit 1) +- @rm -f .test.c .test ++ @$(CC) -c $(CPPFLAGS) $(CFLAGS) .test.c -o .test.o >/dev/null 2>&1 && \ ++ echo "found." || ( echo "not found."; echo; \ ++ echo "Please install libpci headers (package pciutils-devel)."; \ ++ echo "See README for more information."; echo; \ ++ rm -f .test.c .test.o; exit 1) ++ @printf "Checking for libpci... " ++ @$(shell ( echo "#include "; \ ++ echo "int main(int argc, char **argv)"; \ ++ echo "{ return 0; }"; ) > .test1.c ) ++ @$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .test1.c -o .test1 -lpci $(LIBS) >/dev/null 2>&1 && \ ++ echo "found." || ( echo "not found."; echo; \ ++ echo "Please install libpci (package pciutils)."; \ ++ echo "See README for more information."; echo; \ ++ rm -f .test1.c .test1; exit 1) ++ @printf "Checking if libpci is sufficient... " ++ @printf "" > .libdeps ++ @$(CC) $(LDFLAGS) .test.o -o .test -lpci $(LIBS) >/dev/null 2>&1 && \ ++ echo "yes." || ( echo "no."; \ ++ printf "Checking if libz is present and supplies all needed symbols..."; \ ++ $(CC) $(LDFLAGS) .test.o -o .test -lpci -lz $(LIBS) >/dev/null 2>&1 && \ ++ ( echo "yes."; echo "NEEDLIBZ := yes" > .libdeps ) || ( echo "no."; echo; \ ++ echo "Please install libz."; \ ++ echo "See README for more information."; echo; \ ++ rm -f .test.c .test.o .test; exit 1) ) ++ @rm -f .test.c .test.o .test .test1.c .test1 ++else ++pciutils: compiler ++ @printf "" > .libdeps ++endif + + .features: features + ++ifeq ($(CONFIG_FT2232SPI), yes) + features: compiler + @echo "FEATURES := yes" > .features.tmp + @printf "Checking for FTDI support... " +@@ -135,11 +281,16 @@ features: compiler + echo "struct ftdi_context *ftdic = NULL;"; \ + echo "int main(int argc, char **argv)"; \ + echo "{ return ftdi_init(ftdic); }"; ) > .featuretest.c ) +- @$(CC) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest $(LIBS) -lftdi >/dev/null 2>&1 && \ ++ @$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest $(FTDILIBS) $(LIBS) >/dev/null 2>&1 && \ + ( echo "found."; echo "FTDISUPPORT := yes" >> .features.tmp ) || \ + ( echo "not found."; echo "FTDISUPPORT := no" >> .features.tmp ) + @$(DIFF) -q .features.tmp .features >/dev/null 2>&1 && rm .features.tmp || mv .features.tmp .features + @rm -f .featuretest.c .featuretest ++else ++features: compiler ++ @echo "FEATURES := yes" > .features.tmp ++ @$(DIFF) -q .features.tmp .features >/dev/null 2>&1 && rm .features.tmp || mv .features.tmp .features ++endif + + install: $(PROGRAM) + mkdir -p $(DESTDIR)$(PREFIX)/sbin +diff --git a/README b/README +index b6a0792..3082f71 100644 +--- a/README ++++ b/README +@@ -11,6 +11,8 @@ program flash chips. + It supports a wide range of DIP32, PLCC32, DIP8, SO8/SOIC8, TSOP32, and TSOP40 + chips, which use various protocols such as LPC, FWH, parallel flash, or SPI. + ++Please see the flashrom(8) manpage. ++ + + Packaging + --------- +@@ -38,7 +40,7 @@ To build flashrom you need to install the following packages or ports: + + Linux et al: + +- * pciutils ++ * pciutils / libpci + * pciutils-devel / pciutils-dev / libpci-dev + * zlib-devel / zlib1g-dev (only needed if libpci is static) + +@@ -63,10 +65,10 @@ To compile on Solaris, use: + + gmake LDFLAGS="-L$pathtolibpci" CC="gcc -I$pathtopciheaders" CFLAGS=-O2 + +-To compile on DragonFly BSD, use: ++To compile on NetBSD or DragonFly BSD, use: + + ln -s /usr/pkg/include/pciutils pci +- gmake CFLAGS=-I. LDFLAGS="-L/usr/pkg/lib" ++ gmake CFLAGS=-I. LDFLAGS="-L/usr/pkg/lib -Wl,-rpath-link,/usr/pkg/lib" + + To compile and run on Darwin/Mac OS X: + +@@ -79,42 +81,14 @@ Installation + + In order to install flashrom and the manpage into /usr/local, type: + +- sudo make install ++ make install + + For installation in a different directory use DESTDIR, e.g. like this: + +- sudo make DESTDIR=/usr install +- +- +-Usage / Options +---------------- +- +-Please see the flashrom(8) manpage. +- +- +-Exit status +------------ +- +-Please see the flashrom(8) manpage. +- +- +-coreboot Table and Mainboard Identification +--------------------------------------------- +- +-Please see the flashrom(8) manpage. +- +- +-ROM Layout Support +------------------- +- +-Please see the flashrom(8) manpage. +- +- +-Supported Flash Chips / Chipsets / Mainboards +---------------------------------------------- ++ make DESTDIR=/usr install + +-Please check the output of 'flashrom -L' for the list of supported +-flash chips, chipsets/southbridges, mainboards, and flash programmers. ++If you have insufficient permissions for the destination directory, use sudo ++by adding sudo in front of the commands above. + + + Contact +diff --git a/am29f040b.c b/am29f040b.c +index 7f1269c..62774ba 100644 +--- a/am29f040b.c ++++ b/am29f040b.c +@@ -20,9 +20,9 @@ + + #include "flash.h" + +-static int erase_sector_29f040b(struct flashchip *flash, unsigned long address) ++/* FIMXE: Use erase_sector_jedec if not? */ ++int erase_sector_29f040b(struct flashchip *flash, unsigned int address, unsigned int blocklen) + { +- int page_size = flash->page_size; + chipaddr bios = flash->virtual_memory; + + chip_writeb(0xAA, bios + 0x555); +@@ -32,18 +32,30 @@ static int erase_sector_29f040b(struct flashchip *flash, unsigned long address) + chip_writeb(0x55, bios + 0x2AA); + chip_writeb(0x30, bios + address); + +- programmer_delay(2 * 1000 * 1000); ++ programmer_delay(10); + + /* wait for Toggle bit ready */ + toggle_ready_jedec(bios + address); + +- if (check_erased_range(flash, address, page_size)) { ++ if (check_erased_range(flash, address, blocklen)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } + return 0; + } + ++/* erase chip with block_erase() prototype */ ++int erase_chip_29f040b(struct flashchip *flash, unsigned int addr, unsigned int blocklen) ++{ ++ if ((addr != 0) || (blocklen != flash->total_size * 1024)) { ++ fprintf(stderr, "%s called with incorrect arguments\n", ++ __func__); ++ return -1; ++ } ++ return erase_29f040b(flash); ++} ++ ++/* FIXME: use write_sector_jedec? */ + static int write_sector_29f040b(chipaddr bios, uint8_t *src, chipaddr dst, + unsigned int page_size) + { +@@ -84,13 +96,14 @@ int probe_29f040b(struct flashchip *flash) + + programmer_delay(10); + +- printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __FUNCTION__, id1, id2); ++ printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2); + if (id1 == flash->manufacture_id && id2 == flash->model_id) + return 1; + + return 0; + } + ++/* FIXME: use erase_chip_jedec? */ + int erase_29f040b(struct flashchip *flash) + { + int total_size = flash->total_size * 1024; +@@ -123,7 +136,7 @@ int write_29f040b(struct flashchip *flash, uint8_t *buf) + printf("Programming page "); + for (i = 0; i < total_size / page_size; i++) { + /* erase the page before programming */ +- if (erase_sector_29f040b(flash, i * page_size)) { ++ if (erase_sector_29f040b(flash, i * page_size, page_size)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } +diff --git a/bitbang_spi.c b/bitbang_spi.c +new file mode 100644 +index 0000000..8925884 +--- /dev/null ++++ b/bitbang_spi.c +@@ -0,0 +1,164 @@ ++/* ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2009 Carl-Daniel Hailfinger ++ * ++ * 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; version 2 of the License. ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "flash.h" ++#include "spi.h" ++ ++/* Length of half a clock period in usecs */ ++int bitbang_spi_half_period = 0; ++ ++enum bitbang_spi_master bitbang_spi_master = BITBANG_SPI_INVALID; ++ ++const struct bitbang_spi_master_entry bitbang_spi_master_table[] = { ++ {}, /* This entry corresponds to BITBANG_SPI_INVALID. */ ++}; ++ ++const int bitbang_spi_master_count = ARRAY_SIZE(bitbang_spi_master_table); ++ ++void bitbang_spi_set_cs(int val) ++{ ++ bitbang_spi_master_table[bitbang_spi_master].set_cs(val); ++} ++ ++void bitbang_spi_set_sck(int val) ++{ ++ bitbang_spi_master_table[bitbang_spi_master].set_sck(val); ++} ++ ++void bitbang_spi_set_mosi(int val) ++{ ++ bitbang_spi_master_table[bitbang_spi_master].set_mosi(val); ++} ++ ++int bitbang_spi_get_miso(void) ++{ ++ return bitbang_spi_master_table[bitbang_spi_master].get_miso(); ++} ++ ++int bitbang_spi_init(void) ++{ ++ bitbang_spi_set_cs(1); ++ bitbang_spi_set_sck(0); ++ buses_supported = CHIP_BUSTYPE_SPI; ++ return 0; ++} ++ ++uint8_t bitbang_spi_readwrite_byte(uint8_t val) ++{ ++ uint8_t ret = 0; ++ int i; ++ ++ for (i = 7; i >= 0; i--) { ++ bitbang_spi_set_mosi((val >> i) & 1); ++ programmer_delay(bitbang_spi_half_period); ++ bitbang_spi_set_sck(1); ++ ret <<= 1; ++ ret |= bitbang_spi_get_miso(); ++ programmer_delay(bitbang_spi_half_period); ++ bitbang_spi_set_sck(0); ++ } ++ return ret; ++} ++ ++int bitbang_spi_send_command(unsigned int writecnt, unsigned int readcnt, ++ const unsigned char *writearr, unsigned char *readarr) ++{ ++ static unsigned char *bufout = NULL; ++ static unsigned char *bufin = NULL; ++ static int oldbufsize = 0; ++ int bufsize; ++ int i; ++ ++ /* Arbitrary size limitation here. We're only constrained by memory. */ ++ if (writecnt > 65536 || readcnt > 65536) ++ return SPI_INVALID_LENGTH; ++ ++ bufsize = max(writecnt + readcnt, 260); ++ /* Never shrink. realloc() calls are expensive. */ ++ if (bufsize > oldbufsize) { ++ bufout = realloc(bufout, bufsize); ++ if (!bufout) { ++ msg_perr("Out of memory!\n"); ++ if (bufin) ++ free(bufin); ++ exit(1); ++ } ++ bufin = realloc(bufout, bufsize); ++ if (!bufin) { ++ msg_perr("Out of memory!\n"); ++ if (bufout) ++ free(bufout); ++ exit(1); ++ } ++ oldbufsize = bufsize; ++ } ++ ++ memcpy(bufout, writearr, writecnt); ++ /* Shift out 0x00 while reading data. */ ++ memset(bufout + writecnt, 0x00, readcnt); ++ /* Make sure any non-read data is 0xff. */ ++ memset(bufin + writecnt, 0xff, readcnt); ++ ++ bitbang_spi_set_cs(0); ++ for (i = 0; i < readcnt + writecnt; i++) { ++ bufin[i] = bitbang_spi_readwrite_byte(bufout[i]); ++ } ++ programmer_delay(bitbang_spi_half_period); ++ bitbang_spi_set_cs(1); ++ programmer_delay(bitbang_spi_half_period); ++ memcpy(readarr, bufin + writecnt, readcnt); ++ ++ return 0; ++} ++ ++int bitbang_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len) ++{ ++ /* Maximum read length is unlimited, use 64k bytes. */ ++ return spi_read_chunked(flash, buf, start, len, 64 * 1024); ++} ++ ++int bitbang_spi_write_256(struct flashchip *flash, uint8_t *buf) ++{ ++ int total_size = 1024 * flash->total_size; ++ int i; ++ ++ msg_pdbg("total_size is %d\n", total_size); ++ for (i = 0; i < total_size; i += 256) { ++ int l, r; ++ if (i + 256 <= total_size) ++ l = 256; ++ else ++ l = total_size - i; ++ ++ if ((r = spi_nbyte_program(i, &buf[i], l))) { ++ msg_perr("%s: write fail %d\n", __func__, r); ++ return 1; ++ } ++ ++ while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP) ++ /* loop */; ++ } ++ ++ return 0; ++} +diff --git a/board_enable.c b/board_enable.c +index c87e782..2de5fd7 100644 +--- a/board_enable.c ++++ b/board_enable.c +@@ -66,6 +66,35 @@ void sio_mask(uint16_t port, uint8_t reg, uint8_t data, uint8_t mask) + OUTB(tmp | (data & mask), port + 1); + } + ++/* Not used yet. */ ++#if 0 ++static int enable_flash_decode_superio(void) ++{ ++ int ret; ++ uint8_t tmp; ++ ++ switch (superio.vendor) { ++ case SUPERIO_VENDOR_NONE: ++ ret = -1; ++ break; ++ case SUPERIO_VENDOR_ITE: ++ enter_conf_mode_ite(superio.port); ++ /* Enable flash mapping. Works for most old ITE style SuperI/O. */ ++ tmp = sio_read(superio.port, 0x24); ++ tmp |= 0xfc; ++ sio_write(superio.port, 0x24, tmp); ++ exit_conf_mode_ite(superio.port); ++ ret = 0; ++ break; ++ default: ++ printf_debug("Unhandled SuperI/O type!\n"); ++ ret = -1; ++ break; ++ } ++ return ret; ++} ++#endif ++ + /** + * Winbond W83627HF: Raise GPIO24. + * +@@ -162,25 +191,67 @@ static void w836xx_memw_enable(uint16_t port) + } + + /** +- * Common routine for several VT823x based boards. ++ * Suited for: ++ * - EPoX EP-8K5A2: VIA KT333 + VT8235. ++ * - Albatron PM266A Pro: VIA P4M266A + VT8235. ++ * - Shuttle AK31 (all versions): VIA KT266 + VT8233. ++ * - ASUS A7V8X-MX SE and A7V400-MX: AMD K7 + VIA KM400A + VT8235 ++ * - Tyan S2498 (Tomcat K7M): AMD Geode NX + VIA KM400 + VT8237. + */ +-static void vt823x_set_all_writes_to_lpc(struct pci_dev *dev) ++static int w836xx_memw_enable_2e(const char *name) + { +- uint8_t val; ++ w836xx_memw_enable(0x2E); ++ ++ return 0; ++} ++ ++/** ++ * ++ */ ++static int it8705f_write_enable(uint8_t port, const char *name) ++{ ++ enter_conf_mode_ite(port); ++ sio_mask(port, 0x24, 0x04, 0x04); /* Flash ROM I/F Writes Enable */ ++ exit_conf_mode_ite(port); + +- /* All memory cycles, not just ROM ones, go to LPC. */ +- val = pci_read_byte(dev, 0x59); +- val &= ~0x80; +- pci_write_byte(dev, 0x59, val); ++ return 0; ++} ++ ++/** ++ * Suited for: ++ * - AOpen vKM400Am-S: VIA KM400 + VT8237 + IT8705F. ++ * - Biostar P4M80-M4: VIA P4M800 + VT8237 + IT8705AF ++ * - Elitegroup K7S6A: SiS745 + ITE IT8705F ++ * - Elitegroup K7VTA3: VIA Apollo KT266/A/333 + VIA VT8235 + ITE IT8705F ++ * - GIGABYTE GA-7VT600: VIA KT600 + VT8237 + IT8705 ++ * - Shuttle AK38N: VIA KT333CF + VIA VT8235 + ITE IT8705F ++ * ++ * SIS950 superio probably requires the same flash write enable. ++ */ ++static int it8705f_write_enable_2e(const char *name) ++{ ++ return it8705f_write_enable(0x2e, name); + } + + /** + * VT823x: Set one of the GPIO pins. + */ +-static void vt823x_gpio_set(struct pci_dev *dev, uint8_t gpio, int raise) ++static int via_vt823x_gpio_set(uint8_t gpio, int raise) + { ++ struct pci_dev *dev; + uint16_t base; +- uint8_t val, bit; ++ uint8_t val, bit, offset; ++ ++ dev = pci_dev_find_vendorclass(0x1106, 0x0601); ++ switch (dev->device_id) { ++ case 0x3177: /* VT8235 */ ++ case 0x3227: /* VT8237R */ ++ case 0x3337: /* VT8237A */ ++ break; ++ default: ++ fprintf(stderr, "\nERROR: VT823x ISA bridge not found.\n"); ++ return -1; ++ } + + if ((gpio >= 12) && (gpio <= 15)) { + /* GPIO12-15 -> output */ +@@ -192,126 +263,75 @@ static void vt823x_gpio_set(struct pci_dev *dev, uint8_t gpio, int raise) + val = pci_read_byte(dev, 0xE4); + val |= 0x20; + pci_write_byte(dev, 0xE4, val); ++ } else if (gpio == 5) { ++ val = pci_read_byte(dev, 0xE4); ++ val |= 0x01; ++ pci_write_byte(dev, 0xE4, val); + } else { + fprintf(stderr, "\nERROR: " + "VT823x GPIO%02d is not implemented.\n", gpio); +- return; ++ return -1; + } + +- /* Now raise/drop the GPIO line itself. */ +- bit = 0x01 << (gpio - 8); +- + /* We need the I/O Base Address for this board's flash enable. */ + base = pci_read_word(dev, 0x88) & 0xff80; + +- val = INB(base + 0x4D); ++ offset = 0x4C + gpio / 8; ++ bit = 0x01 << (gpio % 8); ++ ++ val = INB(base + offset); + if (raise) + val |= bit; + else + val &= ~bit; +- OUTB(val, base + 0x4D); +-} +- +-/** +- * Suited for VIAs EPIA M and MII, and maybe other CLE266 based EPIAs. +- * +- * We don't need to do this when using coreboot, GPIO15 is never lowered there. +- */ +-static int board_via_epia_m(const char *name) +-{ +- struct pci_dev *dev; +- +- dev = pci_dev_find(0x1106, 0x3177); /* VT8235 ISA bridge */ +- if (!dev) { +- fprintf(stderr, "\nERROR: VT8235 ISA bridge not found.\n"); +- return -1; +- } +- +- /* GPIO15 is connected to write protect. */ +- vt823x_gpio_set(dev, 15, 1); ++ OUTB(val, base + offset); + + return 0; + } + + /** +- * Suited for: +- * - ASUS A7V8X-MX SE and A7V400-MX: AMD K7 + VIA KM400A + VT8235 +- * - Tyan S2498 (Tomcat K7M): AMD Geode NX + VIA KM400 + VT8237. ++ * Suited for Asus M2V-MX: VIA K8M890 + VT8237A + IT8716F + */ +-static int board_asus_a7v8x_mx(const char *name) ++static int via_vt823x_gpio5_raise(const char *name) + { +- struct pci_dev *dev; +- +- dev = pci_dev_find(0x1106, 0x3177); /* VT8235 ISA bridge */ +- if (!dev) +- dev = pci_dev_find(0x1106, 0x3227); /* VT8237 ISA bridge */ +- if (!dev) { +- fprintf(stderr, "\nERROR: VT823x ISA bridge not found.\n"); +- return -1; +- } +- +- vt823x_set_all_writes_to_lpc(dev); +- w836xx_memw_enable(0x2E); +- +- return 0; ++ /* On M2V-MX: GPO5 is connected to WP# and TBL#. */ ++ return via_vt823x_gpio_set(5, 1); + } + + /** +- * Suited for VIAs EPIA SP and EPIA CN. ++ * Suited for VIAs EPIA N & NL. + */ +-static int board_via_epia_sp(const char *name) ++static int via_vt823x_gpio9_raise(const char *name) + { +- struct pci_dev *dev; +- +- dev = pci_dev_find(0x1106, 0x3227); /* VT8237R ISA bridge */ +- if (!dev) { +- fprintf(stderr, "\nERROR: VT8237R ISA bridge not found.\n"); +- return -1; +- } +- +- vt823x_set_all_writes_to_lpc(dev); +- +- return 0; ++ return via_vt823x_gpio_set(9, 1); + } + + /** +- * Suited for VIAs EPIA N & NL. ++ * Suited for VIAs EPIA M and MII, and maybe other CLE266 based EPIAs. ++ * ++ * We don't need to do this for EPIA M when using coreboot, GPIO15 is never ++ * lowered there. + */ +-static int board_via_epia_n(const char *name) ++static int via_vt823x_gpio15_raise(const char *name) + { +- struct pci_dev *dev; +- +- dev = pci_dev_find(0x1106, 0x3227); /* VT8237R ISA bridge */ +- if (!dev) { +- fprintf(stderr, "\nERROR: VT8237R ISA bridge not found.\n"); +- return -1; +- } +- +- /* All memory cycles, not just ROM ones, go to LPC */ +- vt823x_set_all_writes_to_lpc(dev); +- +- /* GPIO9 -> output */ +- vt823x_gpio_set(dev, 9, 1); +- +- return 0; ++ return via_vt823x_gpio_set(15, 1); + } + + /** +- * Suited for EPoX EP-8K5A2 and Albatron PM266A Pro. ++ * Winbond W83697HF Super I/O + VIA VT8235 southbridge ++ * ++ * Suited for: ++ * - MSI KT4V and KT4V-L: AMD K7 + VIA KT400 + VT8235 ++ * - MSI KT4 Ultra: AMD K7 + VIA KT400 + VT8235 + */ +-static int board_epox_ep_8k5a2(const char *name) ++static int board_msi_kt4v(const char *name) + { +- struct pci_dev *dev; +- +- dev = pci_dev_find(0x1106, 0x3177); /* VT8235 ISA bridge */ +- if (!dev) { +- fprintf(stderr, "\nERROR: VT8235 ISA bridge not found.\n"); +- return -1; +- } ++ int ret; + ++ ret = via_vt823x_gpio_set(12, 1); + w836xx_memw_enable(0x2E); + +- return 0; ++ return ret; + } + + /** +@@ -390,94 +410,126 @@ static int board_asus_p5a(const char *name) + return 0; + } + ++/* ++ * Set GPIO lines in the Broadcom HT-1000 southbridge. ++ * ++ * It's not a Super I/O but it uses the same index/data port method. ++ */ ++static int board_hp_dl145_g3_enable(const char *name) ++{ ++ /* GPIO 0 reg from PM regs */ ++ /* Set GPIO 2 and 5 high, connected to flash WP# and TBL# pins. */ ++ sio_mask(0xcd6, 0x44, 0x24, 0x24); ++ ++ return 0; ++} ++ + static int board_ibm_x3455(const char *name) + { +- /* Set GPIO lines in the Broadcom HT-1000 southbridge. */ +- /* It's not a Super I/O but it uses the same index/data port method. */ ++ /* raise gpio13 */ + sio_mask(0xcd6, 0x45, 0x20, 0x20); + + return 0; + } + + /** +- * Suited for the Gigabyte GA-K8N-SLI: CK804 southbridge. ++ * Suited for Shuttle FN25 (SN25P): AMD S939 + Nvidia CK804 (nForce4). + */ +-static int board_ga_k8n_sli(const char *name) ++static int board_shuttle_fn25(const char *name) + { + struct pci_dev *dev; +- uint32_t base; +- uint8_t tmp; + +- dev = pci_dev_find(0x10DE, 0x0050); /* NVIDIA CK804 LPC */ ++ dev = pci_dev_find(0x10DE, 0x0050); /* NVIDIA CK804 ISA Bridge. */ + if (!dev) { +- fprintf(stderr, "\nERROR: NVIDIA LPC bridge not found.\n"); ++ fprintf(stderr, ++ "\nERROR: NVIDIA nForce4 ISA bridge not found.\n"); + return -1; + } + +- base = pci_read_long(dev, 0x64) & 0x0000FF00; /* System control area */ +- +- /* if anyone knows more about nvidia lpcs, feel free to explain this */ +- tmp = INB(base + 0xE1); +- tmp |= 0x05; +- OUTB(tmp, base + 0xE1); +- +- return 0; +-} +- +-static int board_hp_dl145_g3_enable(const char *name) +-{ +- /* Set GPIO lines in the Broadcom HT-1000 southbridge. */ +- /* GPIO 0 reg from PM regs */ +- /* Set GPIO 2 and 5 high, connected to flash WP# and TBL# pins. */ +- /* It's not a Super I/O but it uses the same index/data port method. */ +- sio_mask(0xcd6, 0x44, 0x24, 0x24); ++ /* one of those bits seems to be connected to TBL#, but -ENOINFO. */ ++ pci_write_byte(dev, 0x92, 0); + + return 0; + } + + /** +- * Suited for EPoX EP-BX3, and maybe some other Intel 440BX based boards. ++ * Very similar to AMD 8111 IO Hub. + */ +-static int board_epox_ep_bx3(const char *name) ++static int nvidia_mcp_gpio_set(int gpio, int raise) + { ++ struct pci_dev *dev; ++ uint16_t base; + uint8_t tmp; + +- /* Raise GPIO22. */ +- tmp = INB(0x4036); +- OUTB(tmp, 0xEB); ++ if ((gpio < 0) || (gpio >= 0x40)) { ++ fprintf(stderr, "\nERROR: unsupported GPIO: %d.\n", gpio); ++ return -1; ++ } + +- tmp |= 0x40; ++ /* First, check the ISA Bridge */ ++ dev = pci_dev_find_vendorclass(0x10DE, 0x0601); ++ switch (dev->device_id) { ++ case 0x0030: /* CK804 */ ++ case 0x0050: /* MCP04 */ ++ case 0x0060: /* MCP2 */ ++ break; ++ default: ++ /* Newer MCPs use the SMBus Controller */ ++ dev = pci_dev_find_vendorclass(0x10DE, 0x0C05); ++ switch (dev->device_id) { ++ case 0x0264: /* MCP51 */ ++ break; ++ default: ++ fprintf(stderr, ++ "\nERROR: no nVidia LPC/SMBus controller found.\n"); ++ return -1; ++ } ++ break; ++ } + +- OUTB(tmp, 0x4036); +- OUTB(tmp, 0xEB); ++ base = pci_read_long(dev, 0x64) & 0x0000FF00; /* System control area */ ++ base += 0xC0; ++ ++ tmp = INB(base + gpio); ++ tmp &= ~0x0F; /* null lower nibble */ ++ tmp |= 0x04; /* gpio -> output. */ ++ if (raise) ++ tmp |= 0x01; ++ OUTB(tmp, base + gpio); + + return 0; + } + + /** +- * Suited for Acorp 6A815EPD. ++ * Suited for MSI K8N Neo4: nVidia CK804. + */ +-static int board_acorp_6a815epd(const char *name) ++static int nvidia_mcp_gpio2_raise(const char *name) + { +- struct pci_dev *dev; +- uint16_t port; +- uint8_t val; +- +- dev = pci_dev_find(0x8086, 0x2440); /* Intel ICH2 LPC */ +- if (!dev) { +- fprintf(stderr, "\nERROR: ICH2 LPC bridge not found.\n"); +- return -1; +- } ++ return nvidia_mcp_gpio_set(0x02, 1); ++} + +- /* Use GPIOBASE register to find where the GPIO is mapped. */ +- port = (pci_read_word(dev, 0x58) & 0xFFC0) + 0xE; ++/** ++ * Suited for ASUS P5ND2-SLI Deluxe: LGA775 + nForce4 SLI + MCP04. ++ */ ++static int nvidia_mcp_gpio10_raise(const char *name) ++{ ++ return nvidia_mcp_gpio_set(0x10, 1); ++} + +- val = INB(port); +- val |= 0x80; /* Top Block Lock -- pin 8 of PLCC32 */ +- val |= 0x40; /* Lower Blocks Lock -- pin 7 of PLCC32 */ +- OUTB(val, port); ++/** ++ * Suited for the Gigabyte GA-K8N-SLI: CK804 southbridge. ++ */ ++static int nvidia_mcp_gpio21_raise(const char *name) ++{ ++ return nvidia_mcp_gpio_set(0x21, 0x01); ++} + +- return 0; ++/** ++ * Suited for EPoX EP-8RDA3+: Socket A + nForce2 Ultra 400 + MCP2. ++ */ ++static int nvidia_mcp_gpio31_raise(const char *name) ++{ ++ return nvidia_mcp_gpio_set(0x31, 0x01); + } + + /** +@@ -522,165 +574,345 @@ static int board_artecgroup_dbe6x(const char *name) + } + + /** +- * Set the specified GPIO on the specified ICHx southbridge to high. +- * +- * @param name The name of this board. +- * @param ich_vendor PCI vendor ID of the specified ICHx southbridge. +- * @param ich_device PCI device ID of the specified ICHx southbridge. +- * @param gpiobase_reg GPIOBASE register offset in the LPC bridge. +- * @param gp_lvl Offset of GP_LVL register in I/O space, relative to GPIOBASE. +- * @param gp_lvl_bitmask GP_LVL bitmask (set GPIO bits to 1, all others to 0). +- * @param gpio_bit The bit (GPIO) which shall be set to high. +- * @return If the write-enable was successful return 0, otherwise return -1. ++ * Helper function to raise/drop a given gpo line on intel PIIX4{,E,M} + */ +-static int ich_gpio_raise(const char *name, uint16_t ich_vendor, +- uint16_t ich_device, uint8_t gpiobase_reg, +- uint8_t gp_lvl, uint32_t gp_lvl_bitmask, +- unsigned int gpio_bit) ++static int intel_piix4_gpo_set(unsigned int gpo, int raise) + { + struct pci_dev *dev; +- uint16_t gpiobar; +- uint32_t reg32; ++ uint32_t tmp, base; + +- dev = pci_dev_find(ich_vendor, ich_device); /* Intel ICHx LPC */ ++ dev = pci_dev_find(0x8086, 0x7110); /* Intel PIIX4 ISA bridge */ + if (!dev) { +- fprintf(stderr, "\nERROR: ICHx LPC dev %4x:%4x not found.\n", +- ich_vendor, ich_device); ++ fprintf(stderr, "\nERROR: Intel PIIX4 ISA bridge not found.\n"); + return -1; + } + +- /* Use GPIOBASE register to find the I/O space for GPIO. */ +- gpiobar = pci_read_word(dev, gpiobase_reg) & gp_lvl_bitmask; ++ /* sanity check */ ++ if (gpo > 30) { ++ fprintf(stderr, "\nERROR: Intel PIIX4 has no GPO%d.\n", gpo); ++ return -1; ++ } + +- /* Set specified GPIO to high. */ +- reg32 = INL(gpiobar + gp_lvl); +- reg32 |= (1 << gpio_bit); +- OUTL(reg32, gpiobar + gp_lvl); ++ /* these are dual function pins which are most likely in use already */ ++ if (((gpo >= 1) && (gpo <= 7)) || ++ ((gpo >= 9) && (gpo <= 21)) || (gpo == 29)) { ++ fprintf(stderr, "\nERROR: Unsupported PIIX4 GPO%d.\n", gpo); ++ return -1; ++ } ++ ++ /* dual function that need special enable. */ ++ if ((gpo >= 22) && (gpo <= 26)) { ++ tmp = pci_read_long(dev, 0xB0); /* GENCFG */ ++ switch (gpo) { ++ case 22: /* XBUS: XDIR#/GPO22 */ ++ case 23: /* XBUS: XOE#/GPO23 */ ++ tmp |= 1 << 28; ++ break; ++ case 24: /* RTCSS#/GPO24 */ ++ tmp |= 1 << 29; ++ break; ++ case 25: /* RTCALE/GPO25 */ ++ tmp |= 1 << 30; ++ break; ++ case 26: /* KBCSS#/GPO26 */ ++ tmp |= 1 << 31; ++ break; ++ } ++ pci_write_long(dev, 0xB0, tmp); ++ } ++ ++ /* GPO {0,8,27,28,30} are always available. */ ++ ++ dev = pci_dev_find(0x8086, 0x7113); /* Intel PIIX4 PM */ ++ if (!dev) { ++ fprintf(stderr, "\nERROR: Intel PIIX4 PM not found.\n"); ++ return -1; ++ } ++ ++ /* PM IO base */ ++ base = pci_read_long(dev, 0x40) & 0x0000FFC0; ++ ++ tmp = INL(base + 0x34); /* GPO register */ ++ if (raise) ++ tmp |= 0x01 << gpo; ++ else ++ tmp |= ~(0x01 << gpo); ++ OUTL(tmp, base + 0x34); + + return 0; + } + + /** +- * Suited for ASUS P4B266. ++ * Suited for EPoX EP-BX3, and maybe some other Intel 440BX based boards. + */ +-static int ich2_gpio22_raise(const char *name) ++static int board_epox_ep_bx3(const char *name) + { +- return ich_gpio_raise(name, 0x8086, 0x2440, 0x58, 0x0c, 0xffc0, 22); ++ return intel_piix4_gpo_set(22, 1); + } + + /** +- * Suited for MSI MS-7046. ++ * Set a GPIO line on a given intel ICH LPC controller. + */ +-static int ich6_gpio19_raise(const char *name) ++static int intel_ich_gpio_set(int gpio, int raise) + { +- return ich_gpio_raise(name, 0x8086, 0x2640, 0x48, 0x0c, 0xffc0, 19); +-} ++ /* table mapping the different intel ICH LPC chipsets. */ ++ static struct { ++ uint16_t id; ++ uint8_t base_reg; ++ uint32_t bank0; ++ uint32_t bank1; ++ uint32_t bank2; ++ } intel_ich_gpio_table[] = { ++ {0x2410, 0x58, 0x0FE30000, 0, 0}, /* 82801AA (ICH) */ ++ {0x2420, 0x58, 0x0FE30000, 0, 0}, /* 82801AB (ICH0) */ ++ {0x2440, 0x58, 0x1BFF391B, 0, 0}, /* 82801BA (ICH2) */ ++ {0x244C, 0x58, 0x1A23399B, 0, 0}, /* 82801BAM (ICH2M) */ ++ {0x2450, 0x58, 0x1BFF0000, 0, 0}, /* 82801E (C-ICH) */ ++ {0x2480, 0x58, 0x1BFF0000, 0x00000FFF, 0}, /* 82801CA (ICH3-S) */ ++ {0x248C, 0x58, 0x1A230000, 0x00000FFF, 0}, /* 82801CAM (ICH3-M) */ ++ {0x24C0, 0x58, 0x1BFF0000, 0x00000FFF, 0}, /* 82801DB/DBL (ICH4/ICH4-L) */ ++ {0x24CC, 0x58, 0x1A030000, 0x00000FFF, 0}, /* 82801DBM (ICH4-M) */ ++ {0x24D0, 0x58, 0x1BFF0000, 0x00030305, 0}, /* 82801EB/ER (ICH5/ICH5R) */ ++ {0x2640, 0x48, 0x1BFF0000, 0x00030307, 0}, /* 82801FB/FR (ICH6/ICH6R) */ ++ {0x2641, 0x48, 0x1BFF0000, 0x00030307, 0}, /* 82801FBM (ICH6M) */ ++ {0x27B8, 0x48, 0xFFFFFFFF, 0x000300FF, 0}, /* 82801GB/GR (ICH7 Family) */ ++ {0x27B9, 0x48, 0xFFEBFFFE, 0x000300FE, 0}, /* 82801GBM (ICH7-M) */ ++ {0x27BD, 0x48, 0xFFEBFFFE, 0x000300FE, 0}, /* 82801GHM (ICH7-M DH) */ ++ {0x2810, 0x48, 0xFFFFFFFF, 0x00FF0FFF, 0}, /* 82801HB/HR (ICH8/R) */ ++ {0x2811, 0x48, 0xFFFFFFFF, 0x00FF0FFF, 0}, /* 82801HBM (ICH8M-E) */ ++ {0x2812, 0x48, 0xFFFFFFFF, 0x00FF0FFF, 0}, /* 82801HH (ICH8DH) */ ++ {0x2814, 0x48, 0xFFFFFFFF, 0x00FF0FFF, 0}, /* 82801HO (ICH8DO) */ ++ {0x2815, 0x48, 0xFFFFFFFF, 0x00FF0FFF, 0}, /* 82801HEM (ICH8M) */ ++ {0x2912, 0x48, 0xFFFFFFFF, 0x00FFFFFF, 0}, /* 82801IH (ICH9DH) */ ++ {0x2914, 0x48, 0xFFFFFFFF, 0x00FFFFFF, 0}, /* 82801IO (ICH9DO) */ ++ {0x2916, 0x48, 0xFFFFFFFF, 0x00FFFFFF, 0}, /* 82801IR (ICH9R) */ ++ {0x2917, 0x48, 0xFFFFFFFF, 0x00FFFFFF, 0}, /* 82801IEM (ICH9M-E) */ ++ {0x2918, 0x48, 0xFFFFFFFF, 0x00FFFFFF, 0}, /* 82801IB (ICH9) */ ++ {0x2919, 0x48, 0xFFFFFFFF, 0x00FFFFFF, 0}, /* 82801IBM (ICH9M) */ ++ {0x3A14, 0x48, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000100}, /* 82801JDO (ICH10DO) */ ++ {0x3A16, 0x48, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000100}, /* 82801JIR (ICH10R) */ ++ {0x3A18, 0x48, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000100}, /* 82801JIB (ICH10) */ ++ {0x3A1A, 0x48, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000100}, /* 82801JD (ICH10D) */ ++ {0, 0, 0, 0, 0} /* end marker */ ++ }; + +-static int board_kontron_986lcd_m(const char *name) +-{ + struct pci_dev *dev; +- uint16_t gpiobar; +- uint32_t val; +- +-#define ICH7_GPIO_LVL2 0x38 ++ uint16_t base; ++ uint32_t tmp; ++ int i, allowed; ++ ++ /* First, look for a known LPC bridge */ ++ for (dev = pacc->devices; dev; dev = dev->next) { ++ pci_fill_info(dev, PCI_FILL_CLASS); ++ if ((dev->vendor_id == 0x8086) && ++ (dev->device_class == 0x0601)) { /* ISA Bridge */ ++ /* Is this device in our list? */ ++ for (i = 0; intel_ich_gpio_table[i].id; i++) ++ if (dev->device_id == intel_ich_gpio_table[i].id) ++ break; ++ ++ if (intel_ich_gpio_table[i].id) ++ break; ++ } ++ } + +- dev = pci_dev_find(0x8086, 0x27b8); /* Intel ICH7 LPC */ + if (!dev) { +- // This will never happen on this board +- fprintf(stderr, "\nERROR: ICH7 LPC bridge not found.\n"); ++ fprintf(stderr, "\nERROR: No Known Intel LPC Bridge found.\n"); + return -1; + } + +- /* Use GPIOBASE register to find where the GPIO is mapped. */ +- gpiobar = pci_read_word(dev, 0x48) & 0xfffc; ++ /* According to the datasheets, all intel ICHs have the gpio bar 5:1 ++ strapped to zero. From some mobile ich9 version on, this becomes ++ 6:1. The mask below catches all. */ ++ base = pci_read_word(dev, intel_ich_gpio_table[i].base_reg) & 0xFFC0; + +- val = INL(gpiobar + ICH7_GPIO_LVL2); /* GP_LVL2 */ +- printf_debug("\nGPIOBAR=0x%04x GP_LVL: 0x%08x\n", gpiobar, val); ++ /* check whether the line is allowed */ ++ if (gpio < 32) ++ allowed = (intel_ich_gpio_table[i].bank0 >> gpio) & 0x01; ++ else if (gpio < 64) ++ allowed = (intel_ich_gpio_table[i].bank1 >> (gpio - 32)) & 0x01; ++ else ++ allowed = (intel_ich_gpio_table[i].bank2 >> (gpio - 64)) & 0x01; + +- /* bit 2 (0x04) = 0 #TBL --> bootblock locking = 1 +- * bit 2 (0x04) = 1 #TBL --> bootblock locking = 0 +- * bit 3 (0x08) = 0 #WP --> block locking = 1 +- * bit 3 (0x08) = 1 #WP --> block locking = 0 +- * +- * To enable full block locking, you would do: +- * val &= ~ ((1 << 2) | (1 << 3)); +- */ +- val |= (1 << 2) | (1 << 3); ++ if (!allowed) { ++ fprintf(stderr, "\nERROR: This Intel LPC Bridge does not allow" ++ " setting GPIO%02d\n", gpio); ++ return -1; ++ } + +- OUTL(val, gpiobar + ICH7_GPIO_LVL2); ++ printf("\nIntel ICH LPC Bridge: %sing GPIO%02d.\n", ++ raise ? "Rais" : "Dropp", gpio); + +- return 0; +-} ++ if (gpio < 32) { ++ /* Set line to GPIO */ ++ tmp = INL(base); ++ /* ICH/ICH0 multiplexes 27/28 on the line set. */ ++ if ((gpio == 28) && ++ ((dev->device_id == 0x2410) || (dev->device_id == 0x2420))) ++ tmp |= 1 << 27; ++ else ++ tmp |= 1 << gpio; ++ OUTL(tmp, base); ++ ++ /* As soon as we are talking to ICH8 and above, this register ++ decides whether we can set the gpio or not. */ ++ if (dev->device_id > 0x2800) { ++ tmp = INL(base); ++ if (!(tmp & (1 << gpio))) { ++ fprintf(stderr, "\nERROR: This Intel LPC Bridge" ++ " does not allow setting GPIO%02d\n", ++ gpio); ++ return -1; ++ } ++ } + +-/** +- * Suited for: +- * - Biostar P4M80-M4: VIA P4M800 + VT8237 + IT8705AF +- * - GIGABYTE GA-7VT600: VIA KT600 + VT8237 + IT8705 +- */ +-static int it8705_rom_write_enable(const char *name) +-{ +- /* enter IT87xx conf mode */ +- enter_conf_mode_ite(0x2e); ++ /* Set GPIO to OUTPUT */ ++ tmp = INL(base + 0x04); ++ tmp &= ~(1 << gpio); ++ OUTL(tmp, base + 0x04); ++ ++ /* Raise GPIO line */ ++ tmp = INL(base + 0x0C); ++ if (raise) ++ tmp |= 1 << gpio; ++ else ++ tmp &= ~(1 << gpio); ++ OUTL(tmp, base + 0x0C); ++ } else if (gpio < 64) { ++ gpio -= 32; ++ ++ /* Set line to GPIO */ ++ tmp = INL(base + 0x30); ++ tmp |= 1 << gpio; ++ OUTL(tmp, base + 0x30); ++ ++ /* As soon as we are talking to ICH8 and above, this register ++ decides whether we can set the gpio or not. */ ++ if (dev->device_id > 0x2800) { ++ tmp = INL(base + 30); ++ if (!(tmp & (1 << gpio))) { ++ fprintf(stderr, "\nERROR: This Intel LPC Bridge" ++ " does not allow setting GPIO%02d\n", ++ gpio + 32); ++ return -1; ++ } ++ } + +- /* select right flash chip */ +- sio_mask(0x2e, 0x22, 0x80, 0x80); ++ /* Set GPIO to OUTPUT */ ++ tmp = INL(base + 0x34); ++ tmp &= ~(1 << gpio); ++ OUTL(tmp, base + 0x34); ++ ++ /* Raise GPIO line */ ++ tmp = INL(base + 0x38); ++ if (raise) ++ tmp |= 1 << gpio; ++ else ++ tmp &= ~(1 << gpio); ++ OUTL(tmp, base + 0x38); ++ } else { ++ gpio -= 64; ++ ++ /* Set line to GPIO */ ++ tmp = INL(base + 0x40); ++ tmp |= 1 << gpio; ++ OUTL(tmp, base + 0x40); ++ ++ tmp = INL(base + 40); ++ if (!(tmp & (1 << gpio))) { ++ fprintf(stderr, "\nERROR: This Intel LPC Bridge does " ++ "not allow setting GPIO%02d\n", gpio + 64); ++ return -1; ++ } + +- /* bit 3: flash chip write enable +- * bit 7: map flash chip at 1MB-128K (why though? ignoring this.) +- */ +- sio_mask(0x2e, 0x24, 0x04, 0x04); ++ /* Set GPIO to OUTPUT */ ++ tmp = INL(base + 0x44); ++ tmp &= ~(1 << gpio); ++ OUTL(tmp, base + 0x44); + +- /* exit IT87xx conf mode */ +- exit_conf_mode_ite(0x2e); ++ /* Raise GPIO line */ ++ tmp = INL(base + 0x48); ++ if (raise) ++ tmp |= 1 << gpio; ++ else ++ tmp &= ~(1 << gpio); ++ OUTL(tmp, base + 0x48); ++ } + + return 0; + } + + /** +- * Suited for AOpen vKM400Am-S: VIA KM400 + VT8237 + IT8705F. ++ * Suited for Abit IP35: Intel P35 + ICH9R. + */ +-static int board_aopen_vkm400(const char *name) ++static int intel_ich_gpio16_raise(const char *name) + { +- struct pci_dev *dev; ++ return intel_ich_gpio_set(16, 1); ++} + +- dev = pci_dev_find(0x1106, 0x3227); /* VT8237 ISA bridge */ +- if (!dev) { +- fprintf(stderr, "\nERROR: VT8237 ISA bridge not found.\n"); +- return -1; +- } ++/** ++ * Suited for MSI MS-7046: LGA775 + 915P + ICH6. ++ */ ++static int intel_ich_gpio19_raise(const char *name) ++{ ++ return intel_ich_gpio_set(19, 1); ++} + +- vt823x_set_all_writes_to_lpc(dev); ++/** ++ * Suited for: ++ * - Asus P4B266LM (Sony Vaio PCV-RX650): socket478 + 845D + ICH2. ++ * - Asus P4P800-E Deluxe: Intel socket478 + 865PE + ICH5R. ++ */ ++static int intel_ich_gpio21_raise(const char *name) ++{ ++ return intel_ich_gpio_set(21, 1); ++} + +- return it8705_rom_write_enable(name); ++/** ++ * Suited for ASUS P4B266: socket478 + intel 845D + ICH2. ++ */ ++static int intel_ich_gpio22_raise(const char *name) ++{ ++ return intel_ich_gpio_set(22, 1); + } + + /** +- * Winbond W83697HF Super I/O + VIA VT8235 southbridge +- * + * Suited for: +- * - MSI KT4V and KT4V-L: AMD K7 + VIA KT400 + VT8235 +- * - MSI KT4 Ultra: AMD K7 + VIA KT400 + VT8235 +- * - MSI KT3 Ultra2: AMD K7 + VIA KT333 + VT8235 ++ * - Dell Poweredge 1850: Intel PPGA604 + E7520 + ICH5R. ++ * - ASRock P4i65GV: Intel Socket478 + 865GV + ICH5R. + */ +-static int board_msi_kt4v(const char *name) ++static int intel_ich_gpio23_raise(const char *name) + { +- struct pci_dev *dev; +- uint8_t val; ++ return intel_ich_gpio_set(23, 1); ++} + +- dev = pci_dev_find(0x1106, 0x3177); /* VT8235 ISA bridge */ +- if (!dev) { +- fprintf(stderr, "\nERROR: VT823x ISA bridge not found.\n"); +- return -1; +- } ++/** ++ * Suited for Acorp 6A815EPD: socket 370 + intel 815 + ICH2. ++ */ ++static int board_acorp_6a815epd(const char *name) ++{ ++ int ret; + +- val = pci_read_byte(dev, 0x59); +- val &= 0x0c; +- pci_write_byte(dev, 0x59, val); ++ /* Lower Blocks Lock -- pin 7 of PLCC32 */ ++ ret = intel_ich_gpio_set(22, 1); ++ if (!ret) /* Top Block Lock -- pin 8 of PLCC32 */ ++ ret = intel_ich_gpio_set(23, 1); + +- vt823x_gpio_set(dev, 12, 1); +- w836xx_memw_enable(0x2E); ++ return ret; ++} + +- return 0; ++/** ++ * Suited for Kontron 986LCD-M: socket478 + 915GM + ICH7R. ++ */ ++static int board_kontron_986lcd_m(const char *name) ++{ ++ int ret; ++ ++ ret = intel_ich_gpio_set(34, 1); /* #TBL */ ++ if (!ret) ++ ret = intel_ich_gpio_set(35, 1); /* #WP */ ++ ++ return ret; + } + + /** +@@ -715,23 +947,35 @@ static int board_soyo_sy_7vca(const char *name) + return 0; + } + +-static int it8705f_write_enable(uint8_t port, const char *name) +-{ +- enter_conf_mode_ite(port); +- sio_mask(port, 0x24, 0x04, 0x04); /* Flash ROM I/F Writes Enable */ +- exit_conf_mode_ite(port); +- +- return 0; +-} +- + /** +- * Suited for: +- * - Shuttle AK38N: VIA KT333CF + VIA VT8235 + ITE IT8705F +- * - Elitegroup K7VTA3: VIA Apollo KT266/A/333 + VIA VT8235 + ITE IT8705F ++ * Enable some GPIO pin on SiS southbridge. ++ * Suited for MSI 651M-L: SiS651 / SiS962 + */ +-static int it8705f_write_enable_2e(const char *name) ++static int board_msi_651ml(const char *name) + { +- return it8705f_write_enable(0x2e, name); ++ struct pci_dev *dev; ++ uint16_t base; ++ uint16_t temp; ++ ++ dev = pci_dev_find(0x1039, 0x0962); ++ if (!dev) { ++ fprintf(stderr, "Expected south bridge not found\n"); ++ return 1; ++ } ++ ++ /* Registers 68 and 64 seem like bitmaps */ ++ base = pci_read_word(dev, 0x74); ++ temp = INW(base + 0x68); ++ temp &= ~(1 << 0); /* Make pin output? */ ++ OUTW(temp, base + 0x68); ++ ++ temp = INW(base + 0x64); ++ temp |= (1 << 0); /* Raise output? */ ++ OUTW(temp, base + 0x64); ++ ++ w836xx_memw_enable(0x2E); ++ ++ return 0; + } + + /** +@@ -802,32 +1046,6 @@ static int board_mitac_6513wu(const char *name) + } + + /** +- * Suited for Abit IP35: Intel P35 + ICH9R. +- */ +-static int board_abit_ip35(const char *name) +-{ +- struct pci_dev *dev; +- uint16_t base; +- uint8_t tmp; +- +- dev = pci_dev_find(0x8086, 0x2916); /* Intel ICH9R LPC Interface */ +- if (!dev) { +- fprintf(stderr, "\nERROR: Intel ICH9R LPC not found.\n"); +- return -1; +- } +- +- /* get LPC GPIO base */ +- base = pci_read_long(dev, 0x48) & 0x0000FFC0; +- +- /* Raise GPIO 16 */ +- tmp = INB(base + 0x0E); +- tmp |= 0x01; +- OUTB(tmp, base + 0x0E); +- +- return 0; +-} +- +-/** + * Suited for Asus A7V8X: VIA KT400 + VT8235 + IT8703F-A + */ + static int board_asus_a7v8x(const char *name) +@@ -930,41 +1148,34 @@ static int board_asus_a7v600x(const char *name) + } + + /** +- * Suited for Asus P4P800-E Deluxe: Intel Intel 865PE + ICH5R. +- */ +-static int board_asus_p4p800(const char *name) +-{ +- struct pci_dev *dev; +- uint16_t base; +- uint8_t tmp; +- +- dev = pci_dev_find(0x8086, 0x24D0); /* Intel ICH5R ISA Bridge */ +- if (!dev) { +- fprintf(stderr, "\nERROR: Intel ICH5R ISA Bridge not found.\n"); +- return -1; +- } +- +- /* get PM IO base */ +- base = pci_read_long(dev, 0x58) & 0x0000FFC0; +- +- /* Raise GPIO 21 */ +- tmp = INB(base + 0x0E); +- tmp |= 0x20; +- OUTB(tmp, base + 0x0E); +- +- return 0; +-} +- +-/** ++ * Below is the list of boards which need a special "board enable" code in ++ * flashrom before their ROM chip can be accessed/written to. ++ * ++ * NOTE: Please add boards that _don't_ need such enables or don't work yet ++ * to the respective tables in print.c. Thanks! ++ * + * We use 2 sets of IDs here, you're free to choose which is which. This + * is to provide a very high degree of certainty when matching a board on + * the basis of subsystem/card IDs. As not every vendor handles + * subsystem/card IDs in a sane manner. + * + * Keep the second set NULLed if it should be ignored. Keep the subsystem IDs +- * NULLed if they don't identify the board fully. But please take care to +- * provide an as complete set of pci ids as possible; autodetection is the +- * preferred behaviour and we would like to make sure that matches are unique. ++ * NULLed if they don't identify the board fully and if you can't use DMI. ++ * But please take care to provide an as complete set of pci ids as possible; ++ * autodetection is the preferred behaviour and we would like to make sure that ++ * matches are unique. ++ * ++ * If PCI IDs are not sufficient for board matching, the match can be further ++ * constrained by a string that has to be present in the DMI database for ++ * the baseboard or the system entry. The pattern is matched by case sensitve ++ * substring match, unless it is anchored to the beginning (with a ^ in front) ++ * or the end (with a $ at the end). Both anchors may be specified at the ++ * same time to match the full field. ++ * ++ * When a board is matched through DMI, the first and second main PCI IDs ++ * and the first subsystem PCI ID have to match as well. If you specify the ++ * first subsystem ID as 0x0:0x0, the DMI matching code expects that the ++ * subsystem ID of that device is indeed zero. + * + * The coreboot ids are used two fold. When running with a coreboot firmware, + * the ids uniquely matches the coreboot board identification string. When a +@@ -977,195 +1188,65 @@ static int board_asus_p4p800(const char *name) + + /* Please keep this list alphabetically ordered by vendor/board name. */ + struct board_pciid_enable board_pciid_enables[] = { +- /* first pci-id set [4], second pci-id set [4], coreboot id [2], vendor name board name flash enable */ +- {0x8086, 0x2926, 0x147b, 0x1084, 0x11ab, 0x4364, 0x147b, 0x1084, NULL, NULL, "Abit", "IP35", board_abit_ip35}, +- {0x8086, 0x1130, 0, 0, 0x105a, 0x0d30, 0x105a, 0x4d33, "acorp", "6a815epd", "Acorp", "6A815EPD", board_acorp_6a815epd}, +- {0x1022, 0x746B, 0x1022, 0x36C0, 0, 0, 0, 0, "AGAMI", "ARUMA", "agami", "Aruma", w83627hf_gpio24_raise_2e}, +- {0x1106, 0x3177, 0x17F2, 0x3177, 0x1106, 0x3148, 0x17F2, 0x3148, NULL, NULL, "Albatron", "PM266A*", board_epox_ep_8k5a2}, +- {0x1106, 0x3205, 0x1106, 0x3205, 0x10EC, 0x8139, 0xA0A0, 0x0477, NULL, NULL, "AOpen", "vKM400Am-S", board_aopen_vkm400}, +- {0x1022, 0x2090, 0, 0, 0x1022, 0x2080, 0, 0, "artecgroup", "dbe61", "Artec Group", "DBE61", board_artecgroup_dbe6x}, +- {0x1022, 0x2090, 0, 0, 0x1022, 0x2080, 0, 0, "artecgroup", "dbe62", "Artec Group", "DBE62", board_artecgroup_dbe6x}, +- {0x1106, 0x3189, 0x1043, 0x807F, 0x1106, 0x3065, 0x1043, 0x80ED, NULL, NULL, "ASUS", "A7V600-X", board_asus_a7v600x}, +- {0x1106, 0x3189, 0x1043, 0x807F, 0x1106, 0x3177, 0x1043, 0x808C, NULL, NULL, "ASUS", "A7V8X", board_asus_a7v8x}, +- {0x1106, 0x3177, 0x1043, 0x80A1, 0x1106, 0x3205, 0x1043, 0x8118, NULL, NULL, "ASUS", "A7V8X-MX SE", board_asus_a7v8x_mx}, +- {0x8086, 0x1a30, 0x1043, 0x8070, 0x8086, 0x244b, 0x1043, 0x8028, NULL, NULL, "ASUS", "P4B266", ich2_gpio22_raise}, +- {0x8086, 0x2570, 0x1043, 0x80F2, 0x105A, 0x3373, 0x1043, 0x80F5, NULL, NULL, "ASUS", "P4P800-E Deluxe", board_asus_p4p800}, +- {0x10B9, 0x1541, 0, 0, 0x10B9, 0x1533, 0, 0, "asus", "p5a", "ASUS", "P5A", board_asus_p5a}, +- {0x1106, 0x3149, 0x1565, 0x3206, 0x1106, 0x3344, 0x1565, 0x1202, NULL, NULL, "Biostar", "P4M80-M4", it8705_rom_write_enable}, +- {0x1106, 0x3038, 0x1019, 0x0996, 0x1106, 0x3177, 0x1019, 0x0996, NULL, NULL, "Elitegroup", "K7VTA3", it8705f_write_enable_2e}, +- {0x1106, 0x3177, 0x1106, 0x3177, 0x1106, 0x3059, 0x1695, 0x3005, NULL, NULL, "EPoX", "EP-8K5A2", board_epox_ep_8k5a2}, +- {0x8086, 0x7110, 0, 0, 0x8086, 0x7190, 0, 0, "epox", "ep-bx3", "EPoX", "EP-BX3", board_epox_ep_bx3}, +- {0x1039, 0x0761, 0, 0, 0, 0, 0, 0, "gigabyte", "2761gxdk", "GIGABYTE", "GA-2761GXDK", it87xx_probe_spi_flash}, +- {0x1106, 0x3227, 0x1458, 0x5001, 0x10ec, 0x8139, 0x1458, 0xe000, NULL, NULL, "GIGABYTE", "GA-7VT600", it8705_rom_write_enable}, +- {0x10DE, 0x0050, 0x1458, 0x0C11, 0x10DE, 0x005e, 0x1458, 0x5000, NULL, NULL, "GIGABYTE", "GA-K8N-SLI", board_ga_k8n_sli}, +- {0x10de, 0x0360, 0, 0, 0, 0, 0, 0, "gigabyte", "m57sli", "GIGABYTE", "GA-M57SLI-S4", it87xx_probe_spi_flash}, +- {0x10de, 0x03e0, 0, 0, 0, 0, 0, 0, "gigabyte", "m61p", "GIGABYTE", "GA-M61P-S3", it87xx_probe_spi_flash}, +- {0x1002, 0x4398, 0x1458, 0x5004, 0x1002, 0x4391, 0x1458, 0xb000, NULL, NULL, "GIGABYTE", "GA-MA78G-DS3H", it87xx_probe_spi_flash}, +- {0x1002, 0x4398, 0x1458, 0x5004, 0x1002, 0x4391, 0x1458, 0xb002, NULL, NULL, "GIGABYTE", "GA-MA78GM-S2H", it87xx_probe_spi_flash}, +- /* SB600 LPC, RD790 North. Neither are specific to the GA-MA790FX-DQ6. The coreboot ID is here to be able to trigger the board enable more easily. */ +- {0x1002, 0x438d, 0x1458, 0x5001, 0x1002, 0x5956, 0x1002, 0x5956, "gigabyte", "ma790fx-dq6", "GIGABYTE", "GA-MA790FX-DQ6", it87xx_probe_spi_flash}, +- {0x1166, 0x0223, 0x103c, 0x320d, 0x102b, 0x0522, 0x103c, 0x31fa, "hp", "dl145_g3", "HP", "DL145 G3", board_hp_dl145_g3_enable}, +- {0x1166, 0x0205, 0x1014, 0x0347, 0, 0, 0, 0, "ibm", "x3455", "IBM", "x3455", board_ibm_x3455}, +- {0x1039, 0x5513, 0x8086, 0xd61f, 0x1039, 0x6330, 0x8086, 0xd61f, NULL, NULL, "Intel", "D201GLY", wbsio_check_for_spi}, +- {0x1022, 0x7468, 0, 0, 0, 0, 0, 0, "iwill", "dk8_htx", "IWILL", "DK8-HTX", w83627hf_gpio24_raise_2e}, +- /* Note: There are >= 2 version of the Kontron 986LCD-M/mITX! */ +- {0x8086, 0x27b8, 0, 0, 0, 0, 0, 0, "kontron", "986lcd-m", "Kontron", "986LCD-M", board_kontron_986lcd_m}, +- {0x10ec, 0x8168, 0x10ec, 0x8168, 0x104c, 0x8023, 0x104c, 0x8019, "kontron", "986lcd-m", "Kontron", "986LCD-M", board_kontron_986lcd_m}, +- {0x8086, 0x2411, 0x8086, 0x2411, 0x8086, 0x7125, 0x0e11, 0xb165, NULL, NULL, "Mitac", "6513WU", board_mitac_6513wu}, +- {0x13f6, 0x0111, 0x1462, 0x5900, 0x1106, 0x3177, 0x1106, 0, "msi", "kt4ultra", "MSI", "MS-6590 (KT4 Ultra)",board_msi_kt4v}, +- {0x1106, 0x3149, 0x1462, 0x7094, 0x10ec, 0x8167, 0x1462, 0x094c, NULL, NULL, "MSI", "MS-6702E (K8T Neo2-F)",w83627thf_gpio4_4_raise_2e}, +- {0x1106, 0x0571, 0x1462, 0x7120, 0, 0, 0, 0, "msi", "kt4v", "MSI", "MS-6712 (KT4V)", board_msi_kt4v}, +- {0x8086, 0x2658, 0x1462, 0x7046, 0x1106, 0x3044, 0x1462, 0x046d, NULL, NULL, "MSI", "MS-7046", ich6_gpio19_raise}, +- {0x10de, 0x005e, 0, 0, 0, 0, 0, 0, "msi", "k8n-neo3", "MSI", "MS-7135 (K8N Neo3)", w83627thf_gpio4_4_raise_4e}, +- {0x1106, 0x3104, 0x1297, 0xa238, 0x1106, 0x3059, 0x1297, 0xc063, NULL, NULL, "Shuttle", "AK38N", it8705f_write_enable_2e}, +- {0x1106, 0x3038, 0x0925, 0x1234, 0x1106, 0x3058, 0x15DD, 0x7609, NULL, NULL, "Soyo", "SY-7VCA", board_soyo_sy_7vca}, +- {0x8086, 0x1076, 0x8086, 0x1176, 0x1106, 0x3059, 0x10f1, 0x2498, NULL, NULL, "Tyan", "S2498 (Tomcat K7M)", board_asus_a7v8x_mx}, +- {0x1106, 0x0314, 0x1106, 0xaa08, 0x1106, 0x3227, 0x1106, 0xAA08, NULL, NULL, "VIA", "EPIA-CN", board_via_epia_sp}, +- {0x1106, 0x3177, 0x1106, 0xAA01, 0x1106, 0x3123, 0x1106, 0xAA01, NULL, NULL, "VIA", "EPIA M/MII/...", board_via_epia_m}, +- {0x1106, 0x0259, 0x1106, 0x3227, 0x1106, 0x3065, 0x1106, 0x3149, "via", "epia-n", "VIA", "EPIA-N/NL", board_via_epia_n}, /* TODO: remove coreboot ids */ +- {0x1106, 0x3227, 0x1106, 0xAA01, 0x1106, 0x0259, 0x1106, 0xAA01, NULL, NULL, "VIA", "EPIA SP", board_via_epia_sp}, +- {0x1106, 0x5337, 0x1458, 0xb003, 0x1106, 0x287e, 0x1106, 0x337e, "via", "pc3500g", "VIA", "PC3500G", it87xx_probe_spi_flash}, +- +- { 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL}, /* end marker */ +-}; +- +-/* Please keep this list alphabetically ordered by vendor/board. */ +-const struct board_info boards_ok[] = { +- /* Verified working boards that don't need write-enables. */ +- { "Abit", "AX8", }, +- { "Abit", "Fatal1ty F-I90HD", }, +- { "Advantech", "PCM-5820", }, +- { "ASI", "MB-5BLMP", }, +- { "ASRock", "A770CrossFire", }, +- { "ASUS", "A7N8X Deluxe", }, +- { "ASUS", "A7N8X-E Deluxe", }, +- { "ASUS", "A7V400-MX", }, +- { "ASUS", "A7V8X-MX", }, +- { "ASUS", "A8N-E", }, +- { "ASUS", "A8NE-FM/S", }, +- { "ASUS", "A8N-SLI", }, +- { "ASUS", "A8N-SLI Premium", }, +- { "ASUS", "A8V Deluxe", }, +- { "ASUS", "A8V-E Deluxe", }, +- { "ASUS", "A8V-E SE", }, +- { "ASUS", "M2A-MX", }, +- { "ASUS", "M2A-VM", }, +- { "ASUS", "M2N-E", }, +- { "ASUS", "M2V", }, +- { "ASUS", "P2B", }, +- { "ASUS", "P2B-D", }, +- { "ASUS", "P2B-DS", }, +- { "ASUS", "P2B-F", }, +- { "ASUS", "P2L97-S", }, +- { "ASUS", "P5B-Deluxe", }, +- { "ASUS", "P5KC", }, +- { "ASUS", "P5L-MX", }, +- { "ASUS", "P6T Deluxe V2", }, +- { "A-Trend", "ATC-6220", }, +- { "BCOM", "WinNET100", }, +- { "GIGABYTE", "GA-6BXC", }, +- { "GIGABYTE", "GA-6BXDU", }, +- { "GIGABYTE", "GA-6ZMA", }, +- { "GIGABYTE", "GA-7ZM", }, +- { "GIGABYTE", "GA-EP35-DS3L", }, +- { "GIGABYTE", "GA-EX58-UD4P", }, +- { "GIGABYTE", "GA-MA78GPM-DS2H", }, +- { "GIGABYTE", "GA-MA790GP-DS4H", }, +- { "Intel", "EP80759", }, +- { "Jetway", "J7F4K1G5D-PB", }, +- { "MSI", "MS-6570 (K7N2)", }, +- { "MSI", "MS-7065", }, +- { "MSI", "MS-7168 (Orion)", }, +- { "MSI", "MS-7236 (945PL Neo3)", }, +- { "MSI", "MS-7255 (P4M890M)", }, +- { "MSI", "MS-7345 (P35 Neo2-FIR)", }, +- { "NEC", "PowerMate 2000", }, +- { "PC Engines", "Alix.1c", }, +- { "PC Engines", "Alix.2c2", }, +- { "PC Engines", "Alix.2c3", }, +- { "PC Engines", "Alix.3c3", }, +- { "PC Engines", "Alix.3d3", }, +- { "RCA", "RM4100", }, +- { "Sun", "Blade x6250", }, +- { "Supermicro", "H8QC8", }, +- { "Thomson", "IP1000", }, +- { "TriGem", "Lomita", }, +- { "T-Online", "S-100", }, +- { "Tyan", "iS5375-1U", }, +- { "Tyan", "S1846", }, +- { "Tyan", "S2466", }, +- { "Tyan", "S2881", }, +- { "Tyan", "S2882", }, +- { "Tyan", "S2882-D", }, +- { "Tyan", "S2891", }, +- { "Tyan", "S2892", }, +- { "Tyan", "S2895", }, +- { "Tyan", "S3095", }, +- { "Tyan", "S5180", }, +- { "Tyan", "S5191", }, +- { "Tyan", "S5197", }, +- { "Tyan", "S5211", }, +- { "Tyan", "S5211-1U", }, +- { "Tyan", "S5220", }, +- { "Tyan", "S5375", }, +- { "Tyan", "S5376G2NR/S5376WAG2NR", }, +- { "Tyan", "S5377", }, +- { "Tyan", "S5397", }, +- { "VIA", "EPIA-EX15000G", }, +- { "VIA", "EPIA-LN", }, +- { "VIA", "EPIA-M700", }, +- { "VIA", "EPIA-NX15000G", }, +- { "VIA", "NAB74X0", }, +- { "VIA", "pc2500e", }, +- { "VIA", "VB700X", }, +- +- {}, +-}; +- +-/* Please keep this list alphabetically ordered by vendor/board. */ +-const struct board_info boards_bad[] = { +- /* Verified non-working boards (for now). */ +- { "Abit", "IS-10", }, +- { "ASRock", "K7VT4A+", }, +- { "ASUS", "MEW-AM", }, +- { "ASUS", "MEW-VM", }, +- { "ASUS", "P3B-F", }, +- { "ASUS", "P5B", }, +- { "ASUS", "P5BV-M", }, +- { "Biostar", "M6TBA", }, +- { "Boser", "HS-6637", }, +- { "DFI", "855GME-MGF", }, +- { "FIC", "VA-502", }, +- { "MSI", "MS-6178", }, +- { "MSI", "MS-7260 (K9N Neo)", }, +- { "Soyo", "SY-5VD", }, +- { "Sun", "Fire x4150", }, +- { "Sun", "Fire x4200", }, +- { "Sun", "Fire x4540", }, +- { "Sun", "Fire x4600", }, +- +- {}, +-}; +- +-/* Please keep this list alphabetically ordered by vendor/board. */ +-const struct board_info laptops_ok[] = { +- /* Verified working laptops. */ +- { "Lenovo", "3000 V100 TF05Cxx", }, + +- {}, +-}; +- +-/* Please keep this list alphabetically ordered by vendor/board. */ +-const struct board_info laptops_bad[] = { +- /* Verified non-working laptops (for now). */ +- { "Acer", "Aspire One", }, +- { "ASUS", "Eee PC 701 4G", }, +- { "Dell", "Latitude CPi A366XT", }, +- { "HP/Compaq", "nx9010", }, +- { "IBM/Lenovo", "Thinkpad T40p", }, +- { "IBM/Lenovo", "240", }, +- +- {}, ++ /* first pci-id set [4], second pci-id set [4], dmi identifier coreboot id [2], vendor name board name max_rom_... flash enable */ ++ {0x8086, 0x2926, 0x147b, 0x1084, 0x11ab, 0x4364, 0x147b, 0x1084, NULL, NULL, NULL, "Abit", "IP35", 0, intel_ich_gpio16_raise}, ++ {0x105a, 0x0d30, 0x105a, 0x4d33, 0x8086, 0x1130, 0x8086, 0, NULL, NULL, NULL, "Acorp", "6A815EPD", 0, board_acorp_6a815epd}, ++ {0x8086, 0x24D4, 0x1849, 0x24D0, 0x8086, 0x24D5, 0x1849, 0x9739, NULL, NULL, NULL, "ASRock", "P4i65GV", 0, intel_ich_gpio23_raise}, ++ {0x1022, 0x746B, 0, 0, 0, 0, 0, 0, NULL, "AGAMI", "ARUMA", "agami", "Aruma", 0, w83627hf_gpio24_raise_2e}, ++ {0x1106, 0x3177, 0x17F2, 0x3177, 0x1106, 0x3148, 0x17F2, 0x3148, NULL, NULL, NULL, "Albatron", "PM266A", 0, w836xx_memw_enable_2e}, ++ {0x1106, 0x3205, 0x1106, 0x3205, 0x10EC, 0x8139, 0xA0A0, 0x0477, NULL, NULL, NULL, "AOpen", "vKM400Am-S", 0, it8705f_write_enable_2e}, ++ {0x1022, 0x2090, 0, 0, 0x1022, 0x2080, 0, 0, NULL, "artecgroup", "dbe61", "Artec Group", "DBE61", 0, board_artecgroup_dbe6x}, ++ {0x1022, 0x2090, 0, 0, 0x1022, 0x2080, 0, 0, NULL, "artecgroup", "dbe62", "Artec Group", "DBE62", 0, board_artecgroup_dbe6x}, ++ {0x1106, 0x3189, 0x1043, 0x807F, 0x1106, 0x3065, 0x1043, 0x80ED, NULL, NULL, NULL, "ASUS", "A7V600-X", 0, board_asus_a7v600x}, ++ {0x1106, 0x3189, 0x1043, 0x807F, 0x1106, 0x3177, 0x1043, 0x808C, NULL, NULL, NULL, "ASUS", "A7V8X", 0, board_asus_a7v8x}, ++ {0x1106, 0x3177, 0x1043, 0x80A1, 0x1106, 0x3205, 0x1043, 0x8118, NULL, NULL, NULL, "ASUS", "A7V8X-MX SE", 0, w836xx_memw_enable_2e}, ++ {0x1106, 0x1336, 0x1043, 0x80ed, 0x1106, 0x3288, 0x1043, 0x8249, NULL, NULL, NULL, "ASUS", "M2V-MX", 0, via_vt823x_gpio5_raise}, ++ {0x8086, 0x1a30, 0x1043, 0x8070, 0x8086, 0x244b, 0x1043, 0x8028, NULL, NULL, NULL, "ASUS", "P4B266", 0, intel_ich_gpio22_raise}, ++ {0x8086, 0x1A30, 0x1043, 0x8025, 0x8086, 0x244B, 0x104D, 0x80F0, NULL, NULL, NULL, "ASUS", "P4B266-LM", 0, intel_ich_gpio21_raise}, ++ {0x8086, 0x2570, 0x1043, 0x80F2, 0x105A, 0x3373, 0x1043, 0x80F5, NULL, NULL, NULL, "ASUS", "P4P800-E Deluxe", 0, intel_ich_gpio21_raise}, ++ {0x10B9, 0x1541, 0, 0, 0x10B9, 0x1533, 0, 0, "^P5A$", "asus", "p5a", "ASUS", "P5A", 0, board_asus_p5a}, ++ {0x10DE, 0x0030, 0x1043, 0x818a, 0x8086, 0x100E, 0x1043, 0x80EE, NULL, NULL, NULL, "ASUS", "P5ND2-SLI Deluxe", 0, nvidia_mcp_gpio10_raise}, ++ {0x1106, 0x3149, 0x1565, 0x3206, 0x1106, 0x3344, 0x1565, 0x1202, NULL, NULL, NULL, "Biostar", "P4M80-M4", 0, it8705f_write_enable_2e}, ++ {0x8086, 0x3590, 0x1028, 0x016c, 0x1000, 0x0030, 0x1028, 0x016c, NULL, NULL, NULL, "Dell", "PowerEdge 1850", 0, intel_ich_gpio23_raise}, ++ {0x1039, 0x5513, 0x1019, 0x0A41, 0x1039, 0x0018, 0, 0, NULL, NULL, NULL, "Elitegroup", "K7S6A", 0, it8705f_write_enable_2e}, ++ {0x1106, 0x3038, 0x1019, 0x0996, 0x1106, 0x3177, 0x1019, 0x0996, NULL, NULL, NULL, "Elitegroup", "K7VTA3", 256, it8705f_write_enable_2e}, ++ {0x1106, 0x3177, 0x1106, 0x3177, 0x1106, 0x3059, 0x1695, 0x3005, NULL, NULL, NULL, "EPoX", "EP-8K5A2", 0, w836xx_memw_enable_2e}, ++ {0x10EC, 0x8139, 0x1695, 0x9001, 0x11C1, 0x5811, 0x1695, 0x9015, NULL, NULL, NULL, "EPoX", "EP-8RDA3+", 0, nvidia_mcp_gpio31_raise}, ++ {0x8086, 0x7110, 0, 0, 0x8086, 0x7190, 0, 0, NULL, "epox", "ep-bx3", "EPoX", "EP-BX3", 0, board_epox_ep_bx3}, ++ {0x1106, 0x0686, 0x1106, 0x0686, 0x1106, 0x3058, 0x1458, 0xa000, NULL, NULL, NULL, "GIGABYTE", "GA-7ZM", 512, NULL}, ++ {0x1039, 0x0761, 0, 0, 0x10EC, 0x8168, 0, 0, NULL, "gigabyte", "2761gxdk", "GIGABYTE", "GA-2761GXDK", 0, it87xx_probe_spi_flash}, ++ {0x1106, 0x3227, 0x1458, 0x5001, 0x10ec, 0x8139, 0x1458, 0xe000, NULL, NULL, NULL, "GIGABYTE", "GA-7VT600", 0, it8705f_write_enable_2e}, ++ {0x10DE, 0x0050, 0x1458, 0x0C11, 0x10DE, 0x005e, 0x1458, 0x5000, NULL, NULL, NULL, "GIGABYTE", "GA-K8N-SLI", 0, nvidia_mcp_gpio21_raise}, ++ {0x10DE, 0x0360, 0x1458, 0x0C11, 0x10DE, 0x0369, 0x1458, 0x5001, NULL, "gigabyte", "m57sli", "GIGABYTE", "GA-M57SLI-S4", 0, it87xx_probe_spi_flash}, ++ {0x10de, 0x03e0, 0, 0, 0x10DE, 0x03D0, 0, 0, NULL, NULL, NULL, "GIGABYTE", "GA-M61P-S3", 0, it87xx_probe_spi_flash}, ++ {0x1002, 0x4398, 0x1458, 0x5004, 0x1002, 0x4391, 0x1458, 0xb000, NULL, NULL, NULL, "GIGABYTE", "GA-MA78G-DS3H", 0, it87xx_probe_spi_flash}, ++ {0x1002, 0x4398, 0x1458, 0x5004, 0x1002, 0x4391, 0x1458, 0xb002, NULL, NULL, NULL, "GIGABYTE", "GA-MA78GM-S2H", 0, it87xx_probe_spi_flash}, ++ {0x1002, 0x438d, 0x1458, 0x5001, 0x1002, 0x5956, 0x1002, 0x5956, NULL, NULL, NULL, "GIGABYTE", "GA-MA790FX-DQ6", 0, it87xx_probe_spi_flash}, ++ {0x1166, 0x0223, 0x103c, 0x320d, 0x102b, 0x0522, 0x103c, 0x31fa, NULL, "hp", "dl145_g3", "HP", "DL145 G3", 0, board_hp_dl145_g3_enable}, ++ {0x1166, 0x0205, 0x1014, 0x0347, 0x1002, 0x515E, 0x1014, 0x0325, NULL, NULL, NULL, "IBM", "x3455", 0, board_ibm_x3455}, ++ {0x1039, 0x5513, 0x8086, 0xd61f, 0x1039, 0x6330, 0x8086, 0xd61f, NULL, NULL, NULL, "Intel", "D201GLY", 0, wbsio_check_for_spi}, ++ {0x1022, 0x7468, 0, 0, 0, 0, 0, 0, NULL, "iwill", "dk8_htx", "IWILL", "DK8-HTX", 0, w83627hf_gpio24_raise_2e}, ++ {0x8086, 0x27A0, 0, 0, 0x8086, 0x27b8, 0, 0, NULL, "kontron", "986lcd-m", "Kontron", "986LCD-M", 0, board_kontron_986lcd_m}, ++ {0x8086, 0x2411, 0x8086, 0x2411, 0x8086, 0x7125, 0x0e11, 0xb165, NULL, NULL, NULL, "Mitac", "6513WU", 0, board_mitac_6513wu}, ++ {0x13f6, 0x0111, 0x1462, 0x5900, 0x1106, 0x3177, 0x1106, 0, NULL, NULL, NULL, "MSI", "MS-6590 (KT4 Ultra)", 0, board_msi_kt4v}, ++ {0x1106, 0x3149, 0x1462, 0x7094, 0x10ec, 0x8167, 0x1462, 0x094c, NULL, NULL, NULL, "MSI", "MS-6702E (K8T Neo2-F)", 0, w83627thf_gpio4_4_raise_2e}, ++ {0x1106, 0x0571, 0x1462, 0x7120, 0x1106, 0x3065, 0x1462, 0x7120, NULL, NULL, NULL, "MSI", "MS-6712 (KT4V)", 0, board_msi_kt4v}, ++ {0x1039, 0x7012, 0x1462, 0x0050, 0x1039, 0x6325, 0x1462, 0x0058, NULL, NULL, NULL, "MSI", "MS-7005 (651M-L)", 0, board_msi_651ml}, ++ {0x8086, 0x2658, 0x1462, 0x7046, 0x1106, 0x3044, 0x1462, 0x046d, NULL, NULL, NULL, "MSI", "MS-7046", 0, intel_ich_gpio19_raise}, ++ {0x10DE, 0x005E, 0x1462, 0x7135, 0x10DE, 0x0050, 0x1462, 0x7135, NULL, "msi", "k8n-neo3", "MSI", "MS-7135 (K8N Neo3)", 0, w83627thf_gpio4_4_raise_4e}, ++ {0x10DE, 0x005E, 0x1462, 0x7125, 0x10DE, 0x0052, 0x1462, 0x7125, NULL, NULL, NULL, "MSI", "K8N Neo4-F", 0, nvidia_mcp_gpio2_raise}, ++ {0x1106, 0x3099, 0, 0, 0x1106, 0x3074, 0, 0, NULL, "shuttle", "ak31", "Shuttle", "AK31", 0, w836xx_memw_enable_2e}, ++ {0x1106, 0x3104, 0x1297, 0xa238, 0x1106, 0x3059, 0x1297, 0xc063, NULL, NULL, NULL, "Shuttle", "AK38N", 256, it8705f_write_enable_2e}, ++ {0x10DE, 0x0050, 0x1297, 0x5036, 0x1412, 0x1724, 0x1297, 0x5036, NULL, NULL, NULL, "Shuttle", "FN25", 0, board_shuttle_fn25}, ++ {0x1106, 0x3038, 0x0925, 0x1234, 0x1106, 0x3058, 0x15DD, 0x7609, NULL, NULL, NULL, "Soyo", "SY-7VCA", 0, board_soyo_sy_7vca}, ++ {0x8086, 0x1076, 0x8086, 0x1176, 0x1106, 0x3059, 0x10f1, 0x2498, NULL, NULL, NULL, "Tyan", "S2498 (Tomcat K7M)", 0, w836xx_memw_enable_2e}, ++ {0x1106, 0x3038, 0x0925, 0x1234, 0x1106, 0x0596, 0x1106, 0, NULL, NULL, NULL, "Tekram", "P6Pro-A5", 256, NULL}, ++ {0x1106, 0x3177, 0x1106, 0xAA01, 0x1106, 0x3123, 0x1106, 0xAA01, NULL, NULL, NULL, "VIA", "EPIA M/MII/...", 0, via_vt823x_gpio15_raise}, ++ {0x1106, 0x0259, 0x1106, 0x3227, 0x1106, 0x3065, 0x1106, 0x3149, NULL, NULL, NULL, "VIA", "EPIA-N/NL", 0, via_vt823x_gpio9_raise}, ++ {0x1106, 0x5337, 0x1458, 0xb003, 0x1106, 0x287e, 0x1106, 0x337e, NULL, NULL, NULL, "VIA", "PC3500G", 0, it87xx_probe_spi_flash}, ++ ++ { 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL}, /* end marker */ + }; + + /** +@@ -1230,7 +1311,8 @@ static struct board_pciid_enable *board_match_pci_card_ids(void) + struct board_pciid_enable *board = board_pciid_enables; + + for (; board->vendor_name; board++) { +- if (!board->first_card_vendor || !board->first_card_device) ++ if ((!board->first_card_vendor || !board->first_card_device) && ++ !board->dmi_pattern) + continue; + + if (!pci_card_find(board->first_vendor, board->first_device, +@@ -1252,6 +1334,18 @@ static struct board_pciid_enable *board_match_pci_card_ids(void) + } + } + ++ if (board->dmi_pattern) { ++ if (!has_dmi_support) { ++ fprintf(stderr, "WARNING: Can't autodetect %s %s," ++ " DMI info unavailable.\n", ++ board->vendor_name, board->board_name); ++ continue; ++ } else { ++ if (!dmi_match(board->dmi_pattern)) ++ continue; ++ } ++ } ++ + return board; + } + +@@ -1270,14 +1364,21 @@ int board_flash_enable(const char *vendor, const char *part) + board = board_match_pci_card_ids(); + + if (board) { +- printf("Disabling flash write protection for board \"%s %s\"... ", +- board->vendor_name, board->board_name); +- +- ret = board->enable(board->vendor_name); +- if (ret) +- printf("FAILED!\n"); +- else +- printf("OK.\n"); ++ if (board->max_rom_decode_parallel) ++ max_rom_decode.parallel = ++ board->max_rom_decode_parallel * 1024; ++ ++ if (board->enable != NULL) { ++ printf("Disabling flash write protection for " ++ "board \"%s %s\"... ", board->vendor_name, ++ board->board_name); ++ ++ ret = board->enable(board->vendor_name); ++ if (ret) ++ printf("FAILED!\n"); ++ else ++ printf("OK.\n"); ++ } + } + + return ret; +diff --git a/buspirate_spi.c b/buspirate_spi.c +new file mode 100644 +index 0000000..ecdd689 +--- /dev/null ++++ b/buspirate_spi.c +@@ -0,0 +1,318 @@ ++/* ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2009 Carl-Daniel Hailfinger ++ * ++ * 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; version 2 of the License. ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "flash.h" ++#include "spi.h" ++ ++/* Change this to #define if you want to test without a serial implementation */ ++#undef FAKE_COMMUNICATION ++ ++#ifndef FAKE_COMMUNICATION ++int buspirate_serialport_setup(char *dev) ++{ ++ /* 115200bps, 8 databits, no parity, 1 stopbit */ ++ sp_fd = sp_openserport(dev, 115200); ++ return 0; ++} ++#else ++#define buspirate_serialport_setup(...) 0 ++#define serialport_shutdown(...) 0 ++#define serialport_write(...) 0 ++#define serialport_read(...) 0 ++#define sp_flush_incoming(...) 0 ++#endif ++ ++int buspirate_sendrecv(unsigned char *buf, unsigned int writecnt, unsigned int readcnt) ++{ ++ int i, ret = 0; ++ ++ msg_pspew("%s: write %i, read %i ", __func__, writecnt, readcnt); ++ if (!writecnt && !readcnt) { ++ msg_perr("Zero length command!\n"); ++ return 1; ++ } ++ msg_pspew("Sending"); ++ for (i = 0; i < writecnt; i++) ++ msg_pspew(" 0x%02x", buf[i]); ++#ifdef FAKE_COMMUNICATION ++ /* Placate the caller for now. */ ++ if (readcnt) { ++ buf[0] = 0x01; ++ memset(buf + 1, 0xff, readcnt - 1); ++ } ++ ret = 0; ++#else ++ if (writecnt) ++ ret = serialport_write(buf, writecnt); ++ if (ret) ++ return ret; ++ if (readcnt) ++ ret = serialport_read(buf, readcnt); ++ if (ret) ++ return ret; ++#endif ++ msg_pspew(", receiving"); ++ for (i = 0; i < readcnt; i++) ++ msg_pspew(" 0x%02x", buf[i]); ++ msg_pspew("\n"); ++ return 0; ++} ++ ++static const struct buspirate_spispeeds spispeeds[] = { ++ {"30k", 0x0}, ++ {"125k", 0x1}, ++ {"250k", 0x2}, ++ {"1M", 0x3}, ++ {"2M", 0x4}, ++ {"2.6M", 0x5}, ++ {"4M", 0x6}, ++ {"8M", 0x7}, ++ {NULL, 0x0} ++}; ++ ++int buspirate_spi_init(void) ++{ ++ unsigned char buf[512]; ++ int ret = 0; ++ int i; ++ char *dev = NULL; ++ char *speed = NULL; ++ int spispeed = 0x7; ++ ++ if (programmer_param && !strlen(programmer_param)) { ++ free(programmer_param); ++ programmer_param = NULL; ++ } ++ if (programmer_param) { ++ dev = extract_param(&programmer_param, "dev=", ",:"); ++ speed = extract_param(&programmer_param, "spispeed=", ",:"); ++ if (strlen(programmer_param)) ++ msg_perr("Unhandled programmer parameters: %s\n", ++ programmer_param); ++ free(programmer_param); ++ programmer_param = NULL; ++ } ++ if (!dev) { ++ msg_perr("No serial device given. Use flashrom -p " ++ "buspiratespi:dev=/dev/ttyUSB0\n"); ++ return 1; ++ } ++ if (speed) { ++ for (i = 0; spispeeds[i].name; i++) ++ if (!strncasecmp(spispeeds[i].name, speed, ++ strlen(spispeeds[i].name))) { ++ spispeed = spispeeds[i].speed; ++ break; ++ } ++ if (!spispeeds[i].name) ++ msg_perr("Invalid SPI speed, using default.\n"); ++ } ++ /* This works because speeds numbering starts at 0 and is contiguous. */ ++ msg_pdbg("SPI speed is %sHz\n", spispeeds[spispeed].name); ++ ++ ret = buspirate_serialport_setup(dev); ++ if (ret) ++ return ret; ++ ++ /* This is the brute force version, but it should work. */ ++ for (i = 0; i < 19; i++) { ++ /* Enter raw bitbang mode */ ++ buf[0] = 0x00; ++ /* Send the command, don't read the response. */ ++ ret = buspirate_sendrecv(buf, 1, 0); ++ if (ret) ++ return ret; ++ /* Read any response and discard it. */ ++ sp_flush_incoming(); ++ } ++ /* Enter raw bitbang mode */ ++ buf[0] = 0x00; ++ ret = buspirate_sendrecv(buf, 1, 5); ++ if (ret) ++ return ret; ++ if (memcmp(buf, "BBIO", 4)) { ++ msg_perr("Entering raw bitbang mode failed!\n"); ++ return 1; ++ } ++ msg_pdbg("Raw bitbang mode version %c\n", buf[4]); ++ if (buf[4] != '1') { ++ msg_perr("Can't handle raw bitbang mode version %c!\n", ++ buf[4]); ++ return 1; ++ } ++ /* Enter raw SPI mode */ ++ buf[0] = 0x01; ++ ret = buspirate_sendrecv(buf, 1, 4); ++ if (memcmp(buf, "SPI", 3)) { ++ msg_perr("Entering raw SPI mode failed!\n"); ++ return 1; ++ } ++ msg_pdbg("Raw SPI mode version %c\n", buf[3]); ++ if (buf[3] != '1') { ++ msg_perr("Can't handle raw SPI mode version %c!\n", ++ buf[3]); ++ return 1; ++ } ++ ++ /* Initial setup (SPI peripherals config): Enable power, CS high, AUX */ ++ buf[0] = 0x40 | 0xb; ++ ret = buspirate_sendrecv(buf, 1, 1); ++ if (ret) ++ return 1; ++ if (buf[0] != 0x01) { ++ msg_perr("Protocol error while setting power/CS/AUX!\n"); ++ return 1; ++ } ++ ++ /* Set SPI speed */ ++ buf[0] = 0x60 | spispeed; ++ ret = buspirate_sendrecv(buf, 1, 1); ++ if (ret) ++ return 1; ++ if (buf[0] != 0x01) { ++ msg_perr("Protocol error while setting SPI speed!\n"); ++ return 1; ++ } ++ ++ /* Set SPI config: output type, idle, clock edge, sample */ ++ buf[0] = 0x80 | 0xa; ++ ret = buspirate_sendrecv(buf, 1, 1); ++ if (ret) ++ return 1; ++ if (buf[0] != 0x01) { ++ msg_perr("Protocol error while setting SPI config!\n"); ++ return 1; ++ } ++ ++ /* De-assert CS# */ ++ buf[0] = 0x03; ++ ret = buspirate_sendrecv(buf, 1, 1); ++ if (ret) ++ return 1; ++ if (buf[0] != 0x01) { ++ msg_perr("Protocol error while raising CS#!\n"); ++ return 1; ++ } ++ ++ buses_supported = CHIP_BUSTYPE_SPI; ++ spi_controller = SPI_CONTROLLER_BUSPIRATE; ++ ++ return 0; ++} ++ ++int buspirate_spi_shutdown(void) ++{ ++ unsigned char buf[5]; ++ int ret = 0; ++ ++ /* Exit raw SPI mode (enter raw bitbang mode) */ ++ buf[0] = 0x00; ++ ret = buspirate_sendrecv(buf, 1, 5); ++ if (ret) ++ return ret; ++ if (memcmp(buf, "BBIO", 4)) { ++ msg_perr("Entering raw bitbang mode failed!\n"); ++ return 1; ++ } ++ msg_pdbg("Raw bitbang mode version %c\n", buf[4]); ++ if (buf[4] != '1') { ++ msg_perr("Can't handle raw bitbang mode version %c!\n", ++ buf[4]); ++ return 1; ++ } ++ /* Reset Bus Pirate (return to user terminal) */ ++ buf[0] = 0x0f; ++ ret = buspirate_sendrecv(buf, 1, 0); ++ if (ret) ++ return ret; ++ ++ /* Shut down serial port communication */ ++ ret = serialport_shutdown(); ++ if (ret) ++ return ret; ++ msg_pdbg("Bus Pirate shutdown completed.\n"); ++ ++ return 0; ++} ++ ++int buspirate_spi_send_command(unsigned int writecnt, unsigned int readcnt, ++ const unsigned char *writearr, unsigned char *readarr) ++{ ++ static unsigned char *buf = NULL; ++ int i = 0, ret = 0; ++ ++ if (writecnt > 16 || readcnt > 16 || (readcnt + writecnt) > 16) ++ return SPI_INVALID_LENGTH; ++ ++ /* +2 is pretty arbitrary. */ ++ buf = realloc(buf, writecnt + readcnt + 2); ++ if (!buf) { ++ msg_perr("Out of memory!\n"); ++ exit(1); // -1 ++ } ++ ++ /* Assert CS# */ ++ buf[i++] = 0x02; ++ ret = buspirate_sendrecv(buf, 1, 1); ++ if (ret) ++ return SPI_GENERIC_ERROR; ++ if (buf[0] != 0x01) { ++ msg_perr("Protocol error while lowering CS#!\n"); ++ return SPI_GENERIC_ERROR; ++ } ++ ++ i = 0; ++ buf[i++] = 0x10 | (writecnt + readcnt - 1); ++ memcpy(buf + i, writearr, writecnt); ++ i += writecnt; ++ memset(buf + i, 0, readcnt); ++ ret = buspirate_sendrecv(buf, i + readcnt, i + readcnt); ++ if (ret) ++ return SPI_GENERIC_ERROR; ++ if (buf[0] != 0x01) { ++ msg_perr("Protocol error while reading/writing SPI!\n"); ++ return SPI_GENERIC_ERROR; ++ } ++ memcpy(readarr, buf + i, readcnt); ++ ++ i = 0; ++ /* De-assert CS# */ ++ buf[i++] = 0x03; ++ ret = buspirate_sendrecv(buf, 1, 1); ++ if (ret) ++ return SPI_GENERIC_ERROR; ++ if (buf[0] != 0x01) { ++ msg_perr("Protocol error while raising CS#!\n"); ++ return SPI_GENERIC_ERROR; ++ } ++ ++ return ret; ++} ++ ++int buspirate_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len) ++{ ++ return spi_read_chunked(flash, buf, start, len, 12); ++} ++ ++/* We could do 12-byte writes, but for now we use the generic 1-byte code. */ +diff --git a/cbtable.c b/cbtable.c +index 9d7e758..c12354c 100644 +--- a/cbtable.c ++++ b/cbtable.c +@@ -6,6 +6,7 @@ + * (Written by Eric Biederman for Linux Networx) + * Copyright (C) 2006-2009 coresystems GmbH + * (Written by Stefan Reinauer for coresystems GmbH) ++ * Copyright (C) 2010 Carl-Daniel Hailfinger + * + * 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 +@@ -30,13 +31,27 @@ + char *lb_part = NULL, *lb_vendor = NULL; + int partvendor_from_cbtable = 0; + ++void lb_vendor_dev_from_string(char *boardstring) ++{ ++ char *tempstr2 = NULL; ++ strtok(boardstring, ":"); ++ tempstr2 = strtok(NULL, ":"); ++ if (tempstr2) { ++ lb_vendor = boardstring; ++ lb_part = tempstr2; ++ } else { ++ lb_vendor = NULL; ++ lb_part = boardstring; ++ } ++} ++ + static unsigned long compute_checksum(void *addr, unsigned long length) + { + uint8_t *ptr; + volatile union { + uint8_t byte[2]; + uint16_t word; +- } value; ++ } chksum; + unsigned long sum; + unsigned long i; + +@@ -58,10 +73,10 @@ static unsigned long compute_checksum(void *addr, unsigned long length) + sum = (sum + (sum >> 16)) & 0xFFFF; + } + } +- value.byte[0] = sum & 0xff; +- value.byte[1] = (sum >> 8) & 0xff; ++ chksum.byte[0] = sum & 0xff; ++ chksum.byte[1] = (sum >> 8) & 0xff; + +- return (~value.word) & 0xFFFF; ++ return (~chksum.word) & 0xFFFF; + } + + #define for_each_lbrec(head, rec) \ +@@ -193,11 +208,15 @@ int coreboot_init(void) + #else + start = 0x0; + #endif +- table_area = physmap("low megabyte", start, BYTES_TO_MAP); ++ table_area = physmap_try_ro("low megabyte", start, BYTES_TO_MAP - start); ++ if (!table_area) { ++ msg_perr("Failed getting access to coreboot low tables.\n"); ++ return -1; ++ } + + lb_table = find_lb_table(table_area, 0x00000, 0x1000); + if (!lb_table) +- lb_table = find_lb_table(table_area, 0xf0000, BYTES_TO_MAP); ++ lb_table = find_lb_table(table_area, 0xf0000 - start, BYTES_TO_MAP - start); + if (lb_table) { + struct lb_forward *forward = (struct lb_forward *) + (((char *)lb_table) + lb_table->header_bytes); +@@ -205,7 +224,12 @@ int coreboot_init(void) + start = forward->forward; + start &= ~(getpagesize() - 1); + physunmap(table_area, BYTES_TO_MAP); +- table_area = physmap("high tables", start, BYTES_TO_MAP); ++ table_area = physmap_try_ro("high tables", start, BYTES_TO_MAP); ++ if (!table_area) { ++ msg_perr("Failed getting access to coreboot " ++ "high tables.\n"); ++ return -1; ++ } + lb_table = find_lb_table(table_area, 0x00000, 0x1000); + } + } +diff --git a/chipdrivers.h b/chipdrivers.h +new file mode 100644 +index 0000000..c5062ca +--- /dev/null ++++ b/chipdrivers.h +@@ -0,0 +1,180 @@ ++/* ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2009 Carl-Daniel Hailfinger ++ * ++ * 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; version 2 of the License. ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * ++ * Header file for flash chip drivers. Included from flash.h. ++ * As a general rule, every function listed here should take a pointer to ++ * struct flashchip as first parameter. ++ */ ++ ++#ifndef __CHIPDRIVERS_H__ ++#define __CHIPDRIVERS_H__ 1 ++ ++/* spi.c, should probably be in spi_chip.c */ ++int probe_spi_rdid(struct flashchip *flash); ++int probe_spi_rdid4(struct flashchip *flash); ++int probe_spi_rems(struct flashchip *flash); ++int probe_spi_res(struct flashchip *flash); ++int spi_write_enable(void); ++int spi_write_disable(void); ++int spi_chip_erase_60(struct flashchip *flash); ++int spi_chip_erase_c7(struct flashchip *flash); ++int spi_chip_erase_60_c7(struct flashchip *flash); ++int spi_chip_erase_d8(struct flashchip *flash); ++int spi_block_erase_20(struct flashchip *flash, unsigned int addr, unsigned int blocklen); ++int spi_block_erase_52(struct flashchip *flash, unsigned int addr, unsigned int blocklen); ++int spi_block_erase_d7(struct flashchip *flash, unsigned int addr, unsigned int blocklen); ++int spi_block_erase_d8(struct flashchip *flash, unsigned int addr, unsigned int blocklen); ++int spi_block_erase_60(struct flashchip *flash, unsigned int addr, unsigned int blocklen); ++int spi_block_erase_c7(struct flashchip *flash, unsigned int addr, unsigned int blocklen); ++int spi_chip_write_1(struct flashchip *flash, uint8_t *buf); ++int spi_chip_write_256(struct flashchip *flash, uint8_t *buf); ++int spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len); ++uint8_t spi_read_status_register(void); ++int spi_disable_blockprotect(void); ++int spi_byte_program(int addr, uint8_t databyte); ++int spi_nbyte_program(int addr, uint8_t *bytes, int len); ++int spi_nbyte_read(int addr, uint8_t *bytes, int len); ++int spi_read_chunked(struct flashchip *flash, uint8_t *buf, int start, int len, int chunksize); ++int spi_aai_write(struct flashchip *flash, uint8_t *buf); ++ ++/* 82802ab.c */ ++uint8_t wait_82802ab(chipaddr bios); ++int probe_82802ab(struct flashchip *flash); ++int erase_82802ab(struct flashchip *flash); ++int erase_82802ab_block(struct flashchip *flash, unsigned int page, unsigned int pagesize); ++int write_82802ab(struct flashchip *flash, uint8_t *buf); ++ ++/* am29f040b.c */ ++int probe_29f040b(struct flashchip *flash); ++int erase_29f040b(struct flashchip *flash); ++int erase_sector_29f040b(struct flashchip *flash, unsigned int blockaddr, unsigned int blocksize); ++int erase_chip_29f040b(struct flashchip *flash, unsigned int blockaddr, unsigned int blocksize); ++int write_29f040b(struct flashchip *flash, uint8_t *buf); ++ ++/* pm29f002.c */ ++int write_pm29f002(struct flashchip *flash, uint8_t *buf); ++ ++/* en29f002a.c */ ++int probe_en29f002a(struct flashchip *flash); ++int erase_en29f002a(struct flashchip *flash); ++int write_en29f002a(struct flashchip *flash, uint8_t *buf); ++ ++/* jedec.c */ ++uint8_t oddparity(uint8_t val); ++void toggle_ready_jedec(chipaddr dst); ++void data_polling_jedec(chipaddr dst, uint8_t data); ++int write_byte_program_jedec(chipaddr bios, uint8_t *src, ++ chipaddr dst); ++int probe_jedec(struct flashchip *flash); ++int erase_chip_jedec(struct flashchip *flash); ++int write_jedec(struct flashchip *flash, uint8_t *buf); ++int write_jedec_1(struct flashchip *flash, uint8_t *buf); ++int erase_sector_jedec(struct flashchip *flash, unsigned int page, unsigned int pagesize); ++int erase_block_jedec(struct flashchip *flash, unsigned int page, unsigned int blocksize); ++int erase_chip_block_jedec(struct flashchip *flash, unsigned int page, unsigned int blocksize); ++int write_sector_jedec_common(struct flashchip *flash, uint8_t *src, chipaddr dst, unsigned int page_size, unsigned int mask); ++ ++/* m29f002.c */ ++int erase_m29f002(struct flashchip *flash); ++int write_m29f002t(struct flashchip *flash, uint8_t *buf); ++int write_m29f002b(struct flashchip *flash, uint8_t *buf); ++ ++/* m29f400bt.c */ ++int probe_m29f400bt(struct flashchip *flash); ++int erase_m29f400bt(struct flashchip *flash); ++int block_erase_m29f400bt(struct flashchip *flash, unsigned int start, unsigned int len); ++int block_erase_chip_m29f400bt(struct flashchip *flash, unsigned int start, unsigned int len); ++int write_m29f400bt(struct flashchip *flash, uint8_t *buf); ++int write_coreboot_m29f400bt(struct flashchip *flash, uint8_t *buf); ++void protect_m29f400bt(chipaddr bios); ++void write_page_m29f400bt(chipaddr bios, uint8_t *src, ++ chipaddr dst, int page_size); ++ ++/* mx29f002.c */ ++int probe_29f002(struct flashchip *flash); ++int erase_29f002(struct flashchip *flash); ++int erase_chip_29f002(struct flashchip *flash, unsigned int addr, unsigned int blocklen); ++int erase_sector_29f002(struct flashchip *flash, unsigned int address, unsigned int blocklen); ++int write_29f002(struct flashchip *flash, uint8_t *buf); ++ ++/* pm49fl00x.c */ ++int probe_49fl00x(struct flashchip *flash); ++int erase_49fl00x(struct flashchip *flash); ++int write_49fl00x(struct flashchip *flash, uint8_t *buf); ++ ++/* sharplhf00l04.c */ ++int probe_lhf00l04(struct flashchip *flash); ++int erase_lhf00l04_block(struct flashchip *flash, unsigned int blockaddr, unsigned int blocklen); ++int write_lhf00l04(struct flashchip *flash, uint8_t *buf); ++void protect_lhf00l04(chipaddr bios); ++ ++/* sst28sf040.c */ ++int probe_28sf040(struct flashchip *flash); ++int erase_chip_28sf040(struct flashchip *flash, unsigned int addr, unsigned int blocklen); ++int erase_sector_28sf040(struct flashchip *flash, unsigned int address, unsigned int sector_size); ++int write_28sf040(struct flashchip *flash, uint8_t *buf); ++ ++/* sst39sf020.c */ ++int probe_39sf020(struct flashchip *flash); ++int write_39sf020(struct flashchip *flash, uint8_t *buf); ++ ++/* sst49lf040.c */ ++int erase_49lf040(struct flashchip *flash); ++int write_49lf040(struct flashchip *flash, uint8_t *buf); ++ ++/* sst49lfxxxc.c */ ++int probe_49lfxxxc(struct flashchip *flash); ++int erase_49lfxxxc(struct flashchip *flash); ++int erase_sector_49lfxxxc(struct flashchip *flash, unsigned int address, unsigned int sector_size); ++int erase_block_49lfxxxc(struct flashchip *flash, unsigned int address, unsigned int sector_size); ++int erase_chip_49lfxxxc(struct flashchip *flash, unsigned int addr, unsigned int blocksize); ++int write_49lfxxxc(struct flashchip *flash, uint8_t *buf); ++ ++/* sst_fwhub.c */ ++int probe_sst_fwhub(struct flashchip *flash); ++int erase_sst_fwhub(struct flashchip *flash); ++int erase_sst_fwhub_block(struct flashchip *flash, unsigned int offset, unsigned int page_size); ++int erase_sst_fwhub_sector(struct flashchip *flash, unsigned int offset, unsigned int page_size); ++int write_sst_fwhub(struct flashchip *flash, uint8_t *buf); ++ ++/* w39v040c.c */ ++int probe_w39v040c(struct flashchip *flash); ++int erase_w39v040c(struct flashchip *flash); ++int write_w39v040c(struct flashchip *flash, uint8_t *buf); ++ ++/* w39V080fa.c */ ++int probe_winbond_fwhub(struct flashchip *flash); ++int erase_winbond_fwhub(struct flashchip *flash); ++int write_winbond_fwhub(struct flashchip *flash, uint8_t *buf); ++ ++/* w29ee011.c */ ++int probe_w29ee011(struct flashchip *flash); ++ ++/* w49f002u.c */ ++int write_49f002(struct flashchip *flash, uint8_t *buf); ++ ++/* stm50flw0x0x.c */ ++int probe_stm50flw0x0x(struct flashchip *flash); ++int erase_stm50flw0x0x(struct flashchip *flash); ++int erase_block_stm50flw0x0x(struct flashchip *flash, unsigned int block, unsigned int blocksize); ++int erase_sector_stm50flw0x0x(struct flashchip *flash, unsigned int block, unsigned int blocksize); ++int erase_chip_stm50flw0x0x(struct flashchip *flash, unsigned int addr, unsigned int blocklen); ++int write_stm50flw0x0x(struct flashchip *flash, uint8_t *buf); ++ ++#endif /* !__CHIPDRIVERS_H__ */ +diff --git a/chipset_enable.c b/chipset_enable.c +index 8dcaf0b..50e62b1 100644 +--- a/chipset_enable.c ++++ b/chipset_enable.c +@@ -4,6 +4,8 @@ + * Copyright (C) 2000 Silicon Integrated System Corporation + * Copyright (C) 2005-2009 coresystems GmbH + * Copyright (C) 2006 Uwe Hermann ++ * Copyright (C) 2007,2008,2009 Carl-Daniel Hailfinger ++ * Copyright (C) 2009 Kontron Modular Computers GmbH + * + * 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 +@@ -32,16 +34,6 @@ + #include + #include "flash.h" + +-unsigned long flashbase = 0; +- +-/** +- * flashrom defaults to Parallel/LPC/FWH flash devices. If a known host +- * controller is found, the init routine sets the buses_supported bitfield to +- * contain the supported buses for that controller. +- */ +- +-enum chipbustype buses_supported = CHIP_BUSTYPE_NONSPI; +- + extern int ichspi_lock; + + static int enable_flash_ali_m1533(struct pci_dev *dev, const char *name) +@@ -59,56 +51,151 @@ static int enable_flash_ali_m1533(struct pci_dev *dev, const char *name) + return 0; + } + +-static int enable_flash_sis630(struct pci_dev *dev, const char *name) ++static int enable_flash_sis85c496(struct pci_dev *dev, const char *name) + { +- uint8_t b; +- +- /* Enable 0xFFF8000~0xFFFF0000 decoding on SiS 540/630. */ +- b = pci_read_byte(dev, 0x40); +- pci_write_byte(dev, 0x40, b | 0xb); +- +- /* Flash write enable on SiS 540/630. */ +- b = pci_read_byte(dev, 0x45); +- pci_write_byte(dev, 0x45, b | 0x40); +- +- /* The same thing on SiS 950 Super I/O side... */ +- +- /* First probe for Super I/O on config port 0x2e. */ +- OUTB(0x87, 0x2e); +- OUTB(0x01, 0x2e); +- OUTB(0x55, 0x2e); +- OUTB(0x55, 0x2e); +- +- if (INB(0x2f) != 0x87) { +- /* If that failed, try config port 0x4e. */ +- OUTB(0x87, 0x4e); +- OUTB(0x01, 0x4e); +- OUTB(0x55, 0x4e); +- OUTB(0xaa, 0x4e); +- if (INB(0x4f) != 0x87) { +- printf("Can not access SiS 950\n"); +- return -1; +- } +- OUTB(0x24, 0x4e); +- b = INB(0x4f) | 0xfc; +- OUTB(0x24, 0x4e); +- OUTB(b, 0x4f); +- OUTB(0x02, 0x4e); +- OUTB(0x02, 0x4f); +- } ++ uint8_t tmp; + +- OUTB(0x24, 0x2e); +- printf_debug("2f is %#x\n", INB(0x2f)); +- b = INB(0x2f) | 0xfc; +- OUTB(0x24, 0x2e); +- OUTB(b, 0x2f); ++ tmp = pci_read_byte(dev, 0xd0); ++ tmp |= 0xf8; ++ pci_write_byte(dev, 0xd0, tmp); + +- OUTB(0x02, 0x2e); +- OUTB(0x02, 0x2f); ++ return 0; ++} + ++static int enable_flash_sis_mapping(struct pci_dev *dev, const char *name) ++{ ++ uint8_t new, newer; ++ ++ /* Extended BIOS enable = 1, Lower BIOS Enable = 1 */ ++ /* This is 0xFFF8000~0xFFFF0000 decoding on SiS 540/630. */ ++ new = pci_read_byte(dev, 0x40); ++ new &= (~0x04); /* No idea why we clear bit 2. */ ++ new |= 0xb; /* 0x3 for some chipsets, bit 7 seems to be don't care. */ ++ pci_write_byte(dev, 0x40, new); ++ newer = pci_read_byte(dev, 0x40); ++ if (newer != new) { ++ printf_debug("tried to set register 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x40, new, name); ++ printf_debug("Stuck at 0x%x\n", newer); ++ return -1; ++ } + return 0; + } + ++static struct pci_dev *find_southbridge(uint16_t vendor, const char *name) ++{ ++ struct pci_dev *sbdev; ++ ++ sbdev = pci_dev_find_vendorclass(vendor, 0x0601); ++ if (!sbdev) ++ sbdev = pci_dev_find_vendorclass(vendor, 0x0680); ++ if (!sbdev) ++ sbdev = pci_dev_find_vendorclass(vendor, 0x0000); ++ if (!sbdev) ++ fprintf(stderr, "No southbridge found for %s!\n", name); ++ if (sbdev) ++ printf_debug("Found southbridge %04x:%04x at %02x:%02x:%01x\n", ++ sbdev->vendor_id, sbdev->device_id, ++ sbdev->bus, sbdev->dev, sbdev->func); ++ return sbdev; ++} ++ ++static int enable_flash_sis501(struct pci_dev *dev, const char *name) ++{ ++ uint8_t tmp; ++ int ret = 0; ++ struct pci_dev *sbdev; ++ ++ sbdev = find_southbridge(dev->vendor_id, name); ++ if (!sbdev) ++ return -1; ++ ++ ret = enable_flash_sis_mapping(sbdev, name); ++ ++ tmp = sio_read(0x22, 0x80); ++ tmp &= (~0x20); ++ tmp |= 0x4; ++ sio_write(0x22, 0x80, tmp); ++ ++ tmp = sio_read(0x22, 0x70); ++ tmp &= (~0x20); ++ tmp |= 0x4; ++ sio_write(0x22, 0x70, tmp); ++ ++ return ret; ++} ++ ++static int enable_flash_sis5511(struct pci_dev *dev, const char *name) ++{ ++ uint8_t tmp; ++ int ret = 0; ++ struct pci_dev *sbdev; ++ ++ sbdev = find_southbridge(dev->vendor_id, name); ++ if (!sbdev) ++ return -1; ++ ++ ret = enable_flash_sis_mapping(sbdev, name); ++ ++ tmp = sio_read(0x22, 0x50); ++ tmp &= (~0x20); ++ tmp |= 0x4; ++ sio_write(0x22, 0x50, tmp); ++ ++ return ret; ++} ++ ++static int enable_flash_sis530(struct pci_dev *dev, const char *name) ++{ ++ uint8_t new, newer; ++ int ret = 0; ++ struct pci_dev *sbdev; ++ ++ sbdev = find_southbridge(dev->vendor_id, name); ++ if (!sbdev) ++ return -1; ++ ++ ret = enable_flash_sis_mapping(sbdev, name); ++ ++ new = pci_read_byte(sbdev, 0x45); ++ new &= (~0x20); ++ new |= 0x4; ++ pci_write_byte(sbdev, 0x45, new); ++ newer = pci_read_byte(sbdev, 0x45); ++ if (newer != new) { ++ printf_debug("tried to set register 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x45, new, name); ++ printf_debug("Stuck at 0x%x\n", newer); ++ ret = -1; ++ } ++ ++ return ret; ++} ++ ++static int enable_flash_sis540(struct pci_dev *dev, const char *name) ++{ ++ uint8_t new, newer; ++ int ret = 0; ++ struct pci_dev *sbdev; ++ ++ sbdev = find_southbridge(dev->vendor_id, name); ++ if (!sbdev) ++ return -1; ++ ++ ret = enable_flash_sis_mapping(sbdev, name); ++ ++ new = pci_read_byte(sbdev, 0x45); ++ new &= (~0x80); ++ new |= 0x40; ++ pci_write_byte(sbdev, 0x45, new); ++ newer = pci_read_byte(sbdev, 0x45); ++ if (newer != new) { ++ printf_debug("tried to set register 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x45, new, name); ++ printf_debug("Stuck at 0x%x\n", newer); ++ ret = -1; ++ } ++ ++ return ret; ++} ++ + /* Datasheet: + * - Name: 82371AB PCI-TO-ISA / IDE XCELERATOR (PIIX4) + * - URL: http://www.intel.com/design/intarch/datashts/290562.htm +@@ -120,6 +207,8 @@ static int enable_flash_piix4(struct pci_dev *dev, const char *name) + uint16_t old, new; + uint16_t xbcs = 0x4e; /* X-Bus Chip Select register. */ + ++ buses_supported = CHIP_BUSTYPE_PARALLEL; ++ + old = pci_read_word(dev, xbcs); + + /* Set bit 9: 1-Meg Extended BIOS Enable (PCI master accesses to +@@ -204,51 +293,115 @@ static int enable_flash_ich_dc(struct pci_dev *dev, const char *name) + uint32_t fwh_conf; + int i; + char *idsel = NULL; ++ int tmp; ++ int max_decode_fwh_idsel = 0; ++ int max_decode_fwh_decode = 0; ++ int contiguous = 1; + +- /* Ignore all legacy ranges below 1 MB. */ ++ if (programmer_param) ++ idsel = strstr(programmer_param, "fwh_idsel="); ++ ++ if (idsel) { ++ idsel += strlen("fwh_idsel="); ++ fwh_conf = (uint32_t)strtoul(idsel, NULL, 0); ++ ++ /* FIXME: Need to undo this on shutdown. */ ++ printf("\nSetting IDSEL=0x%x for top 16 MB", fwh_conf); ++ pci_write_long(dev, 0xd0, fwh_conf); ++ pci_write_word(dev, 0xd4, fwh_conf); ++ /* FIXME: Decode settings are not changed. */ ++ } ++ ++ /* Ignore all legacy ranges below 1 MB. ++ * We currently only support flashing the chip which responds to ++ * IDSEL=0. To support IDSEL!=0, flashbase and decode size calculations ++ * have to be adjusted. ++ */ + /* FWH_SEL1 */ + fwh_conf = pci_read_long(dev, 0xd0); +- for (i = 7; i >= 0; i--) ++ for (i = 7; i >= 0; i--) { ++ tmp = (fwh_conf >> (i * 4)) & 0xf; + printf_debug("\n0x%08x/0x%08x FWH IDSEL: 0x%x", + (0x1ff8 + i) * 0x80000, + (0x1ff0 + i) * 0x80000, +- (fwh_conf >> (i * 4)) & 0xf); ++ tmp); ++ if ((tmp == 0) && contiguous) { ++ max_decode_fwh_idsel = (8 - i) * 0x80000; ++ } else { ++ contiguous = 0; ++ } ++ } + /* FWH_SEL2 */ + fwh_conf = pci_read_word(dev, 0xd4); +- for (i = 3; i >= 0; i--) ++ for (i = 3; i >= 0; i--) { ++ tmp = (fwh_conf >> (i * 4)) & 0xf; + printf_debug("\n0x%08x/0x%08x FWH IDSEL: 0x%x", + (0xff4 + i) * 0x100000, + (0xff0 + i) * 0x100000, +- (fwh_conf >> (i * 4)) & 0xf); ++ tmp); ++ if ((tmp == 0) && contiguous) { ++ max_decode_fwh_idsel = (8 - i) * 0x100000; ++ } else { ++ contiguous = 0; ++ } ++ } ++ contiguous = 1; + /* FWH_DEC_EN1 */ + fwh_conf = pci_read_word(dev, 0xd8); +- for (i = 7; i >= 0; i--) ++ for (i = 7; i >= 0; i--) { ++ tmp = (fwh_conf >> (i + 0x8)) & 0x1; + printf_debug("\n0x%08x/0x%08x FWH decode %sabled", + (0x1ff8 + i) * 0x80000, + (0x1ff0 + i) * 0x80000, +- (fwh_conf >> (i + 0x8)) & 0x1 ? "en" : "dis"); +- for (i = 3; i >= 0; i--) ++ tmp ? "en" : "dis"); ++ if ((tmp == 1) && contiguous) { ++ max_decode_fwh_decode = (8 - i) * 0x80000; ++ } else { ++ contiguous = 0; ++ } ++ } ++ for (i = 3; i >= 0; i--) { ++ tmp = (fwh_conf >> i) & 0x1; + printf_debug("\n0x%08x/0x%08x FWH decode %sabled", + (0xff4 + i) * 0x100000, + (0xff0 + i) * 0x100000, +- (fwh_conf >> i) & 0x1 ? "en" : "dis"); ++ tmp ? "en" : "dis"); ++ if ((tmp == 1) && contiguous) { ++ max_decode_fwh_decode = (8 - i) * 0x100000; ++ } else { ++ contiguous = 0; ++ } ++ } ++ max_rom_decode.fwh = min(max_decode_fwh_idsel, max_decode_fwh_decode); ++ printf_debug("\nMaximum FWH chip size: 0x%x bytes", max_rom_decode.fwh); + +- if (programmer_param) +- idsel = strstr(programmer_param, "fwh_idsel="); ++ /* If we're called by enable_flash_ich_dc_spi, it will override ++ * buses_supported anyway. ++ */ ++ buses_supported = CHIP_BUSTYPE_FWH; ++ return enable_flash_ich(dev, name, 0xdc); ++} + +- if (idsel) { +- idsel += strlen("fwh_idsel="); +- fwh_conf = (uint32_t)strtoul(idsel, NULL, 0); ++static int enable_flash_poulsbo(struct pci_dev *dev, const char *name) ++{ ++ uint16_t old, new; ++ int err; + +- /* FIXME: Need to undo this on shutdown. */ +- printf("\nSetting IDSEL=0x%x for top 16 MB", fwh_conf); +- pci_write_long(dev, 0xd0, fwh_conf); +- pci_write_word(dev, 0xd4, fwh_conf); +- } ++ if ((err = enable_flash_ich(dev, name, 0xd8)) != 0) ++ return err; + +- return enable_flash_ich(dev, name, 0xdc); ++ old = pci_read_byte(dev, 0xd9); ++ printf_debug("BIOS Prefetch Enable: %sabled, ", ++ (old & 1) ? "en" : "dis"); ++ new = old & ~1; ++ ++ if (new != old) ++ pci_write_byte(dev, 0xd9, new); ++ ++ return 0; + } + ++ + #define ICH_STRAP_RSVD 0x00 + #define ICH_STRAP_SPI 0x01 + #define ICH_STRAP_PCI 0x02 +@@ -258,6 +411,7 @@ static int enable_flash_vt8237s_spi(struct pci_dev *dev, const char *name) + { + uint32_t mmio_base; + ++ /* Do we really need no write enable? */ + mmio_base = (pci_read_long(dev, 0xbc)) << 8; + printf_debug("MMIO base at = 0x%x\n", mmio_base); + spibar = physmap("VT8237S MMIO registers", mmio_base, 0x70); +@@ -312,8 +466,7 @@ static int enable_flash_ich_dc_spi(struct pci_dev *dev, const char *name, + */ + + if (ich_generation == 7 && bbs == ICH_STRAP_LPC) { +- /* Not sure if it speaks LPC as well. */ +- buses_supported = CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH; ++ buses_supported = CHIP_BUSTYPE_FWH; + /* No further SPI initialization required */ + return ret; + } +@@ -325,16 +478,14 @@ static int enable_flash_ich_dc_spi(struct pci_dev *dev, const char *name, + spibar_offset = 0x3020; + break; + case 8: +- /* Not sure if it speaks LPC as well. */ +- buses_supported = CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI; ++ buses_supported = CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI; + spi_controller = SPI_CONTROLLER_ICH9; + spibar_offset = 0x3020; + break; + case 9: + case 10: + default: /* Future version might behave the same */ +- /* Not sure if it speaks LPC as well. */ +- buses_supported = CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI; ++ buses_supported = CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI; + spi_controller = SPI_CONTROLLER_ICH9; + spibar_offset = 0x3800; + break; +@@ -506,6 +657,13 @@ static int enable_flash_vt823x(struct pci_dev *dev, const char *name) + return -1; + } + ++ if (dev->device_id == 0x3227) { /* VT8237R */ ++ /* All memory cycles, not just ROM ones, go to LPC. */ ++ val = pci_read_byte(dev, 0x59); ++ val &= ~0x80; ++ pci_write_byte(dev, 0x59, val); ++ } ++ + return 0; + } + +@@ -515,14 +673,22 @@ static int enable_flash_cs5530(struct pci_dev *dev, const char *name) + + #define DECODE_CONTROL_REG2 0x5b /* F0 index 0x5b */ + #define ROM_AT_LOGIC_CONTROL_REG 0x52 /* F0 index 0x52 */ ++#define CS5530_RESET_CONTROL_REG 0x44 /* F0 index 0x44 */ ++#define CS5530_USB_SHADOW_REG 0x43 /* F0 index 0x43 */ + + #define LOWER_ROM_ADDRESS_RANGE (1 << 0) + #define ROM_WRITE_ENABLE (1 << 1) + #define UPPER_ROM_ADDRESS_RANGE (1 << 2) + #define BIOS_ROM_POSITIVE_DECODE (1 << 5) ++#define CS5530_ISA_MASTER (1 << 7) ++#define CS5530_ENABLE_SA2320 (1 << 2) ++#define CS5530_ENABLE_SA20 (1 << 6) + ++ buses_supported = CHIP_BUSTYPE_PARALLEL; + /* Decode 0x000E0000-0x000FFFFF (128 KB), not just 64 KB, and + * decode 0xFF000000-0xFFFFFFFF (16 MB), not just 256 KB. ++ * FIXME: Should we really touch the low mapping below 1 MB? Flashrom ++ * ignores that region completely. + * Make the configured ROM areas writable. + */ + reg8 = pci_read_byte(dev, ROM_AT_LOGIC_CONTROL_REG); +@@ -536,6 +702,24 @@ static int enable_flash_cs5530(struct pci_dev *dev, const char *name) + reg8 |= BIOS_ROM_POSITIVE_DECODE; + pci_write_byte(dev, DECODE_CONTROL_REG2, reg8); + ++ reg8 = pci_read_byte(dev, CS5530_RESET_CONTROL_REG); ++ if (reg8 & CS5530_ISA_MASTER) { ++ /* We have A0-A23 available. */ ++ max_rom_decode.parallel = 16 * 1024 * 1024; ++ } else { ++ reg8 = pci_read_byte(dev, CS5530_USB_SHADOW_REG); ++ if (reg8 & CS5530_ENABLE_SA2320) { ++ /* We have A0-19, A20-A23 available. */ ++ max_rom_decode.parallel = 16 * 1024 * 1024; ++ } else if (reg8 & CS5530_ENABLE_SA20) { ++ /* We have A0-19, A20 available. */ ++ max_rom_decode.parallel = 2 * 1024 * 1024; ++ } else { ++ /* A20 and above are not active. */ ++ max_rom_decode.parallel = 1024 * 1024; ++ } ++ } ++ + return 0; + } + +@@ -592,38 +776,6 @@ static int enable_flash_sc1100(struct pci_dev *dev, const char *name) + return 0; + } + +-static int enable_flash_sis5595(struct pci_dev *dev, const char *name) +-{ +- uint8_t new, newer; +- +- new = pci_read_byte(dev, 0x45); +- +- new &= (~0x20); /* Clear bit 5. */ +- new |= 0x4; /* Set bit 2. */ +- +- pci_write_byte(dev, 0x45, new); +- +- newer = pci_read_byte(dev, 0x45); +- if (newer != new) { +- printf_debug("tried to set register 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x45, new, name); +- printf_debug("Stuck at 0x%x\n", newer); +- return -1; +- } +- +- /* Extended BIOS enable = 1, Lower BIOS Enable = 1 */ +- new = pci_read_byte(dev, 0x40); +- new &= 0xFB; +- new |= 0x3; +- pci_write_byte(dev, 0x40, new); +- newer = pci_read_byte(dev, 0x40); +- if (newer != new) { +- printf_debug("tried to set register 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x40, new, name); +- printf_debug("Stuck at 0x%x\n", newer); +- return -1; +- } +- return 0; +-} +- + /* Works for AMD-8111, VIA VT82C586A/B, VIA VT82C686A/B. */ + static int enable_flash_amd8111(struct pci_dev *dev, const char *name) + { +@@ -874,19 +1026,19 @@ static int enable_flash_sb400(struct pci_dev *dev, const char *name) + + static int enable_flash_mcp55(struct pci_dev *dev, const char *name) + { +- uint8_t old, new, byte; +- uint16_t word; ++ uint8_t old, new, val; ++ uint16_t wordval; + + /* Set the 0-16 MB enable bits. */ +- byte = pci_read_byte(dev, 0x88); +- byte |= 0xff; /* 256K */ +- pci_write_byte(dev, 0x88, byte); +- byte = pci_read_byte(dev, 0x8c); +- byte |= 0xff; /* 1M */ +- pci_write_byte(dev, 0x8c, byte); +- word = pci_read_word(dev, 0x90); +- word |= 0x7fff; /* 16M */ +- pci_write_word(dev, 0x90, word); ++ val = pci_read_byte(dev, 0x88); ++ val |= 0xff; /* 256K */ ++ pci_write_byte(dev, 0x88, val); ++ val = pci_read_byte(dev, 0x8c); ++ val |= 0xff; /* 1M */ ++ pci_write_byte(dev, 0x8c, val); ++ wordval = pci_read_word(dev, 0x90); ++ wordval |= 0x7fff; /* 16M */ ++ pci_write_word(dev, 0x90, wordval); + + old = pci_read_byte(dev, 0x6d); + new = old | 0x01; +@@ -972,12 +1124,16 @@ const struct penable chipset_enables[] = { + {0x1022, 0x7468, OK, "AMD", "AMD8111", enable_flash_amd8111}, + {0x1078, 0x0100, OK, "AMD", "CS5530(A)", enable_flash_cs5530}, + {0x1022, 0x2080, OK, "AMD", "CS5536", enable_flash_cs5536}, ++ {0x1022, 0x2090, OK, "AMD", "CS5536", enable_flash_cs5536}, + {0x1022, 0x3000, OK, "AMD", "Elan SC520", get_flashbase_sc520}, + {0x1002, 0x438D, OK, "AMD", "SB600", enable_flash_sb600}, + {0x1002, 0x439d, OK, "AMD", "SB700/SB710/SB750", enable_flash_sb600}, + {0x100b, 0x0510, NT, "AMD", "SC1100", enable_flash_sc1100}, + {0x1002, 0x4377, OK, "ATI", "SB400", enable_flash_sb400}, + {0x1166, 0x0205, OK, "Broadcom", "HT-1000", enable_flash_ht1000}, ++ {0x8086, 0x3b00, NT, "Intel", "3400 Desktop", enable_flash_ich10}, ++ {0x8086, 0x3b01, NT, "Intel", "3400 Mobile", enable_flash_ich10}, ++ {0x8086, 0x3b0d, NT, "Intel", "3400 Mobile SFF", enable_flash_ich10}, + {0x8086, 0x7198, OK, "Intel", "440MX", enable_flash_piix4}, + {0x8086, 0x25a1, OK, "Intel", "6300ESB", enable_flash_ich_4e}, + {0x8086, 0x2670, OK, "Intel", "631xESB/632xESB/3100", enable_flash_ich_dc}, +@@ -1000,6 +1156,7 @@ const struct penable chipset_enables[] = { + {0x8086, 0x27b8, OK, "Intel", "ICH7/ICH7R", enable_flash_ich7}, + {0x8086, 0x27b9, OK, "Intel", "ICH7M", enable_flash_ich7}, + {0x8086, 0x27bd, OK, "Intel", "ICH7MDH", enable_flash_ich7}, ++ {0x8086, 0x27bc, OK, "Intel", "NM10", enable_flash_ich7}, + {0x8086, 0x2410, OK, "Intel", "ICH", enable_flash_ich_4e}, + {0x8086, 0x2812, OK, "Intel", "ICH8DH", enable_flash_ich8}, + {0x8086, 0x2814, OK, "Intel", "ICH8DO", enable_flash_ich8}, +@@ -1017,6 +1174,8 @@ const struct penable chipset_enables[] = { + {0x8086, 0x7000, OK, "Intel", "PIIX3", enable_flash_piix4}, + {0x8086, 0x7110, OK, "Intel", "PIIX4/4E/4M", enable_flash_piix4}, + {0x8086, 0x122e, OK, "Intel", "PIIX", enable_flash_piix4}, ++ {0x8086, 0x8119, OK, "Intel", "Poulsbo", enable_flash_poulsbo}, ++ {0x10de, 0x0030, OK, "NVIDIA", "nForce4/MCP4", enable_flash_nvidia_nforce2}, + {0x10de, 0x0050, OK, "NVIDIA", "CK804", enable_flash_ck804}, /* LPC */ + {0x10de, 0x0051, OK, "NVIDIA", "CK804", enable_flash_ck804}, /* Pro */ + {0x10de, 0x0060, OK, "NVIDIA", "NForce2", enable_flash_nvidia_nforce2}, +@@ -1035,16 +1194,44 @@ const struct penable chipset_enables[] = { + {0x10de, 0x0366, OK, "NVIDIA", "MCP55", enable_flash_mcp55}, /* LPC */ + {0x10de, 0x0367, OK, "NVIDIA", "MCP55", enable_flash_mcp55}, /* Pro */ + {0x10de, 0x0548, OK, "NVIDIA", "MCP67", enable_flash_mcp55}, +- {0x1039, 0x0008, OK, "SiS", "SiS5595", enable_flash_sis5595}, +- {0x1039, 0x0630, NT, "SiS", "SiS630", enable_flash_sis630}, ++ {0x1039, 0x0496, NT, "SiS", "85C496+497", enable_flash_sis85c496}, ++ {0x1039, 0x0406, NT, "SiS", "501/5101/5501", enable_flash_sis501}, ++ {0x1039, 0x5511, NT, "SiS", "5511", enable_flash_sis5511}, ++ {0x1039, 0x5596, NT, "SiS", "5596", enable_flash_sis5511}, ++ {0x1039, 0x5571, NT, "SiS", "5571", enable_flash_sis530}, ++ {0x1039, 0x5591, NT, "SiS", "5591/5592", enable_flash_sis530}, ++ {0x1039, 0x5597, NT, "SiS", "5597/5598/5581/5120", enable_flash_sis530}, ++ {0x1039, 0x0530, NT, "SiS", "530", enable_flash_sis530}, ++ {0x1039, 0x5600, NT, "SiS", "600", enable_flash_sis530}, ++ {0x1039, 0x0620, NT, "SiS", "620", enable_flash_sis530}, ++ {0x1039, 0x0540, NT, "SiS", "540", enable_flash_sis540}, ++ {0x1039, 0x0630, NT, "SiS", "630", enable_flash_sis540}, ++ {0x1039, 0x0635, NT, "SiS", "635", enable_flash_sis540}, ++ {0x1039, 0x0640, NT, "SiS", "640", enable_flash_sis540}, ++ {0x1039, 0x0645, NT, "SiS", "645", enable_flash_sis540}, ++ {0x1039, 0x0646, NT, "SiS", "645DX", enable_flash_sis540}, ++ {0x1039, 0x0648, NT, "SiS", "648", enable_flash_sis540}, ++ {0x1039, 0x0650, NT, "SiS", "650", enable_flash_sis540}, ++ {0x1039, 0x0651, NT, "SiS", "651", enable_flash_sis540}, ++ {0x1039, 0x0655, NT, "SiS", "655", enable_flash_sis540}, ++ {0x1039, 0x0730, NT, "SiS", "730", enable_flash_sis540}, ++ {0x1039, 0x0733, NT, "SiS", "733", enable_flash_sis540}, ++ {0x1039, 0x0735, OK, "SiS", "735", enable_flash_sis540}, ++ {0x1039, 0x0740, NT, "SiS", "740", enable_flash_sis540}, ++ {0x1039, 0x0745, NT, "SiS", "745", enable_flash_sis540}, ++ {0x1039, 0x0746, NT, "SiS", "746", enable_flash_sis540}, ++ {0x1039, 0x0748, NT, "SiS", "748", enable_flash_sis540}, ++ {0x1039, 0x0755, NT, "SiS", "755", enable_flash_sis540}, + {0x1106, 0x8324, OK, "VIA", "CX700", enable_flash_vt823x}, + {0x1106, 0x8231, NT, "VIA", "VT8231", enable_flash_vt823x}, + {0x1106, 0x3074, NT, "VIA", "VT8233", enable_flash_vt823x}, ++ {0x1106, 0x3147, OK, "VIA", "VT8233A", enable_flash_vt823x}, + {0x1106, 0x3177, OK, "VIA", "VT8235", enable_flash_vt823x}, + {0x1106, 0x3227, OK, "VIA", "VT8237", enable_flash_vt823x}, + {0x1106, 0x3337, OK, "VIA", "VT8237A", enable_flash_vt823x}, + {0x1106, 0x3372, OK, "VIA", "VT8237S", enable_flash_vt8237s_spi}, + {0x1106, 0x8353, OK, "VIA", "VX800", enable_flash_vt8237s_spi}, ++ {0x1106, 0x0596, OK, "VIA", "VT82C596", enable_flash_amd8111}, + {0x1106, 0x0586, OK, "VIA", "VT82C586A/B", enable_flash_amd8111}, + {0x1106, 0x0686, NT, "VIA", "VT82C686A/B", enable_flash_amd8111}, + +diff --git a/cli_classic.c b/cli_classic.c +new file mode 100644 +index 0000000..6cb0578 +--- /dev/null ++++ b/cli_classic.c +@@ -0,0 +1,386 @@ ++/* ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2000 Silicon Integrated System Corporation ++ * Copyright (C) 2004 Tyan Corp ++ * Copyright (C) 2005-2008 coresystems GmbH ++ * Copyright (C) 2008,2009,2010 Carl-Daniel Hailfinger ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "flash.h" ++#include "flashchips.h" ++ ++void cli_classic_usage(const char *name) ++{ ++ const char *pname; ++ int pnamelen; ++ int remaining = 0; ++ enum programmer p; ++ ++ printf("Usage: %s [-VfLzhR] [-E|-r file|-w file|-v file] [-c chipname]\n" ++ " [-m [vendor:]part] [-l file] [-i image] [-p programmer]\n\n", name); ++ ++ printf("Please note that the command line interface for flashrom will " ++ "change before\nflashrom 1.0. Do not use flashrom in scripts " ++ "or other automated tools without\nchecking that your flashrom" ++ " version won't interpret options in a different way.\n\n"); ++ ++ printf ++ (" -r | --read: read flash and save into file\n" ++ " -w | --write: write file into flash\n" ++ " -v | --verify: verify flash against file\n" ++ " -n | --noverify: don't verify flash against file\n" ++ " -E | --erase: erase flash device\n" ++ " -V | --verbose: more verbose output\n" ++ " -c | --chip : probe only for specified flash chip\n" ++#if INTERNAL_SUPPORT == 1 ++ " -m | --mainboard <[vendor:]part>: override mainboard settings\n" ++#endif ++ " -f | --force: force write without checking image\n" ++ " -l | --layout : read ROM layout from file\n" ++ " -i | --image : only flash image name from flash layout\n" ++ " -L | --list-supported: print supported devices\n" ++#if PRINT_WIKI_SUPPORT == 1 ++ " -z | --list-supported-wiki: print supported devices in wiki syntax\n" ++#endif ++ " -p | --programmer : specify the programmer device"); ++ ++ for (p = 0; p < PROGRAMMER_INVALID; p++) { ++ pname = programmer_table[p].name; ++ pnamelen = strlen(pname); ++ if (remaining - pnamelen - 2 < 0) { ++ printf("\n "); ++ remaining = 43; ++ } else { ++ printf(" "); ++ remaining--; ++ } ++ if (p == 0) { ++ printf("("); ++ remaining--; ++ } ++ printf("%s", pname); ++ remaining -= pnamelen; ++ if (p < PROGRAMMER_INVALID - 1) { ++ printf(","); ++ remaining--; ++ } else { ++ printf(")\n"); ++ } ++ } ++ ++ printf( ++ " -h | --help: print this help text\n" ++ " -R | --version: print the version (release)\n" ++ "\nYou can specify one of -E, -r, -w, -v or no operation. If no operation is\n" ++ "specified, then all that happens is that flash info is dumped.\n\n"); ++ exit(1); ++} ++ ++int cli_classic(int argc, char *argv[]) ++{ ++ unsigned long size; ++ /* Probe for up to three flash chips. */ ++ struct flashchip *flash, *flashes[3]; ++ const char *name; ++ int namelen; ++ int opt; ++ int option_index = 0; ++ int force = 0; ++ int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0; ++ int dont_verify_it = 0, list_supported = 0; ++#if PRINT_WIKI_SUPPORT == 1 ++ int list_supported_wiki = 0; ++#endif ++ int operation_specified = 0; ++ int i; ++ ++#if PRINT_WIKI_SUPPORT == 1 ++ const char *optstring = "rRwvnVEfc:m:l:i:p:Lzh"; ++#else ++ const char *optstring = "rRwvnVEfc:m:l:i:p:Lh"; ++#endif ++ static struct option long_options[] = { ++ {"read", 0, 0, 'r'}, ++ {"write", 0, 0, 'w'}, ++ {"erase", 0, 0, 'E'}, ++ {"verify", 0, 0, 'v'}, ++ {"noverify", 0, 0, 'n'}, ++ {"chip", 1, 0, 'c'}, ++ {"mainboard", 1, 0, 'm'}, ++ {"verbose", 0, 0, 'V'}, ++ {"force", 0, 0, 'f'}, ++ {"layout", 1, 0, 'l'}, ++ {"image", 1, 0, 'i'}, ++ {"list-supported", 0, 0, 'L'}, ++#if PRINT_WIKI_SUPPORT == 1 ++ {"list-supported-wiki", 0, 0, 'z'}, ++#endif ++ {"programmer", 1, 0, 'p'}, ++ {"help", 0, 0, 'h'}, ++ {"version", 0, 0, 'R'}, ++ {0, 0, 0, 0} ++ }; ++ ++ char *filename = NULL; ++ ++ char *tempstr = NULL; ++ ++ print_version(); ++ ++ if (argc > 1) { ++ /* Yes, print them. */ ++ printf_debug("The arguments are:\n"); ++ for (i = 1; i < argc; ++i) ++ printf_debug("%s\n", argv[i]); ++ } ++ ++ if (selfcheck()) ++ exit(1); ++ ++ setbuf(stdout, NULL); ++ while ((opt = getopt_long(argc, argv, optstring, ++ long_options, &option_index)) != EOF) { ++ switch (opt) { ++ case 'r': ++ if (++operation_specified > 1) { ++ fprintf(stderr, "More than one operation " ++ "specified. Aborting.\n"); ++ exit(1); ++ } ++ read_it = 1; ++ break; ++ case 'w': ++ if (++operation_specified > 1) { ++ fprintf(stderr, "More than one operation " ++ "specified. Aborting.\n"); ++ exit(1); ++ } ++ write_it = 1; ++ break; ++ case 'v': ++ //FIXME: gracefully handle superfluous -v ++ if (++operation_specified > 1) { ++ fprintf(stderr, "More than one operation " ++ "specified. Aborting.\n"); ++ exit(1); ++ } ++ if (dont_verify_it) { ++ fprintf(stderr, "--verify and --noverify are" ++ "mutually exclusive. Aborting.\n"); ++ exit(1); ++ } ++ verify_it = 1; ++ break; ++ case 'n': ++ if (verify_it) { ++ fprintf(stderr, "--verify and --noverify are" ++ "mutually exclusive. Aborting.\n"); ++ exit(1); ++ } ++ dont_verify_it = 1; ++ break; ++ case 'c': ++ chip_to_probe = strdup(optarg); ++ break; ++ case 'V': ++ verbose++; ++ break; ++ case 'E': ++ if (++operation_specified > 1) { ++ fprintf(stderr, "More than one operation " ++ "specified. Aborting.\n"); ++ exit(1); ++ } ++ erase_it = 1; ++ break; ++#if INTERNAL_SUPPORT == 1 ++ case 'm': ++ tempstr = strdup(optarg); ++ lb_vendor_dev_from_string(tempstr); ++ break; ++#endif ++ case 'f': ++ force = 1; ++ break; ++ case 'l': ++ tempstr = strdup(optarg); ++ if (read_romlayout(tempstr)) ++ exit(1); ++ break; ++ case 'i': ++ tempstr = strdup(optarg); ++ find_romentry(tempstr); ++ break; ++ case 'L': ++ list_supported = 1; ++ break; ++#if PRINT_WIKI_SUPPORT == 1 ++ case 'z': ++ list_supported_wiki = 1; ++ break; ++#endif ++ case 'p': ++ for (programmer = 0; programmer < PROGRAMMER_INVALID; programmer++) { ++ name = programmer_table[programmer].name; ++ namelen = strlen(name); ++ if (strncmp(optarg, name, namelen) == 0) { ++ switch (optarg[namelen]) { ++ case ':': ++ programmer_param = strdup(optarg + namelen + 1); ++ break; ++ case '\0': ++ break; ++ default: ++ /* The continue refers to the ++ * for loop. It is here to be ++ * able to differentiate between ++ * foo and foobar. ++ */ ++ continue; ++ } ++ break; ++ } ++ } ++ if (programmer == PROGRAMMER_INVALID) { ++ printf("Error: Unknown programmer %s.\n", optarg); ++ exit(1); ++ } ++ break; ++ case 'R': ++ /* print_version() is always called during startup. */ ++ exit(0); ++ break; ++ case 'h': ++ default: ++ cli_classic_usage(argv[0]); ++ break; ++ } ++ } ++ ++ if (list_supported) { ++ print_supported(); ++ exit(0); ++ } ++ ++#if PRINT_WIKI_SUPPORT == 1 ++ if (list_supported_wiki) { ++ print_supported_wiki(); ++ exit(0); ++ } ++#endif ++ ++ if (read_it && write_it) { ++ printf("Error: -r and -w are mutually exclusive.\n"); ++ cli_classic_usage(argv[0]); ++ } ++ ++ if (optind < argc) ++ filename = argv[optind++]; ++ ++ if (optind < argc) { ++ printf("Error: Extra parameter found.\n"); ++ cli_classic_usage(argv[0]); ++ } ++ ++ if (programmer_init()) { ++ fprintf(stderr, "Error: Programmer initialization failed.\n"); ++ exit(1); ++ } ++ ++ // FIXME: Delay calibration should happen in programmer code. ++ myusec_calibrate_delay(); ++ ++ for (i = 0; i < ARRAY_SIZE(flashes); i++) { ++ flashes[i] = ++ probe_flash(i ? flashes[i - 1] + 1 : flashchips, 0); ++ if (!flashes[i]) ++ for (i++; i < ARRAY_SIZE(flashes); i++) ++ flashes[i] = NULL; ++ } ++ ++ if (flashes[1]) { ++ printf("Multiple flash chips were detected:"); ++ for (i = 0; i < ARRAY_SIZE(flashes) && flashes[i]; i++) ++ printf(" %s", flashes[i]->name); ++ printf("\nPlease specify which chip to use with the -c option.\n"); ++ programmer_shutdown(); ++ exit(1); ++ } else if (!flashes[0]) { ++ printf("No EEPROM/flash device found.\n"); ++ if (!force || !chip_to_probe) { ++ printf("If you know which flash chip you have, and if this version of flashrom\n"); ++ printf("supports a similar flash chip, you can try to force read your chip. Run:\n"); ++ printf("flashrom -f -r -c similar_supported_flash_chip filename\n"); ++ printf("\n"); ++ printf("Note: flashrom can never write when the flash chip isn't found automatically.\n"); ++ } ++ if (force && read_it && chip_to_probe) { ++ printf("Force read (-f -r -c) requested, forcing chip probe success:\n"); ++ flashes[0] = probe_flash(flashchips, 1); ++ if (!flashes[0]) { ++ printf("flashrom does not support a flash chip named '%s'.\n", chip_to_probe); ++ printf("Run flashrom -L to view the hardware supported in this flashrom version.\n"); ++ exit(1); ++ } ++ printf("Please note that forced reads most likely contain garbage.\n"); ++ return read_flash(flashes[0], filename); ++ } ++ // FIXME: flash writes stay enabled! ++ programmer_shutdown(); ++ exit(1); ++ } ++ ++ flash = flashes[0]; ++ ++ check_chip_supported(flash); ++ ++ size = flash->total_size * 1024; ++ if (check_max_decode((buses_supported & flash->bustype), size) && ++ (!force)) { ++ fprintf(stderr, "Chip is too big for this programmer " ++ "(-V gives details). Use --force to override.\n"); ++ programmer_shutdown(); ++ return 1; ++ } ++ ++ if (!(read_it | write_it | verify_it | erase_it)) { ++ printf("No operations were specified.\n"); ++ // FIXME: flash writes stay enabled! ++ programmer_shutdown(); ++ exit(1); ++ } ++ ++ if (!filename && !erase_it) { ++ printf("Error: No filename specified.\n"); ++ // FIXME: flash writes stay enabled! ++ programmer_shutdown(); ++ exit(1); ++ } ++ ++ /* Always verify write operations unless -n is used. */ ++ if (write_it && !dont_verify_it) ++ verify_it = 1; ++ ++ return doit(flash, force, filename, read_it, write_it, erase_it, verify_it); ++} +diff --git a/cli_output.c b/cli_output.c +new file mode 100644 +index 0000000..2ced043 +--- /dev/null ++++ b/cli_output.c +@@ -0,0 +1,51 @@ ++/* ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2009 Sean Nelson ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include ++#include "flash.h" ++ ++int print(int type, const char *fmt, ...) ++{ ++ va_list ap; ++ int ret; ++ FILE *output_type; ++ ++ switch (type) ++ { ++ case MSG_ERROR: ++ output_type = stderr; ++ break; ++ case MSG_BARF: ++ if (verbose < 2) return 0; ++ case MSG_DEBUG: ++ if (verbose < 1) return 0; ++ case MSG_INFO: ++ default: ++ output_type = stdout; ++ break; ++ } ++ ++ va_start(ap, fmt); ++ ret = vfprintf(output_type, fmt, ap); ++ va_end(ap); ++ return ret; ++} ++ +diff --git a/dediprog.c b/dediprog.c +new file mode 100644 +index 0000000..c6d2a30 +--- /dev/null ++++ b/dediprog.c +@@ -0,0 +1,394 @@ ++/* ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2010 Carl-Daniel Hailfinger ++ * ++ * 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; version 2 of the License. ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "flash.h" ++#include "spi.h" ++ ++#define DEFAULT_TIMEOUT 3000 ++usb_dev_handle *dediprog_handle; ++ ++int dediprog_do_stuff(void); ++ ++void print_hex(void *buf, size_t len) ++{ ++ size_t i; ++ ++ for (i = 0; i < len; i++) ++ msg_pdbg(" %02x", ((uint8_t *)buf)[i]); ++} ++ ++struct usb_device *get_device_by_vid_pid(uint16_t vid, uint16_t pid) ++{ ++ struct usb_bus *bus; ++ struct usb_device *dev; ++ ++ for (bus = usb_get_busses(); bus; bus = bus->next) ++ for (dev = bus->devices; dev; dev = dev->next) ++ if ((dev->descriptor.idVendor == vid) && ++ (dev->descriptor.idProduct == pid)) ++ return dev; ++ ++ return NULL; ++} ++ ++//int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, int value, int index, char *bytes, int size, int timeout); ++ ++int dediprog_set_spi_voltage(uint16_t voltage) ++{ ++ int ret; ++ unsigned int mv; ++ ++ switch (voltage) { ++ case 0x0: ++ /* Admittedly this one is an assumption. */ ++ mv = 0; ++ break; ++ case 0x12: ++ mv = 1800; ++ break; ++ case 0x11: ++ mv = 2500; ++ break; ++ case 0x10: ++ mv = 3500; ++ break; ++ default: ++ msg_perr("Unknown voltage selector 0x%x! Aborting.\n", voltage); ++ return 1; ++ } ++ msg_pdbg("Setting SPI voltage to %u.%03u V\n", mv / 1000, mv % 1000); ++ ++ ret = usb_control_msg(dediprog_handle, 0x42, 0x9, voltage, 0xff, NULL, 0x0, DEFAULT_TIMEOUT); ++ if (ret != 0x0) { ++ msg_perr("Command Set SPI Voltage 0x%x failed!\n", voltage); ++ return 1; ++ } ++ return 0; ++} ++ ++/* After dediprog_set_spi_speed, the original app always calls ++ * dediprog_set_spi_voltage(0) and then ++ * dediprog_check_devicestring() four times in a row. ++ * After that, dediprog_command_a() is called. ++ * This looks suspiciously like the microprocessor in the SF100 has to be ++ * restarted/reinitialized in case the speed changes. ++ */ ++int dediprog_set_spi_speed(uint16_t speed) ++{ ++ int ret; ++ unsigned int khz; ++ ++ /* Case 1 and 2 are in weird order. Probably an organically "grown" ++ * interface. ++ * Base frequency is 24000 kHz, divisors are (in order) ++ * 1, 3, 2, 8, 11, 16, 32, 64. ++ */ ++ switch (speed) { ++ case 0x0: ++ khz = 24000; ++ break; ++ case 0x1: ++ khz = 8000; ++ break; ++ case 0x2: ++ khz = 12000; ++ break; ++ case 0x3: ++ khz = 3000; ++ break; ++ case 0x4: ++ khz = 2180; ++ break; ++ case 0x5: ++ khz = 1500; ++ break; ++ case 0x6: ++ khz = 750; ++ break; ++ case 0x7: ++ khz = 375; ++ break; ++ default: ++ msg_perr("Unknown frequency selector 0x%x! Aborting.\n", speed); ++ return 1; ++ } ++ msg_pdbg("Setting SPI speed to %u kHz\n", khz); ++ ++ ret = usb_control_msg(dediprog_handle, 0x42, 0x61, speed, 0xff, NULL, 0x0, DEFAULT_TIMEOUT); ++ if (ret != 0x0) { ++ msg_perr("Command Set SPI Speed 0x%x failed!\n", speed); ++ return 1; ++ } ++ return 0; ++} ++ ++int dediprog_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len) ++{ ++ msg_pspew("%s, start=0x%x, len=0x%x\n", __func__, start, len); ++ /* Chosen read length is 16 bytes for now. */ ++ return spi_read_chunked(flash, buf, start, len, 16); ++} ++ ++int dediprog_spi_send_command(unsigned int writecnt, unsigned int readcnt, ++ const unsigned char *writearr, unsigned char *readarr) ++{ ++ int ret; ++ ++ msg_pspew("%s, writecnt=%i, readcnt=%i\n", __func__, writecnt, readcnt); ++ /* Paranoid, but I don't want to be blamed if anything explodes. */ ++ if (writecnt > 5) { ++ msg_perr("Untested writecnt=%i, aborting.\n", writecnt); ++ return 1; ++ } ++ /* 16 byte reads should work. */ ++ if (readcnt > 16) { ++ msg_perr("Untested readcnt=%i, aborting.\n", readcnt); ++ return 1; ++ } ++ ++ ret = usb_control_msg(dediprog_handle, 0x42, 0x1, 0xff, readcnt ? 0x1 : 0x0, (char *)writearr, writecnt, DEFAULT_TIMEOUT); ++ if (ret != writecnt) { ++ msg_perr("Send SPI failed, expected %i, got %i %s!\n", ++ writecnt, ret, usb_strerror()); ++ return 1; ++ } ++ if (!readcnt) ++ return 0; ++ memset(readarr, 0, readcnt); ++ ret = usb_control_msg(dediprog_handle, 0xc2, 0x01, 0xbb8, 0x0000, (char *)readarr, readcnt, DEFAULT_TIMEOUT); ++ if (ret != readcnt) { ++ msg_perr("Receive SPI failed, expected %i, got %i %s!\n", ++ readcnt, ret, usb_strerror()); ++ return 1; ++ } ++ return 0; ++} ++ ++int dediprog_check_devicestring(void) ++{ ++ int ret; ++ char buf[0x11]; ++ ++ /* Command Prepare Receive Device String. */ ++ memset(buf, 0, sizeof(buf)); ++ ret = usb_control_msg(dediprog_handle, 0xc3, 0x7, 0x0, 0xef03, buf, 0x1, DEFAULT_TIMEOUT); ++ /* The char casting is needed to stop gcc complaining about an always true comparison. */ ++ if ((ret != 0x1) || (buf[0] != (char)0xff)) { ++ msg_perr("Unexpected response to Command Prepare Receive Device" ++ " String!\n"); ++ return 1; ++ } ++ /* Command Receive Device String. */ ++ memset(buf, 0, sizeof(buf)); ++ ret = usb_control_msg(dediprog_handle, 0xc2, 0x8, 0xff, 0xff, buf, 0x10, DEFAULT_TIMEOUT); ++ if (ret != 0x10) { ++ msg_perr("Incomplete/failed Command Receive Device String!\n"); ++ return 1; ++ } ++ buf[0x10] = '\0'; ++ msg_pdbg("Found a %s\n", buf); ++ if (memcmp(buf, "SF100", 0x5)) { ++ msg_perr("Device not a SF100!\n"); ++ return 1; ++ } ++ /* Only these versions were tested. */ ++ if (memcmp(buf, "SF100 V:2.1.1 ", 0x10) && ++ memcmp(buf, "SF100 V:3.1.8 ", 0x10)) { ++ msg_perr("Unexpected firmware version!\n"); ++ return 1; ++ } ++ return 0; ++} ++ ++/* Command A seems to be some sort of device init. It is either followed by ++ * dediprog_check_devicestring (often) or Command A (often) or ++ * Command F (once). ++ */ ++int dediprog_command_a(void) ++{ ++ int ret; ++ char buf[0x1]; ++ ++ memset(buf, 0, sizeof(buf)); ++ ret = usb_control_msg(dediprog_handle, 0xc3, 0xb, 0x0, 0x0, buf, 0x1, DEFAULT_TIMEOUT); ++ if ((ret != 0x1) || (buf[0] != 0x6f)) { ++ msg_perr("Unexpected response to Command A!\n"); ++ return 1; ++ } ++ return 0; ++} ++ ++/* Command C is only sent after dediprog_check_devicestring, but not after every ++ * invocation of dediprog_check_devicestring. It is only sent after the first ++ * dediprog_command_a(); dediprog_check_devicestring() sequence in each session. ++ * I'm tempted to call this one start_SPI_engine or finish_init. ++ */ ++int dediprog_command_c(void) ++{ ++ int ret; ++ ++ ret = usb_control_msg(dediprog_handle, 0x42, 0x4, 0x0, 0x0, NULL, 0x0, DEFAULT_TIMEOUT); ++ if (ret != 0x0) { ++ msg_perr("Unexpected response to Command C!\n"); ++ return 1; ++ } ++ return 0; ++} ++ ++/* Very strange. Seems to be a programmer keepalive or somesuch. ++ * Wait unsuccessfully for timeout ms to read one byte. ++ * Is usually called after setting voltage to 0. ++ */ ++int dediprog_command_f(int timeout) ++{ ++ int ret; ++ char buf[0x1]; ++ ++ memset(buf, 0, sizeof(buf)); ++ ret = usb_control_msg(dediprog_handle, 0xc2, 0x11, 0xff, 0xff, buf, 0x1, timeout); ++ if (ret != 0x0) { ++ msg_perr("Unexpected response to Command F!\n"); ++ return 1; ++ } ++ return 0; ++} ++ ++/* URB numbers refer to the first log ever captured. */ ++int dediprog_init(void) ++{ ++ struct usb_device *dev; ++ ++ msg_pspew("%s\n", __func__); ++ ++ /* Here comes the USB stuff. */ ++ usb_init(); ++ usb_find_busses(); ++ usb_find_devices(); ++ dev = get_device_by_vid_pid(0x0483, 0xdada); ++ if (!dev) { ++ msg_perr("Could not find a Dediprog SF100 on USB!\n"); ++ return 1; ++ } ++ msg_pdbg("Found USB device (%04x:%04x).\n", ++ dev->descriptor.idVendor, ++ dev->descriptor.idProduct); ++ dediprog_handle = usb_open(dev); ++ usb_set_configuration(dediprog_handle, 1); ++ usb_claim_interface(dediprog_handle, 0); ++ /* URB 6. Command A. */ ++ if (dediprog_command_a()) ++ return 1; ++ /* URB 7. Command A. */ ++ if (dediprog_command_a()) ++ return 1; ++ /* URB 8. Command Prepare Receive Device String. */ ++ /* URB 9. Command Receive Device String. */ ++ if (dediprog_check_devicestring()) ++ return 1; ++ /* URB 10. Command C. */ ++ if (dediprog_command_c()) ++ return 1; ++ /* URB 11. Command Set SPI Voltage. */ ++ if (dediprog_set_spi_voltage(0x10)) ++ return 1; ++ ++ buses_supported = CHIP_BUSTYPE_SPI; ++ spi_controller = SPI_CONTROLLER_DEDIPROG; ++ ++ /* RE leftover, leave in until the driver is complete. */ ++#if 0 ++ /* Execute RDID by hand if you want to test it. */ ++ dediprog_do_stuff(); ++#endif ++ ++ return 0; ++} ++ ++/* Leftovers from reverse engineering. Keep for documentation purposes until ++ * completely understood. ++ */ ++int dediprog_do_stuff(void) ++{ ++ char buf[0x4]; ++ /* SPI command processing starts here. */ ++ ++ /* URB 12. Command Send SPI. */ ++ /* URB 13. Command Receive SPI. */ ++ memset(buf, 0, sizeof(buf)); ++ /* JEDEC RDID */ ++ msg_pdbg("Sending RDID\n"); ++ buf[0] = JEDEC_RDID; ++ if (dediprog_spi_send_command(JEDEC_RDID_OUTSIZE, JEDEC_RDID_INSIZE, (unsigned char *)buf, (unsigned char *)buf)) ++ return 1; ++ msg_pdbg("Receiving response: "); ++ print_hex(buf, JEDEC_RDID_INSIZE); ++#if 0 ++ /* URB 14-27 are more SPI commands. */ ++ /* URB 28. Command Set SPI Voltage. */ ++ if (dediprog_set_spi_voltage(0x0)) ++ return 1; ++ /* URB 29-38. Command F, unsuccessful wait. */ ++ if (dediprog_command_f(544)) ++ return 1; ++ /* URB 39. Command Set SPI Voltage. */ ++ if (dediprog_set_spi_voltage(0x10)) ++ return 1; ++ /* URB 40. Command Set SPI Speed. */ ++ if (dediprog_set_spi_speed(0x2)) ++ return 1; ++ /* URB 41 is just URB 28. */ ++ /* URB 42,44,46,48,51,53 is just URB 8. */ ++ /* URB 43,45,47,49,52,54 is just URB 9. */ ++ /* URB 50 is just URB 6/7. */ ++ /* URB 55-131 is just URB 29-38. (wait unsuccessfully for 4695 (maybe 4751) ms)*/ ++ /* URB 132,134 is just URB 6/7. */ ++ /* URB 133 is just URB 29-38. */ ++ /* URB 135 is just URB 8. */ ++ /* URB 136 is just URB 9. */ ++ /* URB 137 is just URB 11. */ ++ ++ /* Command I is probably Start Bulk Read. Data is u16 blockcount, u16 blocksize. */ ++ /* Command J is probably Start Bulk Write. Data is u16 blockcount, u16 blocksize. */ ++ /* Bulk transfer sizes for Command I/J are always 512 bytes, rest is filled with 0xff. */ ++#endif ++ ++ msg_pinfo("All probes will fail because this driver is not hooked up " ++ "to the SPI infrastructure yet."); ++ return 0; ++} ++ ++int dediprog_shutdown(void) ++{ ++ msg_pspew("%s\n", __func__); ++ ++ /* URB 28. Command Set SPI Voltage to 0. */ ++ if (dediprog_set_spi_voltage(0x0)) ++ return 1; ++ ++ if (usb_close(dediprog_handle)) { ++ msg_perr("Couldn't close USB device!\n"); ++ return 1; ++ } ++ return 0; ++} +diff --git a/dmi.c b/dmi.c +new file mode 100644 +index 0000000..d165e9f +--- /dev/null ++++ b/dmi.c +@@ -0,0 +1,168 @@ ++/* ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2009,2010 Michael Karcher ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++ ++#include "flash.h" ++ ++enum dmi_strings { ++ DMI_SYS_MANUFACTURER, ++ DMI_SYS_PRODUCT, ++ DMI_SYS_VERSION, ++ DMI_BB_MANUFACTURER, ++ DMI_BB_PRODUCT, ++ DMI_BB_VERSION, ++ DMI_ID_INVALID /* This must always be the last entry */ ++}; ++ ++/* The short_id for baseboard starts with "m" as in mainboard to leave ++ "b" available for BIOS */ ++struct { ++ const char *dmidecode_name; ++ char short_id[3]; ++} dmi_properties[DMI_ID_INVALID] = { ++ {"system-manufacturer", "sm"}, ++ {"system-product-name", "sp"}, ++ {"system-version", "sv"}, ++ {"baseboard-manufacturer", "mm"}, ++ {"baseboard-product-name", "mp"}, ++ {"baseboard-version", "mv"} ++}; ++ ++#define DMI_COMMAND_LEN_MAX 260 ++const char *dmidecode_command = "dmidecode"; ++ ++int has_dmi_support = 0; ++char *dmistrings[DMI_ID_INVALID]; ++ ++/* strings longer than 4096 in DMI are just insane */ ++#define DMI_MAX_ANSWER_LEN 4096 ++ ++void dmi_init(void) ++{ ++ FILE *dmidecode_pipe; ++ int i; ++ char *answerbuf = malloc(DMI_MAX_ANSWER_LEN); ++ if(!answerbuf) ++ { ++ fprintf(stderr, "DMI: couldn't alloc answer buffer\n"); ++ return; ++ } ++ for (i = 0; i < DMI_ID_INVALID; i++) ++ { ++ char commandline[DMI_COMMAND_LEN_MAX+40]; ++ snprintf(commandline, sizeof(commandline), ++ "%s -s %s", dmidecode_command, ++ dmi_properties[i].dmidecode_name); ++ dmidecode_pipe = popen(commandline, "r"); ++ if (!dmidecode_pipe) ++ { ++ printf_debug("DMI pipe open error\n"); ++ goto out_free; ++ } ++ if (!fgets(answerbuf, DMI_MAX_ANSWER_LEN, dmidecode_pipe) && ++ ferror(dmidecode_pipe)) ++ { ++ printf_debug("DMI pipe read error\n"); ++ pclose(dmidecode_pipe); ++ goto out_free; ++ } ++ /* Toss all output above DMI_MAX_ANSWER_LEN away to prevent ++ deadlock on pclose. */ ++ while (!feof(dmidecode_pipe)) ++ getc(dmidecode_pipe); ++ if (pclose(dmidecode_pipe) != 0) ++ { ++ printf_debug("DMI pipe close error\n"); ++ goto out_free; ++ } ++ ++ /* chomp trailing newline */ ++ if (answerbuf[0] != 0 && ++ answerbuf[strlen(answerbuf) - 1] == '\n') ++ answerbuf[strlen(answerbuf) - 1] = 0; ++ printf_debug("DMI string %d: \"%s\"\n", i, answerbuf); ++ dmistrings[i] = strdup(answerbuf); ++ } ++ has_dmi_support = 1; ++out_free: ++ free(answerbuf); ++} ++ ++/** ++ * Does an substring/prefix/postfix/whole-string match. ++ * ++ * The pattern is matched as-is. The only metacharacters supported are '^' ++ * at the beginning and '$' at the end. So you can look for "^prefix", ++ * "suffix$", "substring" or "^complete string$". ++ * ++ * @param value The string to check. ++ * @param pattern The pattern. ++ * @return Nonzero if pattern matches. ++ */ ++static int dmi_compare(const char *value, const char *pattern) ++{ ++ int anchored = 0; ++ int patternlen; ++ printf_debug("matching %s against %s\n", value, pattern); ++ /* The empty string is part of all strings */ ++ if (pattern[0] == 0) ++ return 1; ++ ++ if (pattern[0] == '^') { ++ anchored = 1; ++ pattern++; ++ } ++ ++ patternlen = strlen(pattern); ++ if (pattern[patternlen - 1] == '$') { ++ int valuelen = strlen(value); ++ patternlen--; ++ if(patternlen > valuelen) ++ return 0; ++ ++ /* full string match: require same length */ ++ if(anchored && (valuelen != patternlen)) ++ return 0; ++ ++ /* start character to make ends match */ ++ value += valuelen - patternlen; ++ anchored = 1; ++ } ++ ++ if (anchored) ++ return strncmp(value, pattern, patternlen) == 0; ++ else ++ return strstr(value, pattern) != NULL; ++} ++ ++int dmi_match(const char *pattern) ++{ ++ int i; ++ if (!has_dmi_support) ++ return 0; ++ ++ for (i = 0;i < DMI_ID_INVALID; i++) ++ return dmi_compare(dmistrings[i], pattern); ++ ++ return 0; ++} +diff --git a/drkaiser.c b/drkaiser.c +new file mode 100644 +index 0000000..f13c13e +--- /dev/null ++++ b/drkaiser.c +@@ -0,0 +1,79 @@ ++/* ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2009 Joerg Fischer ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include "flash.h" ++ ++#define PCI_VENDOR_ID_DRKAISER 0x1803 ++ ++#define PCI_MAGIC_DRKAISER_ADDR 0x50 ++#define PCI_MAGIC_DRKAISER_VALUE 0xa971 ++ ++struct pcidev_status drkaiser_pcidev[] = { ++ {0x1803, 0x5057, PCI_OK, "Dr. Kaiser", "PC-Waechter (Actel FPGA)"}, ++ {}, ++}; ++ ++uint8_t *drkaiser_bar; ++ ++int drkaiser_init(void) ++{ ++ uint32_t addr; ++ ++ get_io_perms(); ++ pcidev_init(PCI_VENDOR_ID_DRKAISER, PCI_BASE_ADDRESS_2, ++ drkaiser_pcidev, programmer_param); ++ ++ /* Write magic register to enable flash write. */ ++ pci_write_word(pcidev_dev, PCI_MAGIC_DRKAISER_ADDR, ++ PCI_MAGIC_DRKAISER_VALUE); ++ ++ /* TODO: Mask lower bits? How many? 3? 7? */ ++ addr = pci_read_long(pcidev_dev, PCI_BASE_ADDRESS_2) & ~0x03; ++ ++ /* Map 128KB flash memory window. */ ++ drkaiser_bar = physmap("Dr. Kaiser PC-Waechter flash memory", ++ addr, 128 * 1024); ++ ++ buses_supported = CHIP_BUSTYPE_PARALLEL; ++ return 0; ++} ++ ++int drkaiser_shutdown(void) ++{ ++ /* Write protect the flash again. */ ++ pci_write_word(pcidev_dev, PCI_MAGIC_DRKAISER_ADDR, 0); ++ free(programmer_param); ++ pci_cleanup(pacc); ++ release_io_perms(); ++ return 0; ++}; ++ ++void drkaiser_chip_writeb(uint8_t val, chipaddr addr) ++{ ++ mmio_writeb(val, drkaiser_bar + addr); ++} ++ ++uint8_t drkaiser_chip_readb(const chipaddr addr) ++{ ++ return mmio_readb(drkaiser_bar + addr); ++} +diff --git a/dummyflasher.c b/dummyflasher.c +index 903f88b..0f62d16 100644 +--- a/dummyflasher.c ++++ b/dummyflasher.c +@@ -27,7 +27,7 @@ + int dummy_init(void) + { + int i; +- printf_debug("%s\n", __func__); ++ msg_pspew("%s\n", __func__); + + /* "all" is equivalent to specifying no type. */ + if (programmer_param && (!strcmp(programmer_param, "all"))) { +@@ -43,95 +43,95 @@ int dummy_init(void) + buses_supported = CHIP_BUSTYPE_NONE; + if (strstr(programmer_param, "parallel")) { + buses_supported |= CHIP_BUSTYPE_PARALLEL; +- printf_debug("Enabling support for %s flash.\n", "parallel"); ++ msg_pdbg("Enabling support for %s flash.\n", "parallel"); + } + if (strstr(programmer_param, "lpc")) { + buses_supported |= CHIP_BUSTYPE_LPC; +- printf_debug("Enabling support for %s flash.\n", "LPC"); ++ msg_pdbg("Enabling support for %s flash.\n", "LPC"); + } + if (strstr(programmer_param, "fwh")) { + buses_supported |= CHIP_BUSTYPE_FWH; +- printf_debug("Enabling support for %s flash.\n", "FWH"); ++ msg_pdbg("Enabling support for %s flash.\n", "FWH"); + } + if (strstr(programmer_param, "spi")) { + buses_supported |= CHIP_BUSTYPE_SPI; + spi_controller = SPI_CONTROLLER_DUMMY; +- printf_debug("Enabling support for %s flash.\n", "SPI"); ++ msg_pdbg("Enabling support for %s flash.\n", "SPI"); + } + if (buses_supported == CHIP_BUSTYPE_NONE) +- printf_debug("Support for all flash bus types disabled.\n"); ++ msg_pdbg("Support for all flash bus types disabled.\n"); + free(programmer_param); + return 0; + } + + int dummy_shutdown(void) + { +- printf_debug("%s\n", __func__); ++ msg_pspew("%s\n", __func__); + return 0; + } + + void *dummy_map(const char *descr, unsigned long phys_addr, size_t len) + { +- printf_debug("%s: Mapping %s, 0x%lx bytes at 0x%08lx\n", +- __func__, descr, (unsigned long)len, phys_addr); ++ msg_pspew("%s: Mapping %s, 0x%lx bytes at 0x%08lx\n", ++ __func__, descr, (unsigned long)len, phys_addr); + return (void *)phys_addr; + } + + void dummy_unmap(void *virt_addr, size_t len) + { +- printf_debug("%s: Unmapping 0x%lx bytes at %p\n", +- __func__, (unsigned long)len, virt_addr); ++ msg_pspew("%s: Unmapping 0x%lx bytes at %p\n", ++ __func__, (unsigned long)len, virt_addr); + } + + void dummy_chip_writeb(uint8_t val, chipaddr addr) + { +- printf_debug("%s: addr=0x%lx, val=0x%02x\n", __func__, addr, val); ++ msg_pspew("%s: addr=0x%lx, val=0x%02x\n", __func__, addr, val); + } + + void dummy_chip_writew(uint16_t val, chipaddr addr) + { +- printf_debug("%s: addr=0x%lx, val=0x%04x\n", __func__, addr, val); ++ msg_pspew("%s: addr=0x%lx, val=0x%04x\n", __func__, addr, val); + } + + void dummy_chip_writel(uint32_t val, chipaddr addr) + { +- printf_debug("%s: addr=0x%lx, val=0x%08x\n", __func__, addr, val); ++ msg_pspew("%s: addr=0x%lx, val=0x%08x\n", __func__, addr, val); + } + + void dummy_chip_writen(uint8_t *buf, chipaddr addr, size_t len) + { + size_t i; +- printf_debug("%s: addr=0x%lx, len=0x%08lx, writing data (hex):", +- __func__, addr, (unsigned long)len); ++ msg_pspew("%s: addr=0x%lx, len=0x%08lx, writing data (hex):", ++ __func__, addr, (unsigned long)len); + for (i = 0; i < len; i++) { + if ((i % 16) == 0) +- printf_debug("\n"); +- printf_debug("%02x ", buf[i]) ++ msg_pspew("\n"); ++ msg_pspew("%02x ", buf[i]); + } + } + + uint8_t dummy_chip_readb(const chipaddr addr) + { +- printf_debug("%s: addr=0x%lx, returning 0xff\n", __func__, addr); ++ msg_pspew("%s: addr=0x%lx, returning 0xff\n", __func__, addr); + return 0xff; + } + + uint16_t dummy_chip_readw(const chipaddr addr) + { +- printf_debug("%s: addr=0x%lx, returning 0xffff\n", __func__, addr); ++ msg_pspew("%s: addr=0x%lx, returning 0xffff\n", __func__, addr); + return 0xffff; + } + + uint32_t dummy_chip_readl(const chipaddr addr) + { +- printf_debug("%s: addr=0x%lx, returning 0xffffffff\n", __func__, addr); ++ msg_pspew("%s: addr=0x%lx, returning 0xffffffff\n", __func__, addr); + return 0xffffffff; + } + + void dummy_chip_readn(uint8_t *buf, const chipaddr addr, size_t len) + { +- printf_debug("%s: addr=0x%lx, len=0x%lx, returning array of 0xff\n", +- __func__, addr, (unsigned long)len); ++ msg_pspew("%s: addr=0x%lx, len=0x%lx, returning array of 0xff\n", ++ __func__, addr, (unsigned long)len); + memset(buf, 0xff, len); + return; + } +@@ -141,18 +141,18 @@ int dummy_spi_send_command(unsigned int writecnt, unsigned int readcnt, + { + int i; + +- printf_debug("%s:", __func__); ++ msg_pspew("%s:", __func__); + +- printf_debug(" writing %u bytes:", writecnt); ++ msg_pspew(" writing %u bytes:", writecnt); + for (i = 0; i < writecnt; i++) +- printf_debug(" 0x%02x", writearr[i]); ++ msg_pspew(" 0x%02x", writearr[i]); + +- printf_debug(" reading %u bytes:", readcnt); ++ msg_pspew(" reading %u bytes:", readcnt); + for (i = 0; i < readcnt; i++) { +- printf_debug(" 0xff"); ++ msg_pspew(" 0xff"); + readarr[i] = 0xff; + } + +- printf_debug("\n"); ++ msg_pspew("\n"); + return 0; + } +diff --git a/en29f002a.c b/en29f002a.c +index b89eb31..020df32 100644 +--- a/en29f002a.c ++++ b/en29f002a.c +@@ -47,7 +47,7 @@ int probe_en29f512(struct flashchip *flash) + chip_writeb(0x55, bios + 0x2AA); + chip_writeb(0xF0, bios + 0x555); + +- printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __FUNCTION__, id1, id2); ++ printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2); + + if (id1 == flash->manufacture_id && id2 == flash->model_id) + return 1; +@@ -80,46 +80,10 @@ int probe_en29f002a(struct flashchip *flash) + chip_writeb(0x55, bios + 0xAAA); + chip_writeb(0xF0, bios + 0x555); + +- printf_debug("%s: id1 0x%x, id2 0x%x\n", __FUNCTION__, id1, id2); ++ printf_debug("%s: id1 0x%x, id2 0x%x\n", __func__, id1, id2); + + if (id1 == flash->manufacture_id && id2 == flash->model_id) + return 1; + + return 0; + } +- +-/* The EN29F002 chip needs repeated single byte writing, no block writing. */ +-int write_en29f002a(struct flashchip *flash, uint8_t *buf) +-{ +- int i; +- int total_size = flash->total_size * 1024; +- chipaddr bios = flash->virtual_memory; +- chipaddr dst = bios; +- +- //chip_writeb(0xF0, bios); +- programmer_delay(10); +- if (erase_chip_jedec(flash)) { +- fprintf(stderr, "ERASE FAILED!\n"); +- return -1; +- } +- +- printf("Programming page: "); +- for (i = 0; i < total_size; i++) { +- /* write to the sector */ +- if ((i & 0xfff) == 0) +- printf("address: 0x%08lx", (unsigned long)i); +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0xA0, bios + 0x5555); +- chip_writeb(*buf++, dst++); +- +- /* wait for Toggle bit ready */ +- toggle_ready_jedec(dst); +- +- if ((i & 0xfff) == 0) +- printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); +- } +- +- printf("\n"); +- return 0; +-} +diff --git a/flash.h b/flash.h +index d3e2e30..d8f02bb 100644 +--- a/flash.h ++++ b/flash.h +@@ -24,74 +24,52 @@ + #ifndef __FLASH_H__ + #define __FLASH_H__ 1 + +-#if defined(__GLIBC__) +-#include +-#endif + #include + #include + #include +-#include +- +-/* for iopl and outb under Solaris */ +-#if defined (__sun) && (defined(__i386) || defined(__amd64)) +-#include +-#include +-#include +-#include +-#endif +- +-#if (defined(__MACH__) && defined(__APPLE__)) +-#define __DARWIN__ +-#endif +- +-#if defined(__FreeBSD__) || defined(__DragonFly__) +- #include +- #define off64_t off_t +- #define lseek64 lseek +- #define OUTB(x, y) do { u_int tmp = (y); outb(tmp, (x)); } while (0) +- #define OUTW(x, y) do { u_int tmp = (y); outw(tmp, (x)); } while (0) +- #define OUTL(x, y) do { u_int tmp = (y); outl(tmp, (x)); } while (0) +- #define INB(x) __extension__ ({ u_int tmp = (x); inb(tmp); }) +- #define INW(x) __extension__ ({ u_int tmp = (x); inw(tmp); }) +- #define INL(x) __extension__ ({ u_int tmp = (x); inl(tmp); }) +-#else +-#if defined(__DARWIN__) +- #include +- #define off64_t off_t +- #define lseek64 lseek +-#endif +-#if defined (__sun) && (defined(__i386) || defined(__amd64)) +- /* Note different order for outb */ +- #define OUTB(x,y) outb(y, x) +- #define OUTW(x,y) outw(y, x) +- #define OUTL(x,y) outl(y, x) +- #define INB inb +- #define INW inw +- #define INL inl +-#else +- #define OUTB outb +- #define OUTW outw +- #define OUTL outl +- #define INB inb +- #define INW inw +- #define INL inl +-#endif ++#include "hwaccess.h" ++#ifdef _WIN32 ++#include ++#undef min ++#undef max + #endif + + typedef unsigned long chipaddr; + + enum programmer { ++#if INTERNAL_SUPPORT == 1 + PROGRAMMER_INTERNAL, ++#endif ++#if DUMMY_SUPPORT == 1 + PROGRAMMER_DUMMY, ++#endif ++#if NIC3COM_SUPPORT == 1 + PROGRAMMER_NIC3COM, ++#endif ++#if GFXNVIDIA_SUPPORT == 1 ++ PROGRAMMER_GFXNVIDIA, ++#endif ++#if DRKAISER_SUPPORT == 1 ++ PROGRAMMER_DRKAISER, ++#endif ++#if SATASII_SUPPORT == 1 + PROGRAMMER_SATASII, ++#endif ++#if INTERNAL_SUPPORT == 1 + PROGRAMMER_IT87SPI, ++#endif + #if FT2232_SPI_SUPPORT == 1 + PROGRAMMER_FT2232SPI, + #endif + #if SERPROG_SUPPORT == 1 + PROGRAMMER_SERPROG, + #endif ++#if BUSPIRATE_SPI_SUPPORT == 1 ++ PROGRAMMER_BUSPIRATESPI, ++#endif ++#if DEDIPROG_SUPPORT == 1 ++ PROGRAMMER_DEDIPROG, ++#endif + PROGRAMMER_INVALID /* This must always be the last entry. */ + }; + +@@ -136,6 +114,21 @@ uint32_t chip_readl(const chipaddr addr); + void chip_readn(uint8_t *buf, const chipaddr addr, size_t len); + void programmer_delay(int usecs); + ++enum bitbang_spi_master { ++ BITBANG_SPI_INVALID /* This must always be the last entry. */ ++}; ++ ++extern const int bitbang_spi_master_count; ++ ++extern enum bitbang_spi_master bitbang_spi_master; ++ ++struct bitbang_spi_master_entry { ++ void (*set_cs) (int val); ++ void (*set_sck) (int val); ++ void (*set_mosi) (int val); ++ int (*get_miso) (void); ++}; ++ + #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + + enum chipbustype { +@@ -148,6 +141,28 @@ enum chipbustype { + CHIP_BUSTYPE_UNKNOWN = CHIP_BUSTYPE_PARALLEL | CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI, + }; + ++/* ++ * How many different contiguous runs of erase blocks with one size each do ++ * we have for a given erase function? ++ */ ++#define NUM_ERASEREGIONS 5 ++ ++/* ++ * How many different erase functions do we have per chip? ++ */ ++#define NUM_ERASEFUNCTIONS 5 ++ ++#define FEATURE_REGISTERMAP (1 << 0) ++#define FEATURE_BYTEWRITES (1 << 1) ++#define FEATURE_LONG_RESET (0 << 4) ++#define FEATURE_SHORT_RESET (1 << 4) ++#define FEATURE_EITHER_RESET FEATURE_LONG_RESET ++#define FEATURE_ADDR_FULL (0 << 2) ++#define FEATURE_ADDR_MASK (3 << 2) ++#define FEATURE_ADDR_2AA (1 << 2) ++#define FEATURE_ADDR_AAA (2 << 2) ++#define FEATURE_ADDR_SHIFTED 0 ++ + struct flashchip { + const char *vendor; + const char *name; +@@ -164,6 +179,7 @@ struct flashchip { + + int total_size; + int page_size; ++ int feature_bits; + + /* + * Indicate if flashrom has been tested with this flash chip and if +@@ -176,6 +192,19 @@ struct flashchip { + /* Delay after "enter/exit ID mode" commands in microseconds. */ + int probe_timing; + int (*erase) (struct flashchip *flash); ++ ++ /* ++ * Erase blocks and associated erase function. Any chip erase function ++ * is stored as chip-sized virtual block together with said function. ++ */ ++ struct block_eraser { ++ struct eraseblock{ ++ unsigned int size; /* Eraseblock size */ ++ unsigned int count; /* Number of contiguous blocks with that size */ ++ } eraseblocks[NUM_ERASEREGIONS]; ++ int (*block_erase) (struct flashchip *flash, unsigned int blockaddr, unsigned int blocklen); ++ } block_erasers[NUM_ERASEFUNCTIONS]; ++ + int (*write) (struct flashchip *flash, uint8_t *buf); + int (*read) (struct flashchip *flash, uint8_t *buf, int start, int len); + +@@ -192,6 +221,7 @@ struct flashchip { + #define TEST_OK_WRITE (1 << 3) + #define TEST_OK_PR (TEST_OK_PROBE | TEST_OK_READ) + #define TEST_OK_PRE (TEST_OK_PROBE | TEST_OK_READ | TEST_OK_ERASE) ++#define TEST_OK_PRW (TEST_OK_PROBE | TEST_OK_READ | TEST_OK_WRITE) + #define TEST_OK_PREW (TEST_OK_PROBE | TEST_OK_READ | TEST_OK_ERASE | TEST_OK_WRITE) + #define TEST_OK_MASK 0x0f + +@@ -214,6 +244,7 @@ struct flashchip { + + extern struct flashchip flashchips[]; + ++#if INTERNAL_SUPPORT == 1 + struct penable { + uint16_t vendor_id; + uint16_t device_id; +@@ -240,6 +271,9 @@ struct board_pciid_enable { + uint16_t second_card_vendor; + uint16_t second_card_device; + ++ /* Pattern to match DMI entries */ ++ const char *dmi_pattern; ++ + /* The vendor / part name from the coreboot table. */ + const char *lb_vendor; + const char *lb_part; +@@ -247,6 +281,7 @@ struct board_pciid_enable { + const char *vendor_name; + const char *board_name; + ++ int max_rom_decode_parallel; + int (*enable) (const char *name); + }; + +@@ -261,18 +296,20 @@ extern const struct board_info boards_ok[]; + extern const struct board_info boards_bad[]; + extern const struct board_info laptops_ok[]; + extern const struct board_info laptops_bad[]; ++#endif + + /* udelay.c */ + void myusec_delay(int usecs); + void myusec_calibrate_delay(void); ++void internal_delay(int usecs); + ++#if NEED_PCI == 1 + /* pcidev.c */ + #define PCI_OK 0 + #define PCI_NT 1 /* Not tested */ + + extern uint32_t io_base_addr; + extern struct pci_access *pacc; +-extern struct pci_filter filter; + extern struct pci_dev *pcidev_dev; + struct pcidev_status { + uint16_t vendor_id; +@@ -281,16 +318,17 @@ struct pcidev_status { + const char *vendor_name; + const char *device_name; + }; +-uint32_t pcidev_validate(struct pci_dev *dev, struct pcidev_status *devs); +-uint32_t pcidev_init(uint16_t vendor_id, struct pcidev_status *devs, char *pcidev_bdf); ++uint32_t pcidev_validate(struct pci_dev *dev, uint32_t bar, struct pcidev_status *devs); ++uint32_t pcidev_init(uint16_t vendor_id, uint32_t bar, struct pcidev_status *devs, char *pcidev_bdf); ++#endif + + /* print.c */ + char *flashbuses_to_text(enum chipbustype bustype); +-void print_supported_chips(void); +-void print_supported_chipsets(void); +-void print_supported_boards(void); ++void print_supported(void); ++#if (NIC3COM_SUPPORT == 1) || (GFXNVIDIA_SUPPORT == 1) || (DRKAISER_SUPPORT == 1) || (SATASII_SUPPORT == 1) + void print_supported_pcidevs(struct pcidev_status *devs); +-void print_wiki_tables(void); ++#endif ++void print_supported_wiki(void); + + /* board_enable.c */ + void w836xx_ext_enter(uint16_t port); +@@ -301,39 +339,46 @@ void sio_mask(uint16_t port, uint8_t reg, uint8_t data, uint8_t mask); + int board_flash_enable(const char *vendor, const char *part); + + /* chipset_enable.c */ +-extern enum chipbustype buses_supported; + int chipset_flash_enable(void); + +-extern unsigned long flashbase; +- + /* physmap.c */ + void *physmap(const char *descr, unsigned long phys_addr, size_t len); ++void *physmap_try_ro(const char *descr, unsigned long phys_addr, size_t len); + void physunmap(void *virt_addr, size_t len); + int setup_cpu_msr(int cpu); + void cleanup_cpu_msr(void); +-#if !defined(__DARWIN__) && !defined(__FreeBSD__) && !defined(__DragonFly__) +-typedef struct { uint32_t hi, lo; } msr_t; +-msr_t rdmsr(int addr); +-int wrmsr(int addr, msr_t msr); +-#endif +-#if defined(__FreeBSD__) || defined(__DragonFly__) +-/* FreeBSD already has conflicting definitions for wrmsr/rdmsr. */ +-#undef rdmsr +-#undef wrmsr +-#define rdmsr freebsd_rdmsr +-#define wrmsr freebsd_wrmsr +-typedef struct { uint32_t hi, lo; } msr_t; +-msr_t freebsd_rdmsr(int addr); +-int freebsd_wrmsr(int addr, msr_t msr); +-#endif ++ ++/* cbtable.c */ ++void lb_vendor_dev_from_string(char *boardstring); ++int coreboot_init(void); ++extern char *lb_part, *lb_vendor; ++extern int partvendor_from_cbtable; ++ ++/* dmi.c */ ++extern int has_dmi_support; ++void dmi_init(void); ++int dmi_match(const char *pattern); + + /* internal.c */ ++#if NEED_PCI == 1 ++struct superio { ++ uint16_t vendor; ++ uint16_t port; ++ uint16_t model; ++}; ++extern struct superio superio; ++#define SUPERIO_VENDOR_NONE 0x0 ++#define SUPERIO_VENDOR_ITE 0x1 + struct pci_dev *pci_dev_find_filter(struct pci_filter filter); ++struct pci_dev *pci_dev_find_vendorclass(uint16_t vendor, uint16_t class); + struct pci_dev *pci_dev_find(uint16_t vendor, uint16_t device); + struct pci_dev *pci_card_find(uint16_t vendor, uint16_t device, + uint16_t card_vendor, uint16_t card_device); ++#endif + void get_io_perms(void); + void release_io_perms(void); ++#if INTERNAL_SUPPORT == 1 ++void probe_superio(void); + int internal_init(void); + int internal_shutdown(void); + void internal_chip_writeb(uint8_t val, chipaddr addr); +@@ -343,28 +388,29 @@ uint8_t internal_chip_readb(const chipaddr addr); + uint16_t internal_chip_readw(const chipaddr addr); + uint32_t internal_chip_readl(const chipaddr addr); + void internal_chip_readn(uint8_t *buf, const chipaddr addr, size_t len); ++#endif + void mmio_writeb(uint8_t val, void *addr); + void mmio_writew(uint16_t val, void *addr); + void mmio_writel(uint32_t val, void *addr); + uint8_t mmio_readb(void *addr); + uint16_t mmio_readw(void *addr); + uint32_t mmio_readl(void *addr); +-void internal_delay(int usecs); +-int fallback_shutdown(void); ++ ++/* programmer.c */ ++int noop_shutdown(void); + void *fallback_map(const char *descr, unsigned long phys_addr, size_t len); + void fallback_unmap(void *virt_addr, size_t len); +-void fallback_chip_writeb(uint8_t val, chipaddr addr); ++uint8_t noop_chip_readb(const chipaddr addr); ++void noop_chip_writeb(uint8_t val, chipaddr addr); + void fallback_chip_writew(uint16_t val, chipaddr addr); + void fallback_chip_writel(uint32_t val, chipaddr addr); + void fallback_chip_writen(uint8_t *buf, chipaddr addr, size_t len); + uint16_t fallback_chip_readw(const chipaddr addr); + uint32_t fallback_chip_readl(const chipaddr addr); + void fallback_chip_readn(uint8_t *buf, const chipaddr addr, size_t len); +-#if defined(__FreeBSD__) || defined(__DragonFly__) +-extern int io_fd; +-#endif + + /* dummyflasher.c */ ++#if DUMMY_SUPPORT == 1 + int dummy_init(void); + int dummy_shutdown(void); + void *dummy_map(const char *descr, unsigned long phys_addr, size_t len); +@@ -379,20 +425,43 @@ uint32_t dummy_chip_readl(const chipaddr addr); + void dummy_chip_readn(uint8_t *buf, const chipaddr addr, size_t len); + int dummy_spi_send_command(unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr); ++#endif + + /* nic3com.c */ ++#if NIC3COM_SUPPORT == 1 + int nic3com_init(void); + int nic3com_shutdown(void); + void nic3com_chip_writeb(uint8_t val, chipaddr addr); + uint8_t nic3com_chip_readb(const chipaddr addr); + extern struct pcidev_status nics_3com[]; ++#endif ++ ++/* gfxnvidia.c */ ++#if GFXNVIDIA_SUPPORT == 1 ++int gfxnvidia_init(void); ++int gfxnvidia_shutdown(void); ++void gfxnvidia_chip_writeb(uint8_t val, chipaddr addr); ++uint8_t gfxnvidia_chip_readb(const chipaddr addr); ++extern struct pcidev_status gfx_nvidia[]; ++#endif ++ ++/* drkaiser.c */ ++#if DRKAISER_SUPPORT == 1 ++int drkaiser_init(void); ++int drkaiser_shutdown(void); ++void drkaiser_chip_writeb(uint8_t val, chipaddr addr); ++uint8_t drkaiser_chip_readb(const chipaddr addr); ++extern struct pcidev_status drkaiser_pcidev[]; ++#endif + + /* satasii.c */ ++#if SATASII_SUPPORT == 1 + int satasii_init(void); + int satasii_shutdown(void); + void satasii_chip_writeb(uint8_t val, chipaddr addr); + uint8_t satasii_chip_readb(const chipaddr addr); + extern struct pcidev_status satas_sii[]; ++#endif + + /* ft2232_spi.c */ + #define FTDI_FT2232H 0x6010 +@@ -402,46 +471,116 @@ int ft2232_spi_send_command(unsigned int writecnt, unsigned int readcnt, const u + int ft2232_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len); + int ft2232_spi_write_256(struct flashchip *flash, uint8_t *buf); + ++/* bitbang_spi.c */ ++extern int bitbang_spi_half_period; ++extern const struct bitbang_spi_master_entry bitbang_spi_master_table[]; ++int bitbang_spi_init(void); ++int bitbang_spi_send_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr); ++int bitbang_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len); ++int bitbang_spi_write_256(struct flashchip *flash, uint8_t *buf); ++ ++/* buspirate_spi.c */ ++struct buspirate_spispeeds { ++ const char *name; ++ const int speed; ++}; ++int buspirate_spi_init(void); ++int buspirate_spi_shutdown(void); ++int buspirate_spi_send_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr); ++int buspirate_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len); ++ ++/* dediprog.c */ ++int dediprog_init(void); ++int dediprog_shutdown(void); ++int dediprog_spi_send_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr); ++int dediprog_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len); ++ + /* flashrom.c */ ++extern enum chipbustype buses_supported; ++struct decode_sizes { ++ uint32_t parallel; ++ uint32_t lpc; ++ uint32_t fwh; ++ uint32_t spi; ++}; ++extern struct decode_sizes max_rom_decode; + extern char *programmer_param; ++extern unsigned long flashbase; + extern int verbose; + extern const char *flashrom_version; ++extern char *chip_to_probe; + #define printf_debug(x...) { if (verbose) printf(x); } + void map_flash_registers(struct flashchip *flash); + int read_memmapped(struct flashchip *flash, uint8_t *buf, int start, int len); ++int erase_flash(struct flashchip *flash); ++struct flashchip *probe_flash(struct flashchip *first_flash, int force); ++int read_flash(struct flashchip *flash, char *filename); ++void check_chip_supported(struct flashchip *flash); ++int check_max_decode(enum chipbustype buses, uint32_t size); + int min(int a, int b); + int max(int a, int b); ++char *extract_param(char **haystack, char *needle, char *delim); + int check_erased_range(struct flashchip *flash, int start, int len); + int verify_range(struct flashchip *flash, uint8_t *cmpbuf, int start, int len, char *message); + char *strcat_realloc(char *dest, const char *src); ++void print_version(void); ++int selfcheck(void); ++int doit(struct flashchip *flash, int force, char *filename, int read_it, int write_it, int erase_it, int verify_it); + + #define OK 0 + #define NT 1 /* Not tested */ + ++/* cli_output.c */ ++int print(int type, const char *fmt, ...); ++#define MSG_ERROR 0 ++#define MSG_INFO 1 ++#define MSG_DEBUG 2 ++#define MSG_BARF 3 ++#define msg_gerr(...) print(MSG_ERROR, __VA_ARGS__) /* general errors */ ++#define msg_perr(...) print(MSG_ERROR, __VA_ARGS__) /* programmer errors */ ++#define msg_cerr(...) print(MSG_ERROR, __VA_ARGS__) /* chip errors */ ++#define msg_ginfo(...) print(MSG_INFO, __VA_ARGS__) /* general info */ ++#define msg_pinfo(...) print(MSG_INFO, __VA_ARGS__) /* programmer info */ ++#define msg_cinfo(...) print(MSG_INFO, __VA_ARGS__) /* chip info */ ++#define msg_gdbg(...) print(MSG_DEBUG, __VA_ARGS__) /* general debug */ ++#define msg_pdbg(...) print(MSG_DEBUG, __VA_ARGS__) /* programmer debug */ ++#define msg_cdbg(...) print(MSG_DEBUG, __VA_ARGS__) /* chip debug */ ++#define msg_gspew(...) print(MSG_BARF, __VA_ARGS__) /* general debug barf */ ++#define msg_pspew(...) print(MSG_BARF, __VA_ARGS__) /* programmer debug barf */ ++#define msg_cspew(...) print(MSG_BARF, __VA_ARGS__) /* chip debug barf */ ++ ++/* cli_classic.c */ ++int cli_classic(int argc, char *argv[]); ++ + /* layout.c */ + int show_id(uint8_t *bios, int size, int force); + int read_romlayout(char *name); + int find_romentry(char *name); + int handle_romentries(uint8_t *buffer, struct flashchip *flash); + +-/* cbtable.c */ +-int coreboot_init(void); +-extern char *lb_part, *lb_vendor; +-extern int partvendor_from_cbtable; +- + /* spi.c */ + enum spi_controller { + SPI_CONTROLLER_NONE, ++#if INTERNAL_SUPPORT == 1 + SPI_CONTROLLER_ICH7, + SPI_CONTROLLER_ICH9, + SPI_CONTROLLER_IT87XX, + SPI_CONTROLLER_SB600, + SPI_CONTROLLER_VIA, + SPI_CONTROLLER_WBSIO, ++#endif + #if FT2232_SPI_SUPPORT == 1 + SPI_CONTROLLER_FT2232, + #endif ++#if DUMMY_SUPPORT == 1 + SPI_CONTROLLER_DUMMY, ++#endif ++#if BUSPIRATE_SPI_SUPPORT == 1 ++ SPI_CONTROLLER_BUSPIRATE, ++#endif ++#if DEDIPROG_SUPPORT == 1 ++ SPI_CONTROLLER_DEDIPROG, ++#endif + SPI_CONTROLLER_INVALID /* This must always be the last entry. */ + }; + extern const int spi_programmer_count; +@@ -454,7 +593,7 @@ struct spi_command { + struct spi_programmer { + int (*command)(unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr); +- int (*multicommand)(struct spi_command *spicommands); ++ int (*multicommand)(struct spi_command *cmds); + + /* Optimized functions for this programmer */ + int (*read)(struct flashchip *flash, uint8_t *buf, int start, int len); +@@ -464,56 +603,13 @@ struct spi_programmer { + extern enum spi_controller spi_controller; + extern const struct spi_programmer spi_programmer[]; + extern void *spibar; +-int probe_spi_rdid(struct flashchip *flash); +-int probe_spi_rdid4(struct flashchip *flash); +-int probe_spi_rems(struct flashchip *flash); +-int probe_spi_res(struct flashchip *flash); + int spi_send_command(unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr); +-int spi_send_multicommand(struct spi_command *spicommands); +-int spi_write_enable(void); +-int spi_write_disable(void); +-int spi_chip_erase_60(struct flashchip *flash); +-int spi_chip_erase_c7(struct flashchip *flash); +-int spi_chip_erase_60_c7(struct flashchip *flash); +-int spi_chip_erase_d8(struct flashchip *flash); +-int spi_block_erase_20(struct flashchip *flash, unsigned int addr, unsigned int blocklen); +-int spi_block_erase_52(struct flashchip *flash, unsigned int addr, unsigned int blocklen); +-int spi_block_erase_d8(struct flashchip *flash, unsigned int addr, unsigned int blocklen); +-int spi_block_erase_60(struct flashchip *flash, unsigned int addr, unsigned int blocklen); +-int spi_block_erase_c7(struct flashchip *flash, unsigned int addr, unsigned int blocklen); +-int spi_chip_write_1(struct flashchip *flash, uint8_t *buf); +-int spi_chip_write_256(struct flashchip *flash, uint8_t *buf); +-int spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len); +-uint8_t spi_read_status_register(void); +-int spi_disable_blockprotect(void); +-int spi_byte_program(int addr, uint8_t byte); +-int spi_nbyte_program(int addr, uint8_t *bytes, int len); +-int spi_nbyte_read(int addr, uint8_t *bytes, int len); +-int spi_read_chunked(struct flashchip *flash, uint8_t *buf, int start, int len, int chunksize); +-int spi_aai_write(struct flashchip *flash, uint8_t *buf); +-uint32_t spi_get_valid_read_addr(void); ++int spi_send_multicommand(struct spi_command *cmds); + int default_spi_send_command(unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr); +-int default_spi_send_multicommand(struct spi_command *spicommands); +- +-/* 82802ab.c */ +-int probe_82802ab(struct flashchip *flash); +-int erase_82802ab(struct flashchip *flash); +-int write_82802ab(struct flashchip *flash, uint8_t *buf); +- +-/* am29f040b.c */ +-int probe_29f040b(struct flashchip *flash); +-int erase_29f040b(struct flashchip *flash); +-int write_29f040b(struct flashchip *flash, uint8_t *buf); +- +-/* pm29f002.c */ +-int write_pm29f002(struct flashchip *flash, uint8_t *buf); +- +-/* en29f002a.c */ +-int probe_en29f002a(struct flashchip *flash); +-int erase_en29f002a(struct flashchip *flash); +-int write_en29f002a(struct flashchip *flash, uint8_t *buf); ++int default_spi_send_multicommand(struct spi_command *cmds); ++uint32_t spi_get_valid_read_addr(void); + + /* ichspi.c */ + int ich_init_opcodes(void); +@@ -521,12 +617,13 @@ int ich_spi_send_command(unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr); + int ich_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len); + int ich_spi_write_256(struct flashchip *flash, uint8_t * buf); +-int ich_spi_send_multicommand(struct spi_command *spicommands); ++int ich_spi_send_multicommand(struct spi_command *cmds); + + /* it87spi.c */ + extern uint16_t it8716f_flashport; + void enter_conf_mode_ite(uint16_t port); + void exit_conf_mode_ite(uint16_t port); ++struct superio probe_superio_ite(void); + int it87spi_init(void); + int it87xx_probe_spi_flash(const char *name); + int it8716f_spi_send_command(unsigned int writecnt, unsigned int readcnt, +@@ -541,96 +638,6 @@ int sb600_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len); + int sb600_spi_write_1(struct flashchip *flash, uint8_t *buf); + extern uint8_t *sb600_spibar; + +-/* jedec.c */ +-uint8_t oddparity(uint8_t val); +-void toggle_ready_jedec(chipaddr dst); +-void data_polling_jedec(chipaddr dst, uint8_t data); +-void unprotect_jedec(chipaddr bios); +-void protect_jedec(chipaddr bios); +-int write_byte_program_jedec(chipaddr bios, uint8_t *src, +- chipaddr dst); +-int probe_jedec(struct flashchip *flash); +-int erase_chip_jedec(struct flashchip *flash); +-int write_jedec(struct flashchip *flash, uint8_t *buf); +-int erase_sector_jedec(struct flashchip *flash, unsigned int page, int pagesize); +-int erase_block_jedec(struct flashchip *flash, unsigned int page, int blocksize); +-int write_sector_jedec(chipaddr bios, uint8_t *src, +- chipaddr dst, unsigned int page_size); +- +-/* m29f002.c */ +-int erase_m29f002(struct flashchip *flash); +-int write_m29f002t(struct flashchip *flash, uint8_t *buf); +-int write_m29f002b(struct flashchip *flash, uint8_t *buf); +- +-/* m29f400bt.c */ +-int probe_m29f400bt(struct flashchip *flash); +-int erase_m29f400bt(struct flashchip *flash); +-int block_erase_m29f400bt(struct flashchip *flash, int start, int len); +-int write_m29f400bt(struct flashchip *flash, uint8_t *buf); +-int write_coreboot_m29f400bt(struct flashchip *flash, uint8_t *buf); +-void toggle_ready_m29f400bt(chipaddr dst); +-void data_polling_m29f400bt(chipaddr dst, uint8_t data); +-void protect_m29f400bt(chipaddr bios); +-void write_page_m29f400bt(chipaddr bios, uint8_t *src, +- chipaddr dst, int page_size); +- +-/* mx29f002.c */ +-int probe_29f002(struct flashchip *flash); +-int erase_29f002(struct flashchip *flash); +-int write_29f002(struct flashchip *flash, uint8_t *buf); +- +-/* pm49fl00x.c */ +-int probe_49fl00x(struct flashchip *flash); +-int erase_49fl00x(struct flashchip *flash); +-int write_49fl00x(struct flashchip *flash, uint8_t *buf); +- +-/* sharplhf00l04.c */ +-int probe_lhf00l04(struct flashchip *flash); +-int erase_lhf00l04(struct flashchip *flash); +-int write_lhf00l04(struct flashchip *flash, uint8_t *buf); +-void toggle_ready_lhf00l04(chipaddr dst); +-void data_polling_lhf00l04(chipaddr dst, uint8_t data); +-void protect_lhf00l04(chipaddr bios); +- +-/* sst28sf040.c */ +-int probe_28sf040(struct flashchip *flash); +-int erase_28sf040(struct flashchip *flash); +-int write_28sf040(struct flashchip *flash, uint8_t *buf); +- +-/* sst39sf020.c */ +-int probe_39sf020(struct flashchip *flash); +-int write_39sf020(struct flashchip *flash, uint8_t *buf); +- +-/* sst49lf040.c */ +-int erase_49lf040(struct flashchip *flash); +-int write_49lf040(struct flashchip *flash, uint8_t *buf); +- +-/* sst49lfxxxc.c */ +-int probe_49lfxxxc(struct flashchip *flash); +-int erase_49lfxxxc(struct flashchip *flash); +-int write_49lfxxxc(struct flashchip *flash, uint8_t *buf); +- +-/* sst_fwhub.c */ +-int probe_sst_fwhub(struct flashchip *flash); +-int erase_sst_fwhub(struct flashchip *flash); +-int write_sst_fwhub(struct flashchip *flash, uint8_t *buf); +- +-/* w39v040c.c */ +-int probe_w39v040c(struct flashchip *flash); +-int erase_w39v040c(struct flashchip *flash); +-int write_w39v040c(struct flashchip *flash, uint8_t *buf); +- +-/* w39V080fa.c */ +-int probe_winbond_fwhub(struct flashchip *flash); +-int erase_winbond_fwhub(struct flashchip *flash); +-int write_winbond_fwhub(struct flashchip *flash, uint8_t *buf); +- +-/* w29ee011.c */ +-int probe_w29ee011(struct flashchip *flash); +- +-/* w49f002u.c */ +-int write_49f002(struct flashchip *flash, uint8_t *buf); +- + /* wbsio_spi.c */ + int wbsio_check_for_spi(const char *name); + int wbsio_spi_send_command(unsigned int writecnt, unsigned int readcnt, +@@ -638,11 +645,6 @@ int wbsio_spi_send_command(unsigned int writecnt, unsigned int readcnt, + int wbsio_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len); + int wbsio_spi_write_1(struct flashchip *flash, uint8_t *buf); + +-/* stm50flw0x0x.c */ +-int probe_stm50flw0x0x(struct flashchip *flash); +-int erase_stm50flw0x0x(struct flashchip *flash); +-int write_stm50flw0x0x(struct flashchip *flash, uint8_t *buf); +- + /* serprog.c */ + int serprog_init(void); + int serprog_shutdown(void); +@@ -651,4 +653,21 @@ uint8_t serprog_chip_readb(const chipaddr addr); + void serprog_chip_readn(uint8_t *buf, const chipaddr addr, size_t len); + void serprog_delay(int delay); + ++/* serial.c */ ++#if _WIN32 ++typedef HANDLE fdtype; ++#else ++typedef int fdtype; ++#endif ++ ++void sp_flush_incoming(void); ++fdtype sp_openserport(char *dev, unsigned int baud); ++void __attribute__((noreturn)) sp_die(char *msg); ++extern fdtype sp_fd; ++int serialport_shutdown(void); ++int serialport_write(unsigned char *buf, unsigned int writecnt); ++int serialport_read(unsigned char *buf, unsigned int readcnt); ++ ++#include "chipdrivers.h" ++ + #endif /* !__FLASH_H__ */ +diff --git a/flashchips.c b/flashchips.c +index 5fd78f2..f66b95a 100644 +--- a/flashchips.c ++++ b/flashchips.c +@@ -5,6 +5,7 @@ + * Copyright (C) 2004 Tyan Corp + * Copyright (C) 2005-2008 coresystems GmbH + * Copyright (C) 2006-2009 Carl-Daniel Hailfinger ++ * Copyright (C) 2009 Sean Nelson + * + * 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 +@@ -44,6 +45,11 @@ struct flashchip flashchips[] = { + * .probe = Probe function + * .probe_timing = Probe function delay + * .erase = Chip erase function ++ * .block_erasers[] = Array of erase layouts and erase functions ++ * { ++ * .eraseblocks[] = Array of { blocksize, blockcount } ++ * .block_erase = Block erase function ++ * } + * .write = Chip write function + * .read = Chip read function + */ +@@ -56,11 +62,22 @@ struct flashchip flashchips[] = { + .model_id = AM_29F010B, /* Same as Am29F010A */ + .total_size = 128, + .page_size = 16 * 1024, +- .tested = TEST_OK_PREW, +- .probe = probe_29f040b, +- .probe_timing = TIMING_FIXME, +- .erase = erase_29f040b, +- .write = write_pm29f002, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {16 * 1024, 8} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -72,11 +89,27 @@ struct flashchip flashchips[] = { + .model_id = AM_29F002BB, + .total_size = 256, + .page_size = 256, ++ .feature_bits = FEATURE_SHORT_RESET | FEATURE_ADDR_2AA, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_en29f002a, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {16 * 1024, 1}, ++ {8 * 1024, 2}, ++ {32 * 1024, 1}, ++ {64 * 1024, 3}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -88,11 +121,27 @@ struct flashchip flashchips[] = { + .model_id = AM_29F002BT, + .total_size = 256, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_EITHER_RESET | FEATURE_ADDR_2AA, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_en29f002a, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 3}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -102,13 +151,24 @@ struct flashchip flashchips[] = { + .bustype = CHIP_BUSTYPE_PARALLEL, + .manufacture_id = AMD_ID, + .model_id = AM_29F016D, +- .total_size = 2048, ++ .total_size = 2 * 1024, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, + .tested = TEST_UNTESTED, +- .probe = probe_29f040b, ++ .probe = probe_jedec, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (am29f040b.c) */ +- .erase = erase_29f040b, +- .write = write_29f040b, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {2048 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -120,11 +180,22 @@ struct flashchip flashchips[] = { + .model_id = AM_29F040B, + .total_size = 512, + .page_size = 64 * 1024, +- .tested = TEST_OK_PREW, +- .probe = probe_29f040b, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (am29f040b.c) */ +- .erase = erase_29f040b, +- .write = write_29f040b, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -136,11 +207,22 @@ struct flashchip flashchips[] = { + .model_id = AM_29F080B, + .total_size = 1024, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_29f040b, +- .write = write_29f040b, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -152,11 +234,22 @@ struct flashchip flashchips[] = { + .model_id = AM_29LV040B, + .total_size = 512, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, + .tested = TEST_UNTESTED, +- .probe = probe_29f040b, ++ .probe = probe_jedec, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (am29f040b.c) */ +- .erase = erase_29f040b, +- .write = write_29f040b, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -168,11 +261,22 @@ struct flashchip flashchips[] = { + .model_id = AM_29LV080B, + .total_size = 1024, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, + .tested = TEST_UNTESTED, +- .probe = probe_29f040b, ++ .probe = probe_jedec, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (am29f040b.c) */ +- .erase = erase_29f040b, +- .write = write_29f040b, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -184,10 +288,26 @@ struct flashchip flashchips[] = { + .model_id = ASD_AE49F2008, + .total_size = 256, + .page_size = 128, ++ .feature_bits = FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {128 * 1024, 1}, ++ {96 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_jedec, + .read = read_memmapped, + }, +@@ -203,7 +323,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 64} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 8} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 4} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -219,7 +358,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 16} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -235,7 +393,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 256} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 32} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -251,7 +428,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 512} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 64} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -264,10 +460,29 @@ struct flashchip flashchips[] = { + .model_id = AT_25DF321, + .total_size = 4096, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 1024} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 128} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 64} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -283,7 +498,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 1024} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 128} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 64} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -299,7 +533,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 2048} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 256} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 128} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {8 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {8 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -315,7 +568,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 16} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 2} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {32 * 1024, 2} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -331,7 +603,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 32} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 4} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {32 * 1024, 4} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -347,7 +638,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -364,6 +674,13 @@ struct flashchip flashchips[] = { + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, + .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = spi_block_erase_20, ++ } ++ }, + .write = NULL /* Incompatible Page write */, + .read = spi_chip_read, + }, +@@ -379,7 +696,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 256} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 32} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -395,7 +731,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 512} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 64} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -411,7 +766,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 512} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 64} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -444,7 +818,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 16} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = NULL /* Incompatible Page write */, + .read = spi_chip_read, + }, +@@ -457,10 +850,18 @@ struct flashchip flashchips[] = { + .model_id = AT_29C512, + .total_size = 64, + .page_size = 128, +- .tested = TEST_OK_PREW, +- .probe = probe_jedec, ++ .feature_bits = FEATURE_LONG_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, + .probe_timing = 10000, /* 10mS, Enter=Exec */ +- .erase = erase_chip_jedec, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_jedec, + .read = read_memmapped, + +@@ -474,10 +875,18 @@ struct flashchip flashchips[] = { + .model_id = AT_29C010A, + .total_size = 128, + .page_size = 128, +- .tested = TEST_OK_PRE, ++ .feature_bits = FEATURE_LONG_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 10000, /* 10mS, Enter=Exec */ +- .erase = erase_chip_jedec, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_jedec, /* FIXME */ + .read = read_memmapped, + }, +@@ -490,10 +899,18 @@ struct flashchip flashchips[] = { + .model_id = AT_29C020, + .total_size = 256, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_LONG_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 10000, /* 10ms */ +- .erase = erase_chip_jedec, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_jedec, + .read = read_memmapped, + }, +@@ -506,10 +923,18 @@ struct flashchip flashchips[] = { + .model_id = AT_29C040A, + .total_size = 512, + .page_size = 256, ++ .feature_bits = FEATURE_LONG_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 10000, /* 10 ms */ +- .erase = erase_chip_jedec, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_jedec, + .read = read_memmapped, + }, +@@ -666,11 +1091,19 @@ struct flashchip flashchips[] = { + .model_id = AT_49BV512, + .total_size = 64, + .page_size = 64, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -682,11 +1115,27 @@ struct flashchip flashchips[] = { + .model_id = AT_49F002N, + .total_size = 256, + .page_size = 256, ++ .feature_bits = FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, /* doc1008.pdf dont says anything about probe timing */ +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {16 * 1024, 1}, ++ {8 * 1024, 2}, ++ {96 * 1024, 1}, ++ {128 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -698,26 +1147,92 @@ struct flashchip flashchips[] = { + .model_id = AT_49F002NT, + .total_size = 256, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, /* doc1008.pdf dont says anything about probe timing */ +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {128 * 1024, 1}, ++ {96 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + ++ /* The next two chip definitions have top/bottom boot blocks, but has no ++ device differenciation between the two */ + { + .vendor = "AMIC", +- .name = "A25L40P", ++ .name = "A25L40PT", + .bustype = CHIP_BUSTYPE_SPI, + .manufacture_id = AMIC_ID, + .model_id = AMIC_A25L40P, + .total_size = 512, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid4, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 7}, ++ {32 * 1024, 1}, ++ {16 * 1024, 1}, ++ {8 * 1024, 1}, ++ {4 * 1024, 2}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, ++ .write = spi_chip_write_256, ++ .read = spi_chip_read, ++ }, ++ ++ { ++ .vendor = "AMIC", ++ .name = "A25L40PU", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = AMIC_ID, ++ .model_id = AMIC_A25L40P, ++ .total_size = 512, ++ .page_size = 256, ++ .tested = TEST_OK_PRW, ++ .probe = probe_spi_rdid4, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {4 * 1024, 2}, ++ {8 * 1024, 1}, ++ {16 * 1024, 1}, ++ {32 * 1024, 1}, ++ {64 * 1024, 7}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -730,11 +1245,27 @@ struct flashchip flashchips[] = { + .model_id = AMIC_A29002B, + .total_size = 256, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, + .tested = TEST_UNTESTED, +- .probe = probe_29f002, ++ .probe = probe_jedec, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (mx29f002.c) */ +- .erase = erase_29f002, +- .write = write_29f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {16 * 1024, 1}, ++ {8 * 1024, 2}, ++ {32 * 1024, 1}, ++ {64 * 1024, 3}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -746,11 +1277,27 @@ struct flashchip flashchips[] = { + .model_id = AMIC_A29002T, + .total_size = 256, + .page_size = 64 * 1024, +- .tested = TEST_OK_PREW, +- .probe = probe_29f002, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (mx29f002.c) */ +- .erase = erase_29f002, +- .write = write_29f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 3}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -762,11 +1309,22 @@ struct flashchip flashchips[] = { + .model_id = AMIC_A29040B, + .total_size = 512, + .page_size = 64 * 1024, +- .tested = TEST_OK_PR, +- .probe = probe_29f040b, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (am29f040b.c) */ +- .erase = erase_29f040b, +- .write = write_29f040b, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -778,10 +1336,21 @@ struct flashchip flashchips[] = { + .model_id = AMIC_A49LF040A, + .total_size = 512, + .page_size = 64 * 1024, +- .tested = TEST_OK_PREW, +- .probe = probe_49fl00x, +- .probe_timing = TIMING_FIXME, /* routine is wrapper to probe_jedec (pm49fl00x.c) */ +- .erase = erase_49fl00x, ++ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, ++ .probe_timing = TIMING_ZERO, /* routine is wrapper to probe_jedec (pm49fl00x.c) */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = erase_block_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_49fl00x, + .read = read_memmapped, + }, +@@ -794,11 +1363,27 @@ struct flashchip flashchips[] = { + .model_id = EMST_F49B002UA, + .total_size = 256, + .page_size = 4096, ++ .feature_bits = FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {128 * 1024, 1}, ++ {96 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -813,7 +1398,53 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {4 * 1024, 2}, ++ {8 * 1024, 1}, ++ {16 * 1024, 1}, ++ {32 * 1024, 1}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, ++ .write = spi_chip_write_256, ++ .read = spi_chip_read, ++ }, ++ ++ { ++ .vendor = "Eon", ++ .name = "EN25B05T", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = EON_ID_NOPREFIX, ++ .model_id = EN_25B05, ++ .total_size = 64, ++ .page_size = 256, ++ .tested = TEST_UNTESTED, ++ .probe = probe_spi_rdid, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {32 * 1024, 1}, ++ {16 * 1024, 1}, ++ {8 * 1024, 1}, ++ {4 * 1024, 2}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -829,7 +1460,53 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {4 * 1024, 2}, ++ {8 * 1024, 1}, ++ {16 * 1024, 1}, ++ {32 * 1024, 3}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, ++ .write = spi_chip_write_256, ++ .read = spi_chip_read, ++ }, ++ ++ { ++ .vendor = "Eon", ++ .name = "EN25B10T", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = EON_ID_NOPREFIX, ++ .model_id = EN_25B10, ++ .total_size = 128, ++ .page_size = 256, ++ .tested = TEST_UNTESTED, ++ .probe = probe_spi_rdid, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {32 * 1024, 3}, ++ {16 * 1024, 1}, ++ {8 * 1024, 1}, ++ {4 * 1024, 2}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -845,7 +1522,55 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {4 * 1024, 2}, ++ {8 * 1024, 1}, ++ {16 * 1024, 1}, ++ {32 * 1024, 1}, ++ {64 * 1024, 3} ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, ++ .write = spi_chip_write_256, ++ .read = spi_chip_read, ++ }, ++ ++ { ++ .vendor = "Eon", ++ .name = "EN25B20T", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = EON_ID_NOPREFIX, ++ .model_id = EN_25B20, ++ .total_size = 256, ++ .page_size = 256, ++ .tested = TEST_UNTESTED, ++ .probe = probe_spi_rdid, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 3}, ++ {32 * 1024, 1}, ++ {16 * 1024, 1}, ++ {8 * 1024, 1}, ++ {4 * 1024, 2}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -861,7 +1586,55 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {4 * 1024, 2}, ++ {8 * 1024, 1}, ++ {16 * 1024, 1}, ++ {32 * 1024, 1}, ++ {64 * 1024, 7} ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, ++ .write = spi_chip_write_256, ++ .read = spi_chip_read, ++ }, ++ ++ { ++ .vendor = "Eon", ++ .name = "EN25B40T", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = EON_ID_NOPREFIX, ++ .model_id = EN_25B40, ++ .total_size = 512, ++ .page_size = 256, ++ .tested = TEST_UNTESTED, ++ .probe = probe_spi_rdid, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 7}, ++ {32 * 1024, 1}, ++ {16 * 1024, 1}, ++ {8 * 1024, 1}, ++ {4 * 1024, 2}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -877,7 +1650,55 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {4 * 1024, 2}, ++ {8 * 1024, 1}, ++ {16 * 1024, 1}, ++ {32 * 1024, 1}, ++ {64 * 1024, 15} ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, ++ .write = spi_chip_write_256, ++ .read = spi_chip_read, ++ }, ++ ++ { ++ .vendor = "Eon", ++ .name = "EN25B80T", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = EON_ID_NOPREFIX, ++ .model_id = EN_25B80, ++ .total_size = 1024, ++ .page_size = 256, ++ .tested = TEST_UNTESTED, ++ .probe = probe_spi_rdid, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 15}, ++ {32 * 1024, 1}, ++ {16 * 1024, 1}, ++ {8 * 1024, 1}, ++ {4 * 1024, 2}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -893,7 +1714,55 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {4 * 1024, 2}, ++ {8 * 1024, 1}, ++ {16 * 1024, 1}, ++ {32 * 1024, 1}, ++ {64 * 1024, 31}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, ++ .write = spi_chip_write_256, ++ .read = spi_chip_read, ++ }, ++ ++ { ++ .vendor = "Eon", ++ .name = "EN25B16T", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = EON_ID_NOPREFIX, ++ .model_id = EN_25B16, ++ .total_size = 2048, ++ .page_size = 256, ++ .tested = TEST_UNTESTED, ++ .probe = probe_spi_rdid, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 31}, ++ {32 * 1024, 1}, ++ {16 * 1024, 1}, ++ {8 * 1024, 1}, ++ {4 * 1024, 2}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -909,7 +1778,55 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {4 * 1024, 2}, ++ {8 * 1024, 1}, ++ {16 * 1024, 1}, ++ {32 * 1024, 1}, ++ {64 * 1024, 63}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, ++ .write = spi_chip_write_256, ++ .read = spi_chip_read, ++ }, ++ ++ { ++ .vendor = "Eon", ++ .name = "EN25B32T", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = EON_ID_NOPREFIX, ++ .model_id = EN_25B32, ++ .total_size = 4096, ++ .page_size = 256, ++ .tested = TEST_UNTESTED, ++ .probe = probe_spi_rdid, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 63}, ++ {32 * 1024, 1}, ++ {16 * 1024, 1}, ++ {8 * 1024, 1}, ++ {4 * 1024, 2}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -925,7 +1842,55 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {4 * 1024, 2}, ++ {8 * 1024, 1}, ++ {16 * 1024, 1}, ++ {32 * 1024, 1}, ++ {64 * 1024, 127}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {8 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, ++ .write = spi_chip_write_256, ++ .read = spi_chip_read, ++ }, ++ ++ { ++ .vendor = "Eon", ++ .name = "EN25B64T", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = EON_ID_NOPREFIX, ++ .model_id = EN_25B64, ++ .total_size = 8192, ++ .page_size = 256, ++ .tested = TEST_UNTESTED, ++ .probe = probe_spi_rdid, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 127}, ++ {32 * 1024, 1}, ++ {16 * 1024, 1}, ++ {8 * 1024, 1}, ++ {4 * 1024, 2}, ++ }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {8 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -941,7 +1906,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 512} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -957,7 +1941,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 16} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 2} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {32 * 1024, 2} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -973,7 +1976,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 32} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 4} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {32 * 1024, 4} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -989,7 +2011,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 64} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 4} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {64 * 1024, 4} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1005,7 +2046,23 @@ struct flashchip flashchips[] = { + .tested = TEST_OK_PROBE, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ }, ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1021,7 +2078,23 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 256} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1037,7 +2110,23 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 512} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1053,7 +2142,23 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 1024} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 64} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1066,11 +2171,27 @@ struct flashchip flashchips[] = { + .model_id = EN_29F002B, + .total_size = 256, + .page_size = 256, ++ .feature_bits = FEATURE_ADDR_AAA | FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_en29f002a, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 3}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -1082,11 +2203,27 @@ struct flashchip flashchips[] = { + .model_id = EN_29F002T, + .total_size = 256, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_ADDR_AAA | FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_en29f002a, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {16 * 1024, 1}, ++ {8 * 1024, 2}, ++ {32 * 1024, 1}, ++ {64 * 1024, 3}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -1098,10 +2235,26 @@ struct flashchip flashchips[] = { + .model_id = MBM29F004BC, + .total_size = 512, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ + .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {16 * 1024, 1}, ++ {8 * 1024, 2}, ++ {32 * 1024, 1}, ++ {64 * 1024, 7}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, + .write = NULL, + .read = read_memmapped, + }, +@@ -1114,15 +2267,32 @@ struct flashchip flashchips[] = { + .model_id = MBM29F004TC, + .total_size = 512, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ + .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 7}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, + .write = NULL, + .read = read_memmapped, + }, + + { ++ /* FIXME: this has WORD/BYTE sequences; 2AA for word, 555 for byte */ + .vendor = "Fujitsu", + .name = "MBM29F400BC", + .bustype = CHIP_BUSTYPE_PARALLEL, +@@ -1130,10 +2300,26 @@ struct flashchip flashchips[] = { + .model_id = MBM29F400BC, + .total_size = 512, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_ADDR_SHIFTED | FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_m29f400bt, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (m29f400bt.c) */ +- .erase = erase_m29f400bt, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {16 * 1024, 1}, ++ {8 * 1024, 2}, ++ {32 * 1024, 1}, ++ {64 * 1024, 7}, ++ }, ++ .block_erase = block_erase_m29f400bt, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = block_erase_chip_m29f400bt, ++ }, ++ }, + .write = write_coreboot_m29f400bt, + .read = read_memmapped, + }, +@@ -1146,10 +2332,26 @@ struct flashchip flashchips[] = { + .model_id = MBM29F400TC, + .total_size = 512, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_ADDR_SHIFTED | FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_m29f400bt, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (m29f400bt.c) */ +- .erase = erase_m29f400bt, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 7}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = block_erase_m29f400bt, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = block_erase_chip_m29f400bt, ++ }, ++ }, + .write = write_coreboot_m29f400bt, + .read = read_memmapped, + }, +@@ -1162,10 +2364,22 @@ struct flashchip flashchips[] = { + .model_id = P28F001BXB, + .total_size = 128, + .page_size = 128 * 1024, /* 8k + 2x4k + 112k */ +- .tested = TEST_BAD_ERASE|TEST_BAD_WRITE, ++ .feature_bits = 0, ++ .tested = TEST_BAD_WRITE, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ + .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {8 * 1024, 1}, ++ {4 * 1024, 2}, ++ {112 * 1024, 1}, ++ }, ++ .block_erase = erase_82802ab_block, ++ }, ++ }, + .write = NULL, + .read = read_memmapped, + }, +@@ -1178,10 +2392,22 @@ struct flashchip flashchips[] = { + .model_id = P28F001BXT, + .total_size = 128, + .page_size = 128 * 1024, /* 112k + 2x4k + 8k */ +- .tested = TEST_OK_PR|TEST_BAD_ERASE|TEST_BAD_WRITE, ++ .feature_bits = 0, ++ .tested = TEST_BAD_WRITE, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ + .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {112 * 1024, 1}, ++ {4 * 1024, 2}, ++ {8 * 1024, 1}, ++ }, ++ .block_erase = erase_82802ab_block, ++ }, ++ }, + .write = NULL, + .read = read_memmapped, + }, +@@ -1194,10 +2420,18 @@ struct flashchip flashchips[] = { + .model_id = I_82802AB, + .total_size = 512, + .page_size = 64 * 1024, +- .tested = TEST_OK_PREW, ++ .feature_bits = 0, ++ .tested = TEST_OK_PRW, + .probe = probe_82802ab, + .probe_timing = TIMING_IGNORED, /* routine does not use probe_timing (82802ab.c) */ +- .erase = erase_82802ab, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = erase_82802ab_block, ++ }, ++ }, + .write = write_82802ab, + .read = read_memmapped, + }, +@@ -1210,10 +2444,18 @@ struct flashchip flashchips[] = { + .model_id = I_82802AC, + .total_size = 1024, + .page_size = 64 * 1024, +- .tested = TEST_OK_PREW, ++ .feature_bits = 0, ++ .tested = TEST_OK_PRW, + .probe = probe_82802ab, + .probe_timing = TIMING_IGNORED, /* routine does not use probe_timing (82802ab.c) */ +- .erase = erase_82802ab, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = erase_82802ab_block, ++ }, ++ }, + .write = write_82802ab, + .read = read_memmapped, + }, +@@ -1229,7 +2471,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 16} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ }, ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1245,7 +2506,23 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 32} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 2} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ }, ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1261,7 +2538,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 64} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 4} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 4} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ }, ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1274,10 +2570,29 @@ struct flashchip flashchips[] = { + .model_id = MX_25L4005, + .total_size = 512, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ }, ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1290,10 +2605,29 @@ struct flashchip flashchips[] = { + .model_id = MX_25L8005, + .total_size = 1024, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 256} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ }, ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1306,10 +2640,29 @@ struct flashchip flashchips[] = { + .model_id = MX_25L1605, + .total_size = 2048, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 512} }, ++ .block_erase = spi_block_erase_20, /* This erase function has 64k blocksize for eLiteFlash */ ++ }, { ++ .eraseblocks = { {64 * 1024, 32} }, /* Not supported in MX25L1605 (eLiteFlash) and MX25L1605D */ ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ }, ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1325,7 +2678,23 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 512} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1338,10 +2707,26 @@ struct flashchip flashchips[] = { + .model_id = MX_25L3205, + .total_size = 4096, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 1024} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {4 * 1024, 1024} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ }, ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1357,7 +2742,23 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 1024} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 64} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1373,7 +2774,23 @@ struct flashchip flashchips[] = { + .tested = TEST_OK_PROBE, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 128} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 128} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {8 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {8 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1389,7 +2806,23 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 4096} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 256} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {16 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {16 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1402,11 +2835,28 @@ struct flashchip flashchips[] = { + .model_id = MX_29F001B, + .total_size = 128, + .page_size = 32 * 1024, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, + .tested = TEST_UNTESTED, +- .probe = probe_29f002, ++ .probe = probe_jedec, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (mx29f002.c) */ +- .erase = erase_29f002, +- .write = write_29f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {8 * 1024, 1}, ++ {4 * 1024, 2}, ++ {8 * 1024, 2}, ++ {32 * 1024, 1}, ++ {64 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -1418,11 +2868,28 @@ struct flashchip flashchips[] = { + .model_id = MX_29F001T, + .total_size = 128, + .page_size = 32 * 1024, +- .tested = TEST_OK_PREW, +- .probe = probe_29f002, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (mx29f002.c) */ +- .erase = erase_29f002, +- .write = write_29f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 1}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {4 * 1024, 2}, ++ {8 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -1434,11 +2901,27 @@ struct flashchip flashchips[] = { + .model_id = MX_29F002B, + .total_size = 256, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, + .tested = TEST_UNTESTED, +- .probe = probe_29f002, ++ .probe = probe_jedec, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (mx29f002.c) */ +- .erase = erase_29f002, +- .write = write_29f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {16 * 1024, 1}, ++ {8 * 1024, 2}, ++ {32 * 1024, 1}, ++ {64 * 1024, 3}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -1450,11 +2933,27 @@ struct flashchip flashchips[] = { + .model_id = MX_29F002T, + .total_size = 256, + .page_size = 64 * 1024, +- .tested = TEST_OK_PREW, +- .probe = probe_29f002, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (mx29f002.c) */ +- .erase = erase_29f002, +- .write = write_29f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 3}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -1466,11 +2965,22 @@ struct flashchip flashchips[] = { + .model_id = MX_29LV040, + .total_size = 512, + .page_size = 64 * 1024, +- .tested = TEST_OK_PR, +- .probe = probe_29f002, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (mx29f002.c) */ +- .erase = erase_29f002, +- .write = write_29f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8}, }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -1485,7 +2995,20 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_d8, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 32} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 2} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1501,7 +3024,20 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_d8, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 64} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 4} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1512,12 +3048,25 @@ struct flashchip flashchips[] = { + .bustype = CHIP_BUSTYPE_SPI, + .manufacture_id = ST_ID, + .model_id = ST_M25PE40, +- .total_size = 256, ++ .total_size = 512, + .page_size = 256, + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_d8, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1533,7 +3082,20 @@ struct flashchip flashchips[] = { + .tested = TEST_OK_PREW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_d8, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 256} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1549,7 +3111,20 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_d8, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 512} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1565,7 +3140,20 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 32} }, ++ .block_erase = spi_block_erase_d7, ++ }, { ++ .eraseblocks = { {32 * 1024, 4} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1581,7 +3169,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 512} }, ++ .block_erase = spi_block_erase_d7, ++ }, { ++ .eraseblocks = { {4 * 1024, 512} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1597,7 +3204,20 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 64} }, ++ .block_erase = spi_block_erase_d7, ++ }, { ++ .eraseblocks = { {64 * 1024, 4} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1613,7 +3233,20 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = spi_block_erase_d7, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1629,7 +3262,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 256} }, ++ .block_erase = spi_block_erase_d7, ++ }, { ++ .eraseblocks = { {4 * 1024, 256} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1645,40 +3297,85 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 16} }, ++ .block_erase = spi_block_erase_d7, ++ }, { ++ .eraseblocks = { {32 * 1024, 2} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, + + { + .vendor = "PMC", +- .name = "Pm29F0002T", ++ .name = "Pm29F002T", + .bustype = CHIP_BUSTYPE_PARALLEL, + .manufacture_id = PMC_ID_NOPREFIX, + .model_id = PMC_29F002T, + .total_size = 256, +- .page_size = 8192, +- .tested = TEST_OK_PREW, +- .probe = probe_29f040b, ++ .page_size = 8 * 1024, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, + .probe_timing = TIMING_FIXME, +- .erase = erase_29f040b, +- .write = write_pm29f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {128 * 1024, 1}, ++ {96 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + + { + .vendor = "PMC", +- .name = "Pm29F0002B", ++ .name = "Pm29F002B", + .bustype = CHIP_BUSTYPE_PARALLEL, + .manufacture_id = PMC_ID_NOPREFIX, + .model_id = PMC_29F002B, + .total_size = 256, +- .page_size = 8192, ++ .page_size = 8 * 1024, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, +- .probe = probe_29f040b, ++ .probe = probe_jedec, + .probe_timing = TIMING_FIXME, +- .erase = erase_29f040b, +- .write = write_pm29f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {16 * 1024, 1}, ++ {8 * 1024, 2}, ++ {96 * 1024, 1}, ++ {128 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -1690,26 +3387,54 @@ struct flashchip flashchips[] = { + .model_id = PMC_39F010, + .total_size = 128, + .page_size = 4096, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 32} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {64 * 1024, 2} }, ++ .block_erase = erase_block_jedec, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + + { + .vendor = "PMC", + .name = "Pm49FL002", +- .bustype = CHIP_BUSTYPE_LPC|CHIP_BUSTYPE_FWH, /* A/A Mux*/ ++ .bustype = CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH, /* A/A Mux*/ + .manufacture_id = PMC_ID_NOPREFIX, + .model_id = PMC_49FL002, + .total_size = 256, + .page_size = 16 * 1024, +- .tested = TEST_OK_PREW, +- .probe = probe_49fl00x, +- .probe_timing = TIMING_FIXME, /* routine is wrapper to probe_jedec (pm49fl00x.c) */ +- .erase = erase_49fl00x, ++ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, ++ .probe_timing = TIMING_ZERO, /* routine is wrapper to probe_jedec (pm49fl00x.c) */ ++ .erase = NULL, /* Was: erase_49fl00x */ ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 64} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {16 * 1024, 16} }, ++ .block_erase = erase_block_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_49fl00x, + .read = read_memmapped, + }, +@@ -1717,20 +3442,60 @@ struct flashchip flashchips[] = { + { + .vendor = "PMC", + .name = "Pm49FL004", +- .bustype = CHIP_BUSTYPE_LPC|CHIP_BUSTYPE_FWH, /* A/A Mux*/ ++ .bustype = CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH, /* A/A Mux*/ + .manufacture_id = PMC_ID_NOPREFIX, + .model_id = PMC_49FL004, + .total_size = 512, + .page_size = 64 * 1024, +- .tested = TEST_OK_PREW, +- .probe = probe_49fl00x, +- .probe_timing = TIMING_FIXME, /* routine is wrapper to probe_jedec (pm49fl00x.c) */ +- .erase = erase_49fl00x, ++ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, ++ .probe_timing = TIMING_ZERO, /* routine is wrapper to probe_jedec (pm49fl00x.c) */ ++ .erase = NULL, /* Was: erase_49fl00x */ ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = erase_block_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_49fl00x, + .read = read_memmapped, + }, + + { ++ .vendor = "Sanyo", ++ .name = "LF25FW203A", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = SANYO_ID, ++ .model_id = SANYO_LE25FW203A, ++ .total_size = 2048, ++ .page_size = 256, ++ .tested = TEST_UNTESTED, ++ .probe = probe_spi_rdid, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, ++ .write = spi_chip_write_256, ++ .read = spi_chip_read, ++ }, ++ ++ { + .vendor = "Sharp", + .name = "LHF00L04", + .bustype = CHIP_BUSTYPE_FWH, /* A/A Mux */ +@@ -1738,26 +3503,78 @@ struct flashchip flashchips[] = { + .model_id = SHARP_LHF00L04, + .total_size = 1024, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, +- .probe = probe_lhf00l04, +- .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (sharplhf00l04.c) */ +- .erase = erase_lhf00l04, ++ .probe = probe_49lfxxxc, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 15}, ++ {8 * 1024, 8} ++ }, ++ .block_erase = erase_lhf00l04_block, ++ }, { ++ .eraseblocks = { ++ {1024 * 1024, 1} ++ }, ++ .block_erase = NULL, /* 30 D0, only in A/A mux mode */ ++ }, ++ }, + .write = write_lhf00l04, + .read = read_memmapped, + }, + + { + .vendor = "Spansion", ++ .name = "S25FL008A", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = SPANSION_ID, ++ .model_id = SPANSION_S25FL008A, ++ .total_size = 1024, ++ .page_size = 256, ++ .tested = TEST_OK_PREW, ++ .probe = probe_spi_rdid, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, ++ .write = spi_chip_write_256, ++ .read = spi_chip_read, ++ }, ++ ++ { ++ .vendor = "Spansion", + .name = "S25FL016A", + .bustype = CHIP_BUSTYPE_SPI, + .manufacture_id = SPANSION_ID, + .model_id = SPANSION_S25FL016A, + .total_size = 2048, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -1770,10 +3587,29 @@ struct flashchip flashchips[] = { + .model_id = SST_25VF016B, + .total_size = 2048, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 512} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 64} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ }, ++ }, + .write = spi_chip_write_1, + .read = spi_chip_read, + }, +@@ -1786,42 +3622,93 @@ struct flashchip flashchips[] = { + .model_id = SST_25VF032B, + .total_size = 4096, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 1024} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 128} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 64} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ }, ++ }, + .write = spi_chip_write_1, + .read = spi_chip_read, + }, + + { + .vendor = "SST", +- .name = "SST25VF040B", ++ .name = "SST25VF040.REMS", + .bustype = CHIP_BUSTYPE_SPI, + .manufacture_id = SST_ID, +- .model_id = SST_25VF040B, ++ .model_id = SST_25VF040_REMS, + .total_size = 512, + .page_size = 256, +- .tested = TEST_UNTESTED, +- .probe = probe_spi_rdid, ++ .tested = TEST_OK_PR, ++ .probe = probe_spi_rems, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 16} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, ++ }, + .write = spi_chip_write_1, + .read = spi_chip_read, + }, + + { + .vendor = "SST", +- .name = "SST25VF040.REMS", ++ .name = "SST25VF040B", + .bustype = CHIP_BUSTYPE_SPI, + .manufacture_id = SST_ID, +- .model_id = SST_25VF040_REMS, ++ .model_id = SST_25VF040B, + .total_size = 512, +- .page_size = 64*1024, +- .tested = TEST_OK_PR, +- .probe = probe_spi_rems, ++ .page_size = 256, ++ .tested = TEST_UNTESTED, ++ .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 16} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ }, ++ }, + .write = spi_chip_write_1, + .read = spi_chip_read, + }, +@@ -1833,11 +3720,30 @@ struct flashchip flashchips[] = { + .manufacture_id = SST_ID, + .model_id = SST_25VF040B_REMS, + .total_size = 512, +- .page_size = 64*1024, ++ .page_size = 256, + .tested = TEST_OK_PR, + .probe = probe_spi_rems, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 16} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ }, ++ }, + .write = spi_chip_write_1, + .read = spi_chip_read, + }, +@@ -1850,10 +3756,29 @@ struct flashchip flashchips[] = { + .model_id = SST_25VF080B, + .total_size = 1024, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_60_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 256} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 32} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ }, ++ }, + .write = spi_chip_write_1, + .read = spi_chip_read, + }, +@@ -1866,10 +3791,21 @@ struct flashchip flashchips[] = { + .model_id = SST_28SF040, + .total_size = 512, + .page_size = 256, ++ .feature_bits = 0, + .tested = TEST_UNTESTED, + .probe = probe_28sf040, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (sst28sf040.c) */ +- .erase = erase_28sf040, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {128, 4096} }, ++ .block_erase = erase_sector_28sf040, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_28sf040, ++ } ++ }, + .write = write_28sf040, + .read = read_memmapped, + }, +@@ -1882,10 +3818,18 @@ struct flashchip flashchips[] = { + .model_id = SST_29EE010, + .total_size = 128, + .page_size = 128, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_LONG_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 10, +- .erase = erase_chip_jedec, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_jedec, + .read = read_memmapped, + }, +@@ -1898,10 +3842,18 @@ struct flashchip flashchips[] = { + .model_id = SST_29LE010, + .total_size = 128, + .page_size = 128, ++ .feature_bits = FEATURE_LONG_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 10, +- .erase = erase_chip_jedec, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_jedec, + .read = read_memmapped, + }, +@@ -1914,10 +3866,18 @@ struct flashchip flashchips[] = { + .model_id = SST_29EE020A, + .total_size = 256, + .page_size = 128, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_LONG_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, ++ .probe_timing = 10, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_jedec, + .read = read_memmapped, + }, +@@ -1930,27 +3890,73 @@ struct flashchip flashchips[] = { + .model_id = SST_29LE020, + .total_size = 256, + .page_size = 128, ++ .feature_bits = FEATURE_LONG_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 10, +- .erase = erase_chip_jedec, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_jedec, + .read = read_memmapped, + }, + + { + .vendor = "SST", ++ .name = "SST39SF512", ++ .bustype = CHIP_BUSTYPE_PARALLEL, ++ .manufacture_id = SST_ID, ++ .model_id = SST_39SF512, ++ .total_size = 64, ++ .page_size = 4096, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, ++ .probe_timing = 1, /* 150 ns */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 16} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, ++ .read = read_memmapped, ++ }, ++ ++ { ++ .vendor = "SST", + .name = "SST39SF010A", + .bustype = CHIP_BUSTYPE_PARALLEL, + .manufacture_id = SST_ID, + .model_id = SST_39SF010, + .total_size = 128, + .page_size = 4096, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 1, /* 150 ns */ +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 32} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -1962,11 +3968,22 @@ struct flashchip flashchips[] = { + .model_id = SST_39SF020, + .total_size = 256, + .page_size = 4096, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 1, /* 150 ns */ +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 64} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -1978,11 +3995,22 @@ struct flashchip flashchips[] = { + .model_id = SST_39SF040, + .total_size = 512, + .page_size = 4096, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 1, /* 150 ns */ +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -1994,11 +4022,22 @@ struct flashchip flashchips[] = { + .model_id = SST_39VF512, + .total_size = 64, + .page_size = 4096, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 1, /* 150 ns*/ +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 16} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2010,11 +4049,22 @@ struct flashchip flashchips[] = { + .model_id = SST_39VF010, + .total_size = 128, + .page_size = 4096, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 1, /* 150 ns */ +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 32} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2026,11 +4076,22 @@ struct flashchip flashchips[] = { + .model_id = SST_39VF020, + .total_size = 256, + .page_size = 4096, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 1, /* 150 ns */ +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 64} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2042,11 +4103,22 @@ struct flashchip flashchips[] = { + .model_id = SST_39VF040, + .total_size = 512, + .page_size = 4096, +- .tested = TEST_OK_PROBE, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 1, /* 150 ns */ +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2058,11 +4130,25 @@ struct flashchip flashchips[] = { + .model_id = SST_39VF080, + .total_size = 1024, + .page_size = 4096, ++ .feature_bits = FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 1, /* 150 ns */ +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 256} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = erase_block_jedec, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2074,10 +4160,24 @@ struct flashchip flashchips[] = { + .model_id = SST_49LF002A, + .total_size = 256, + .page_size = 16 * 1024, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET, ++ .tested = TEST_OK_PRW, + .probe = probe_sst_fwhub, +- .probe_timing = TIMING_FIXME, /* routine is wrapper to probe_jedec (sst_fwhub.c) */ +- .erase = erase_sst_fwhub, ++ .probe_timing = 1, /* 150 ns | routine is wrapper to probe_jedec (sst_fwhub.c) */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 64} }, ++ .block_erase = erase_sst_fwhub_sector, ++ }, { ++ .eraseblocks = { {16 * 1024, 16} }, ++ .block_erase = erase_sst_fwhub_block, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = NULL, /* AA 55 80 AA 55 10, only in A/A mux mode */ ++ } ++ }, + .write = write_sst_fwhub, + .read = read_memmapped, + }, +@@ -2090,15 +4190,32 @@ struct flashchip flashchips[] = { + .model_id = SST_49LF003A, + .total_size = 384, + .page_size = 64 * 1024, +- .tested = TEST_OK_PROBE, ++ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET, ++ .tested = TEST_OK_PR, + .probe = probe_sst_fwhub, +- .probe_timing = TIMING_FIXME, /* routine is wrapper to probe_jedec (sst_fwhub.c) */ +- .erase = erase_sst_fwhub, ++ .probe_timing = 1, /* 150 ns | routine is wrapper to probe_jedec (sst_fwhub.c) */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 96} }, ++ .block_erase = erase_sst_fwhub_sector, ++ }, { ++ .eraseblocks = { {64 * 1024, 6} }, ++ .block_erase = erase_sst_fwhub_block, ++ }, { ++ .eraseblocks = { {384 * 1024, 1} }, ++ .block_erase = NULL, /* AA 55 80 AA 55 10, only in A/A mux mode */ ++ } ++ }, + .write = write_sst_fwhub, + .read = read_memmapped, + }, + + { ++ /* Contrary to the data sheet, TBL# on the SST49LF004B affects the top 128kB (instead of 64kB) ++ * and is only honored for 64k block erase, but not 4k sector erase. ++ */ + .vendor = "SST", + .name = "SST49LF004A/B", + .bustype = CHIP_BUSTYPE_FWH, /* A/A Mux */ +@@ -2106,10 +4223,24 @@ struct flashchip flashchips[] = { + .model_id = SST_49LF004A, + .total_size = 512, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET, + .tested = TEST_OK_PREW, + .probe = probe_sst_fwhub, +- .probe_timing = TIMING_FIXME, /* routine is wrapper to probe_jedec (sst_fwhub.c) */ +- .erase = erase_sst_fwhub, ++ .probe_timing = 1, /* 150 ns | routine is wrapper to probe_jedec (sst_fwhub.c) */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = erase_sector_jedec, /* missing unlock */ ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = erase_sst_fwhub_block, /* same as erase_block_jedec, but with unlock */ ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = NULL, /* AA 55 80 AA 55 10, only in A/A mux mode */ ++ }, ++ }, + .write = write_sst_fwhub, + .read = read_memmapped, + }, +@@ -2122,10 +4253,26 @@ struct flashchip flashchips[] = { + .model_id = SST_49LF004C, + .total_size = 512, + .page_size = 4 * 1024, ++ .feature_bits = 0, + .tested = TEST_UNTESTED, + .probe = probe_49lfxxxc, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (sst49lfxxxc.c) */ +- .erase = erase_49lfxxxc, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = erase_sector_49lfxxxc, ++ }, { ++ .eraseblocks = { ++ {64 * 1024, 7}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_block_49lfxxxc, ++ } ++ }, + .write = write_49lfxxxc, + .read = read_memmapped, + }, +@@ -2138,10 +4285,24 @@ struct flashchip flashchips[] = { + .model_id = SST_49LF008A, + .total_size = 1024, + .page_size = 64 * 1024, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET, ++ .tested = TEST_OK_PRW, + .probe = probe_sst_fwhub, +- .probe_timing = TIMING_FIXME, /* routine is wrapper to probe_jedec (sst_fwhub.c) */ +- .erase = erase_sst_fwhub, ++ .probe_timing = 1, /* 150 ns | routine is wrapper to probe_jedec (sst_fwhub.c) */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 256} }, ++ .block_erase = erase_sst_fwhub_sector, ++ }, { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = erase_sst_fwhub_block, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = NULL, /* AA 55 80 AA 55 10, only in A/A mux mode */ ++ } ++ }, + .write = write_sst_fwhub, + .read = read_memmapped, + }, +@@ -2154,10 +4315,26 @@ struct flashchip flashchips[] = { + .model_id = SST_49LF008C, + .total_size = 1024, + .page_size = 4 * 1024, ++ .feature_bits = 0, + .tested = TEST_UNTESTED, + .probe = probe_49lfxxxc, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (sst49lfxxxc.c) */ +- .erase = erase_49lfxxxc, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 256} }, ++ .block_erase = erase_sector_49lfxxxc, ++ }, { ++ .eraseblocks = { ++ {64 * 1024, 15}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_block_49lfxxxc, ++ } ++ }, + .write = write_49lfxxxc, + .read = read_memmapped, + }, +@@ -2170,10 +4347,26 @@ struct flashchip flashchips[] = { + .model_id = SST_49LF016C, + .total_size = 2048, + .page_size = 4 * 1024, +- .tested = TEST_OK_PREW, ++ .feature_bits = 0, ++ .tested = TEST_OK_PRW, + .probe = probe_49lfxxxc, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (sst49lfxxxc.c) */ +- .erase = erase_49lfxxxc, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 512} }, ++ .block_erase = erase_sector_49lfxxxc, ++ }, { ++ .eraseblocks = { ++ {64 * 1024, 31}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_block_49lfxxxc, ++ } ++ }, + .write = write_49lfxxxc, + .read = read_memmapped, + }, +@@ -2186,11 +4379,25 @@ struct flashchip flashchips[] = { + .model_id = SST_49LF020, + .total_size = 256, + .page_size = 16 * 1024, +- .tested = TEST_OK_PR, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 1, /* 150 ns */ +- .erase = erase_49lf040, +- .write = write_49lf040, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 64} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {16 * 1024, 16} }, ++ .block_erase = erase_block_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = NULL, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2202,11 +4409,25 @@ struct flashchip flashchips[] = { + .model_id = SST_49LF020A, + .total_size = 256, + .page_size = 4 * 1024, ++ .feature_bits = FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 1, /* 150 ns */ +- .erase = erase_49lf040, +- .write = write_49lf040, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 64} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {16 * 1024, 16} }, ++ .block_erase = erase_block_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = NULL, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2218,11 +4439,25 @@ struct flashchip flashchips[] = { + .model_id = SST_49LF040, + .total_size = 512, + .page_size = 4096, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 1, /* 150 ns */ +- .erase = erase_49lf040, +- .write = write_49lf040, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = erase_block_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = NULL, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2234,11 +4469,25 @@ struct flashchip flashchips[] = { + .model_id = SST_49LF040B, + .total_size = 512, + .page_size = 64 * 1024, +- .tested = TEST_OK_PREW, +- .probe = probe_sst_fwhub, +- .probe_timing = TIMING_FIXME, /* routine is wrapper to probe_jedec (sst_fwhub.c) */ +- .erase = erase_sst_fwhub, +- .write = write_sst_fwhub, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, ++ .probe_timing = 1, /* 150ns | routine is wrapper to probe_jedec (sst_fwhub.c) */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = erase_block_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = NULL, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2250,11 +4499,25 @@ struct flashchip flashchips[] = { + .model_id = SST_49LF080A, + .total_size = 1024, + .page_size = 4096, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = TIMING_FIXME, +- .erase = erase_49lf040, +- .write = write_49lf040, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 256} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = erase_block_jedec, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = NULL, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2266,10 +4529,27 @@ struct flashchip flashchips[] = { + .model_id = SST_49LF160C, + .total_size = 2048, + .page_size = 4 * 1024, +- .tested = TEST_OK_PREW, ++ .feature_bits = 0, ++ .tested = TEST_OK_PRW, + .probe = probe_49lfxxxc, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (sst49lfxxxc.c) */ + .erase = erase_49lfxxxc, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 512} }, ++ .block_erase = erase_sector_49lfxxxc, ++ }, { ++ .eraseblocks = { ++ {64 * 1024, 31}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_block_49lfxxxc, ++ } ++ }, + .write = write_49lfxxxc, + .read = read_memmapped, + }, +@@ -2285,7 +4565,17 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {32 * 1024, 2} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2306,7 +4596,17 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_res, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {32 * 1024, 2} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_1, /* 128 */ + .read = spi_chip_read, + }, +@@ -2322,7 +4622,17 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {32 * 1024, 4} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2339,7 +4649,17 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_res, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {32 * 1024, 4} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_1, /* 128 */ + .read = spi_chip_read, + }, +@@ -2355,7 +4675,17 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 4} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2368,10 +4698,20 @@ struct flashchip flashchips[] = { + .model_id = ST_M25P40, + .total_size = 512, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2387,7 +4727,17 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_res, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2400,10 +4750,20 @@ struct flashchip flashchips[] = { + .model_id = ST_M25P80, + .total_size = 1024, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2416,10 +4776,20 @@ struct flashchip flashchips[] = { + .model_id = ST_M25P16, + .total_size = 2048, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2432,10 +4802,20 @@ struct flashchip flashchips[] = { + .model_id = ST_M25P32, + .total_size = 4096, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 64} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2451,7 +4831,17 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 128} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {8 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2467,7 +4857,17 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {256 * 1024, 64} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {16 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2480,11 +4880,27 @@ struct flashchip flashchips[] = { + .model_id = ST_M29F002B, + .total_size = 256, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_ADDR_AAA | FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_m29f002, +- .write = write_m29f002b, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {16 * 1024, 1}, ++ {8 * 1024, 2}, ++ {32 * 1024, 1}, ++ {64 * 1024, 3}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2496,11 +4912,27 @@ struct flashchip flashchips[] = { + .model_id = ST_M29F002T, + .total_size = 256, + .page_size = 64 * 1024, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_ADDR_AAA | FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_m29f002, +- .write = write_m29f002t, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 3}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2512,15 +4944,27 @@ struct flashchip flashchips[] = { + .model_id = ST_M29F040B, + .total_size = 512, + .page_size = 64 * 1024, +- .tested = TEST_OK_PREW, +- .probe = probe_29f040b, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (am29f040b.c) */ +- .erase = erase_29f040b, +- .write = write_29f040b, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8}, }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + + { ++ /* FIXME: this has WORD/BYTE sequences; 2AA for word, 555 for byte */ + .vendor = "ST", + .name = "M29F400BT", + .bustype = CHIP_BUSTYPE_PARALLEL, +@@ -2528,10 +4972,26 @@ struct flashchip flashchips[] = { + .model_id = ST_M29F400BT, + .total_size = 512, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_ADDR_SHIFTED | FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_m29f400bt, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (m29f400bt.c) */ +- .erase = erase_m29f400bt, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 7}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = block_erase_m29f400bt, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = block_erase_chip_m29f400bt, ++ } ++ }, + .write = write_coreboot_m29f400bt, + .read = read_memmapped, + }, +@@ -2544,11 +5004,22 @@ struct flashchip flashchips[] = { + .model_id = ST_M29W010B, + .total_size = 128, + .page_size = 16 * 1024, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_jedec, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {16 * 1024, 8}, }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2560,26 +5031,80 @@ struct flashchip flashchips[] = { + .model_id = ST_M29W040B, + .total_size = 512, + .page_size = 64 * 1024, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_jedec, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8}, }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + ++ { ++ .vendor = "ST", ++ .name = "M29W512B", ++ .bustype = CHIP_BUSTYPE_PARALLEL, ++ .manufacture_id = ST_ID, ++ .model_id = ST_M29W512B, ++ .total_size = 64, ++ .page_size = 64 * 1024, ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET, ++ .tested = TEST_OK_PREW, ++ .probe = probe_jedec, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, ++ .read = read_memmapped, ++ }, ++ + { + .vendor = "ST", + .name = "M50FLW040A", +- .bustype = CHIP_BUSTYPE_FWH|CHIP_BUSTYPE_LPC, /* A/A Mux */ ++ .bustype = CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_LPC, /* A/A Mux */ + .manufacture_id = ST_ID, + .model_id = ST_M50FLW040A, + .total_size = 512, + .page_size = 64 * 1024, ++ .feature_bits = 0, + .tested = TEST_UNTESTED, +- .probe = probe_stm50flw0x0x, +- .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (stm50flw0x0x.c) */ +- .erase = erase_stm50flw0x0x, ++ .probe = probe_82802ab, ++ .probe_timing = TIMING_FIXME, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {4 * 1024, 16}, /* sector */ ++ {64 * 1024, 5}, /* block */ ++ {4 * 1024, 16}, /* sector */ ++ {4 * 1024, 16}, /* sector */ ++ }, ++ .block_erase = NULL, ++ }, { ++ .eraseblocks = { {64 * 1024, 8}, }, ++ .block_erase = erase_block_stm50flw0x0x, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_stm50flw0x0x, ++ } ++ }, + .write = write_stm50flw0x0x, + .read = read_memmapped, + }, +@@ -2587,15 +5112,34 @@ struct flashchip flashchips[] = { + { + .vendor = "ST", + .name = "M50FLW040B", +- .bustype = CHIP_BUSTYPE_FWH|CHIP_BUSTYPE_LPC, /* A/A Mux */ ++ .bustype = CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_LPC, /* A/A Mux */ + .manufacture_id = ST_ID, + .model_id = ST_M50FLW040B, + .total_size = 512, + .page_size = 64 * 1024, ++ .feature_bits = 0, + .tested = TEST_UNTESTED, +- .probe = probe_stm50flw0x0x, +- .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (stm50flw0x0x.c) */ +- .erase = erase_stm50flw0x0x, ++ .probe = probe_82802ab, ++ .probe_timing = TIMING_FIXME, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {4 * 1024, 16}, /* sector */ ++ {4 * 1024, 16}, /* sector */ ++ {64 * 1024, 5}, /* block */ ++ {4 * 1024, 16}, /* sector */ ++ }, ++ .block_erase = NULL, ++ }, { ++ .eraseblocks = { {64 * 1024, 8}, }, ++ .block_erase = erase_block_stm50flw0x0x, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_stm50flw0x0x, ++ } ++ }, + .write = write_stm50flw0x0x, + .read = read_memmapped, + }, +@@ -2603,15 +5147,34 @@ struct flashchip flashchips[] = { + { + .vendor = "ST", + .name = "M50FLW080A", +- .bustype = CHIP_BUSTYPE_FWH|CHIP_BUSTYPE_LPC, /* A/A Mux */ ++ .bustype = CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_LPC, /* A/A Mux */ + .manufacture_id = ST_ID, + .model_id = ST_M50FLW080A, + .total_size = 1024, + .page_size = 64 * 1024, +- .tested = TEST_OK_PREW, +- .probe = probe_stm50flw0x0x, +- .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (stm50flw0x0x.c) */ +- .erase = erase_stm50flw0x0x, ++ .feature_bits = 0, ++ .tested = TEST_UNTESTED, ++ .probe = probe_82802ab, ++ .probe_timing = TIMING_FIXME, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {4 * 1024, 16}, /* sector */ ++ {64 * 1024, 13}, /* block */ ++ {4 * 1024, 16}, /* sector */ ++ {4 * 1024, 16}, /* sector */ ++ }, ++ .block_erase = NULL, ++ }, { ++ .eraseblocks = { {64 * 1024, 16}, }, ++ .block_erase = erase_block_stm50flw0x0x, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = erase_chip_stm50flw0x0x, ++ } ++ }, + .write = write_stm50flw0x0x, + .read = read_memmapped, + }, +@@ -2619,15 +5182,34 @@ struct flashchip flashchips[] = { + { + .vendor = "ST", + .name = "M50FLW080B", +- .bustype = CHIP_BUSTYPE_FWH|CHIP_BUSTYPE_LPC, /* A/A Mux */ ++ .bustype = CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_LPC, /* A/A Mux */ + .manufacture_id = ST_ID, + .model_id = ST_M50FLW080B, + .total_size = 1024, + .page_size = 64 * 1024, ++ .feature_bits = 0, + .tested = TEST_UNTESTED, +- .probe = probe_stm50flw0x0x, +- .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (stm50flw0x0x.c) */ +- .erase = erase_stm50flw0x0x, ++ .probe = probe_82802ab, ++ .probe_timing = TIMING_FIXME, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {4 * 1024, 16}, /* sector */ ++ {4 * 1024, 16}, /* sector */ ++ {64 * 1024, 13}, /* block */ ++ {4 * 1024, 16}, /* sector */ ++ }, ++ .block_erase = NULL, ++ }, { ++ .eraseblocks = { {64 * 1024, 16}, }, ++ .block_erase = erase_block_stm50flw0x0x, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = erase_chip_stm50flw0x0x, ++ } ++ }, + .write = write_stm50flw0x0x, + .read = read_memmapped, + }, +@@ -2640,11 +5222,27 @@ struct flashchip flashchips[] = { + .model_id = ST_M50FW002, + .total_size = 256, + .page_size = 64 * 1024, ++ .feature_bits = 0, + .tested = TEST_UNTESTED, +- .probe = probe_49lfxxxc, ++ .probe = probe_82802ab, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (sst49lfxxxc.c) */ + .erase = NULL, +- .write = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 3}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_block_stm50flw0x0x, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_stm50flw0x0x, ++ } ++ }, ++ .write = write_stm50flw0x0x, + .read = read_memmapped, + }, + +@@ -2656,11 +5254,22 @@ struct flashchip flashchips[] = { + .model_id = ST_M50FW016, + .total_size = 2048, + .page_size = 64 * 1024, ++ .feature_bits = 0, + .tested = TEST_UNTESTED, + .probe = probe_82802ab, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (82802ab.c) */ +- .erase = erase_82802ab, +- .write = write_82802ab, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 32}, }, ++ .block_erase = erase_block_stm50flw0x0x, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = erase_chip_stm50flw0x0x, ++ } ++ }, ++ .write = write_stm50flw0x0x, + .read = read_memmapped, + }, + +@@ -2672,11 +5281,22 @@ struct flashchip flashchips[] = { + .model_id = ST_M50FW040, + .total_size = 512, + .page_size = 64 * 1024, +- .tested = TEST_OK_PREW, ++ .feature_bits = 0, ++ .tested = TEST_OK_PRW, + .probe = probe_82802ab, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (82802ab.c) */ +- .erase = erase_82802ab, +- .write = write_82802ab, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8}, }, ++ .block_erase = erase_block_stm50flw0x0x, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_stm50flw0x0x, ++ } ++ }, ++ .write = write_stm50flw0x0x, + .read = read_memmapped, + }, + +@@ -2688,11 +5308,22 @@ struct flashchip flashchips[] = { + .model_id = ST_M50FW080, + .total_size = 1024, + .page_size = 64 * 1024, +- .tested = TEST_OK_PREW, ++ .feature_bits = 0, ++ .tested = TEST_OK_PRW, + .probe = probe_82802ab, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (82802ab.c) */ +- .erase = erase_82802ab, +- .write = write_82802ab, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 16}, }, ++ .block_erase = erase_block_stm50flw0x0x, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = erase_chip_stm50flw0x0x, ++ } ++ }, ++ .write = write_stm50flw0x0x, + .read = read_memmapped, + }, + +@@ -2704,11 +5335,28 @@ struct flashchip flashchips[] = { + .model_id = ST_M50LPW116, + .total_size = 2048, + .page_size = 64 * 1024, ++ .feature_bits = 0, + .tested = TEST_UNTESTED, +- .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_jedec, ++ .probe = probe_82802ab, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {4 * 1024, 16}, ++ {64 * 1024, 30}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_block_stm50flw0x0x, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = erase_chip_stm50flw0x0x, ++ } ++ }, ++ .write = write_stm50flw0x0x, + .read = read_memmapped, + }, + +@@ -2720,11 +5368,22 @@ struct flashchip flashchips[] = { + .model_id = S29C31004T, + .total_size = 512, + .page_size = 128, ++ .feature_bits = FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {1 * 1024, 512} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2736,11 +5395,22 @@ struct flashchip flashchips[] = { + .model_id = S29C51001T, + .total_size = 128, + .page_size = 128, ++ .feature_bits = FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {512, 256} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2752,11 +5422,22 @@ struct flashchip flashchips[] = { + .model_id = S29C51002T, + .total_size = 256, + .page_size = 128, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {512, 512} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2768,11 +5449,22 @@ struct flashchip flashchips[] = { + .model_id = S29C51004T, + .total_size = 512, + .page_size = 128, ++ .feature_bits = FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {1 * 1024, 512} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2784,11 +5476,27 @@ struct flashchip flashchips[] = { + .model_id = TI_TMS29F002RB, + .total_size = 256, + .page_size = 16384, /* Non-uniform sectors */ ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ + .erase = NULL, +- .write = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {16 * 1024, 1}, ++ {8 * 1024, 2}, ++ {32 * 1024, 1}, ++ {64 * 1024, 3}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2800,11 +5508,27 @@ struct flashchip flashchips[] = { + .model_id = TI_TMS29F002RT, + .total_size = 256, + .page_size = 16384, /* Non-uniform sectors */ ++ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, ++ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */ + .erase = NULL, +- .write = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 3}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ }, ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2819,7 +5543,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 32} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 4} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 2} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2835,7 +5578,26 @@ struct flashchip flashchips[] = { + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 64} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 8} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 4} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2848,10 +5610,29 @@ struct flashchip flashchips[] = { + .model_id = W_25X40, + .total_size = 512, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 16} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2864,10 +5645,29 @@ struct flashchip flashchips[] = { + .model_id = W_25X80, + .total_size = 1024, + .page_size = 256, +- .tested = TEST_OK_PREW, ++ .tested = TEST_OK_PRW, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 256} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 32} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2883,7 +5683,96 @@ struct flashchip flashchips[] = { + .tested = TEST_OK_PR, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, +- .erase = spi_chip_erase_c7, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 512} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 64} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 32} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {2 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, ++ .write = spi_chip_write_256, ++ .read = spi_chip_read, ++ }, ++ ++ { ++ .vendor = "Winbond", ++ .name = "W25x32", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = WINBOND_NEX_ID, ++ .model_id = W_25X32, ++ .total_size = 4096, ++ .page_size = 256, ++ .tested = TEST_OK_PROBE, ++ .probe = probe_spi_rdid, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 1024} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 128} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 64} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {4 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, ++ .write = spi_chip_write_256, ++ .read = spi_chip_read, ++ }, ++ ++ { ++ .vendor = "Winbond", ++ .name = "W25x64", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = WINBOND_NEX_ID, ++ .model_id = W_25X64, ++ .total_size = 8192, ++ .page_size = 256, ++ .tested = TEST_UNTESTED, ++ .probe = probe_spi_rdid, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 2048} }, ++ .block_erase = spi_block_erase_20, ++ }, { ++ .eraseblocks = { {32 * 1024, 256} }, ++ .block_erase = spi_block_erase_52, ++ }, { ++ .eraseblocks = { {64 * 1024, 128} }, ++ .block_erase = spi_block_erase_d8, ++ }, { ++ .eraseblocks = { {8 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_60, ++ }, { ++ .eraseblocks = { {8 * 1024 * 1024, 1} }, ++ .block_erase = spi_block_erase_c7, ++ } ++ }, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, +@@ -2896,10 +5785,18 @@ struct flashchip flashchips[] = { + .model_id = W_29C011, + .total_size = 128, + .page_size = 128, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_LONG_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, ++ .probe_timing = 10, /* used datasheet for the W29C011A */ ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_jedec, + .read = read_memmapped, + }, +@@ -2912,10 +5809,18 @@ struct flashchip flashchips[] = { + .model_id = W_29C020C, + .total_size = 256, + .page_size = 128, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_LONG_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 10, +- .erase = erase_chip_jedec, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_jedec, + .read = read_memmapped, + }, +@@ -2928,10 +5833,18 @@ struct flashchip flashchips[] = { + .model_id = W_29C040P, + .total_size = 512, + .page_size = 256, ++ .feature_bits = FEATURE_LONG_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 10, +- .erase = erase_chip_jedec, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_jedec, + .read = read_memmapped, + }, +@@ -2944,10 +5857,18 @@ struct flashchip flashchips[] = { + .model_id = W_29C011, + .total_size = 128, + .page_size = 128, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_LONG_RESET, ++ .tested = TEST_OK_PRW, + .probe = probe_w29ee011, + .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (w29ee011.c) */ +- .erase = erase_chip_jedec, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {128 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, + .write = write_jedec, + .read = read_memmapped, + }, +@@ -2959,12 +5880,23 @@ struct flashchip flashchips[] = { + .manufacture_id = WINBOND_ID, + .model_id = W_39V040A, + .total_size = 512, +- .page_size = 64*1024, +- .tested = TEST_OK_PREW, ++ .page_size = 64 * 1024, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 10, +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2975,12 +5907,23 @@ struct flashchip flashchips[] = { + .manufacture_id = WINBOND_ID, + .model_id = W_39V040B, + .total_size = 512, +- .page_size = 64*1024, +- .tested = TEST_OK_PR | TEST_BAD_ERASE | TEST_BAD_WRITE, ++ .page_size = 64 * 1024, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 10, +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -2991,12 +5934,23 @@ struct flashchip flashchips[] = { + .manufacture_id = WINBOND_ID, + .model_id = W_39V040C, + .total_size = 512, +- .page_size = 64*1024, +- .tested = TEST_OK_PREW, +- .probe = probe_w39v040c, +- .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (w39v040c.c) */ +- .erase = erase_w39v040c, +- .write = write_w39v040c, ++ .page_size = 64 * 1024, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, ++ .probe_timing = TIMING_FIXME, ++ .erase = NULL, /* Was erase_w39v040c */ ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -3007,12 +5961,26 @@ struct flashchip flashchips[] = { + .manufacture_id = WINBOND_ID, + .model_id = W_39V040FA, + .total_size = 512, +- .page_size = 64*1024, +- .tested = TEST_OK_PREW, ++ .page_size = 64 * 1024, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 10, +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {4 * 1024, 128} }, ++ .block_erase = erase_block_jedec, ++ }, { ++ .eraseblocks = { {64 * 1024, 8} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -3023,12 +5991,23 @@ struct flashchip flashchips[] = { + .manufacture_id = WINBOND_ID, + .model_id = W_39V080A, + .total_size = 1024, +- .page_size = 64*1024, +- .tested = TEST_OK_PREW, ++ .page_size = 64 * 1024, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 10, +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 16} }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -3040,11 +6019,27 @@ struct flashchip flashchips[] = { + .model_id = W_49F002U, + .total_size = 256, + .page_size = 128, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .probe_timing = 10, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {128 * 1024, 1}, ++ {96 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -3056,11 +6051,27 @@ struct flashchip flashchips[] = { + .model_id = W_49V002A, + .total_size = 256, + .page_size = 128, +- .tested = TEST_OK_PREW, ++ .feature_bits = FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, + .probe = probe_jedec, + .probe_timing = 10, +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 3}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -3072,11 +6083,27 @@ struct flashchip flashchips[] = { + .model_id = W_49V002FA, + .total_size = 256, + .page_size = 128, ++ .feature_bits = FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, + .probe = probe_jedec, +- .probe_timing = TIMING_FIXME, +- .erase = erase_chip_jedec, +- .write = write_49f002, ++ .probe_timing = 10, ++ .erase = NULL, ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { ++ {64 * 1024, 3}, ++ {32 * 1024, 1}, ++ {8 * 1024, 2}, ++ {16 * 1024, 1}, ++ }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {256 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -3087,12 +6114,23 @@ struct flashchip flashchips[] = { + .manufacture_id = WINBOND_ID, + .model_id = W_39V080FA, + .total_size = 1024, +- .page_size = 64*1024, +- .tested = TEST_OK_PREW, +- .probe = probe_winbond_fwhub, +- .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (w39v080fa.c) */ +- .erase = erase_winbond_fwhub, +- .write = write_winbond_fwhub, ++ .page_size = 64 * 1024, ++ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET, ++ .tested = TEST_UNTESTED, ++ .probe = probe_jedec, ++ .probe_timing = TIMING_FIXME, ++ .erase = NULL, /* Was erase_winbond_fwhub */ ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 16}, }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {1024 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -3103,12 +6141,23 @@ struct flashchip flashchips[] = { + .manufacture_id = WINBOND_ID, + .model_id = W_39V080FA_DM, + .total_size = 512, +- .page_size = 64*1024, ++ .page_size = 64 * 1024, ++ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET, + .tested = TEST_UNTESTED, +- .probe = probe_winbond_fwhub, +- .probe_timing = TIMING_IGNORED, /* routine don't use probe_timing (w39v080fa.c) */ +- .erase = erase_winbond_fwhub, +- .write = write_winbond_fwhub, ++ .probe = probe_jedec, ++ .probe_timing = TIMING_FIXME, ++ .erase = NULL, /* Was erase_winbond_fwhub */ ++ .block_erasers = ++ { ++ { ++ .eraseblocks = { {64 * 1024, 8}, }, ++ .block_erase = erase_sector_jedec, ++ }, { ++ .eraseblocks = { {512 * 1024, 1} }, ++ .block_erase = erase_chip_block_jedec, ++ } ++ }, ++ .write = write_jedec_1, + .read = read_memmapped, + }, + +@@ -3208,5 +6257,48 @@ struct flashchip flashchips[] = { + .read = NULL, + }, + ++ { ++ .vendor = "Sanyo", ++ .name = "unknown Sanyo SPI chip", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = SANYO_ID, ++ .model_id = GENERIC_DEVICE_ID, ++ .total_size = 0, ++ .page_size = 256, ++ .tested = TEST_BAD_PREW, ++ .probe = probe_spi_rdid, ++ .probe_timing = TIMING_ZERO, ++ .erase = NULL, ++ .write = NULL, ++ .read = NULL, ++ }, ++ ++ { ++ .vendor = "Generic", ++ .name = "unknown SPI chip (RDID)", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = GENERIC_MANUF_ID, ++ .model_id = GENERIC_DEVICE_ID, ++ .total_size = 0, ++ .page_size = 256, ++ .tested = TEST_BAD_PREW, ++ .probe = probe_spi_rdid, ++ .erase = NULL, ++ .write = NULL, ++ }, ++ { ++ .vendor = "Generic", ++ .name = "unknown SPI chip (REMS)", ++ .bustype = CHIP_BUSTYPE_SPI, ++ .manufacture_id = GENERIC_MANUF_ID, ++ .model_id = GENERIC_DEVICE_ID, ++ .total_size = 0, ++ .page_size = 256, ++ .tested = TEST_BAD_PREW, ++ .probe = probe_spi_rems, ++ .erase = NULL, ++ .write = NULL, ++ }, ++ + { NULL } + }; +diff --git a/flashchips.h b/flashchips.h +index 40edbbb..e299852 100644 +--- a/flashchips.h ++++ b/flashchips.h +@@ -34,6 +34,7 @@ + * SPI parts have 16-bit device IDs if they support RDID. + */ + ++#define GENERIC_MANUF_ID 0xffff /* Check if there is a vendor ID */ + #define GENERIC_DEVICE_ID 0xffff /* Only match the vendor ID */ + + #define ALLIANCE_ID 0x52 /* Alliance Semiconductor */ +@@ -163,13 +164,29 @@ + #define EON_ID 0x7F1C /* EON Silicon Devices */ + #define EON_ID_NOPREFIX 0x1C /* EON, missing 0x7F prefix */ + #define EN_25B05 0x2010 /* Same as P05, 2^19 kbit or 2^16 kByte */ ++#define EN_25B05T 0x25 ++#define EN_25B05B 0x95 + #define EN_25B10 0x2011 /* Same as P10 */ ++#define EN_25B10T 0x40 ++#define EN_25B10B 0x30 + #define EN_25B20 0x2012 /* Same as P20 */ ++#define EN_25B20T 0x41 ++#define EN_25B20B 0x31 + #define EN_25B40 0x2013 /* Same as P40 */ ++#define EN_25B40T 0x42 ++#define EN_25B40B 0x32 + #define EN_25B80 0x2014 /* Same as P80 */ ++#define EN_25B80T 0x43 ++#define EN_25B80B 0x33 + #define EN_25B16 0x2015 /* Same as P16 */ ++#define EN_25B16T 0x44 ++#define EN_25B16B 0x34 + #define EN_25B32 0x2016 /* Same as P32 */ ++#define EN_25B32T 0x45 ++#define EN_25B32B 0x35 + #define EN_25B64 0x2017 /* Same as P64 */ ++#define EN_25B64T 0x46 ++#define EN_25B64B 0x36 + #define EN_25D16 0x3015 + #define EN_25F05 0x3110 + #define EN_25F10 0x3111 +@@ -251,11 +268,11 @@ + * and use the same set of IDs. + */ + #define MX_ID 0xC2 /* Macronix (MX) */ +-#define MX_25L512 0x2010 /* 2^19 kbit or 2^16 kByte */ ++#define MX_25L512 0x2010 /* Same as MX25V512 */ + #define MX_25L1005 0x2011 + #define MX_25L2005 0x2012 + #define MX_25L4005 0x2013 /* MX25L4005{,A} */ +-#define MX_25L8005 0x2014 ++#define MX_25L8005 0x2014 /* Same as MX25V8005 */ + #define MX_25L1605 0x2015 /* MX25L1605{,A,D} */ + #define MX_25L3205 0x2016 /* MX25L3205{,A} */ + #define MX_25L6405 0x2017 /* MX25L3205{,D} */ +@@ -325,6 +342,14 @@ + #define PMC_49FL002 0x6D + #define PMC_49FL004 0x6E + ++/* ++ * The Sanyo chip found so far uses SPI, first byte is manufacture code, ++ * second byte is the device code, ++ * third byte is a dummy byte. ++ */ ++#define SANYO_ID 0x62 ++#define SANYO_LE25FW203A 0x1600 ++ + #define SHARP_ID 0xB0 /* Sharp */ + #define SHARP_LH28F008BJxxPT 0xEC + #define SHARP_LH28F008BJxxPB 0xED +@@ -340,6 +365,7 @@ + * the second device ID byte is memory capacity. + */ + #define SPANSION_ID 0x01 /* Spansion, same ID as AMD */ ++#define SPANSION_S25FL008A 0x0213 + #define SPANSION_S25FL016A 0x0214 + + /* +@@ -380,6 +406,7 @@ + #define SST_29VF020 0x25 + #define SST_29SF040 0x13 + #define SST_29VF040 0x14 ++#define SST_39SF512 0xB4 + #define SST_39SF010 0xB5 + #define SST_39SF020 0xB6 /* Same as 39SF020A */ + #define SST_39SF040 0xB7 +@@ -446,6 +473,7 @@ + #define ST_M29F800DT 0xEC + #define ST_M29W010B 0x23 + #define ST_M29W040B 0xE3 ++#define ST_M29W512B 0x27 + + #define SYNCMOS_ID 0x40 /* SyncMOS and Mosel Vitelic */ + #define S29C51001T 0x01 +diff --git a/flashrom.8 b/flashrom.8 +index 1324854..1baea3f 100644 +--- a/flashrom.8 ++++ b/flashrom.8 +@@ -140,6 +140,10 @@ Specify the programmer device. Currently supported are: + .sp + .BR "* nic3com" " (for flash ROMs on 3COM network cards)" + .sp ++.BR "* gfxnvidia" " (for flash ROMs on NVIDIA graphics cards)" ++.sp ++.BR "* drkaiser" " (for flash ROMs on Dr. Kaiser PC-Waechter PCI cards)" ++.sp + .BR "* satasii" " (for flash ROMs on Silicon Image SATA/IDE controllers)" + .sp + .BR "* it87spi" " (for flash ROMs behind an ITE IT87xx Super I/O LPC/SPI translation unit)" +@@ -148,6 +152,8 @@ Specify the programmer device. Currently supported are: + .sp + .BR "* serprog" " (for flash ROMs attached to Urja's AVR programmer)" + .sp ++.BR "* buspiratespi" " (for flash ROMs attached to a Bus Pirate)" ++.sp + The dummy programmer has an optional parameter specifying the bus types it + should support. For that you have to use the + .B "flashrom -p dummy:type" +@@ -178,6 +184,7 @@ Example: + .sp + Currently the following programmers support this mechanism: + .BR nic3com , ++.BR gfxnvidia , + .BR satasii . + .sp + The it87spi programmer has an optional parameter which will set the I/O base +@@ -214,6 +221,16 @@ syntax and for IP, you have to use + instead. More information about serprog is available in serprog-protocol.txt in + the source distribution. + .sp ++The buspiratespi programmer has a required dev parameter specifying the Bus ++Pirate device node and an optional spispeed parameter specifying the frequency ++of the SPI bus. The parameter delimiter is a comma. Syntax is ++.B "flashrom -p buspiratespi:dev=/dev/device,spispeed=frequency" ++where ++.B frequency ++can be any of ++.B 30k 125k 250k 1M 2M 2.6M 4M 8M ++(in Hz). ++.sp + Support for some programmers can be disabled at compile time. + .B "flashrom -h" + lists all supported programmers. +@@ -245,7 +262,7 @@ Claus Gindhart + .br + Dominik Geyer + .br +-Eric Biederman ++Eric Biederman + .br + Giampiero Giancipoli + .br +@@ -275,9 +292,9 @@ Steven James + .br + Uwe Hermann + .br +-Wang Qingpei ++Wang Qingpei + .br +-Yinghai Lu ++Yinghai Lu + .br + some others + .PP +diff --git a/flashrom.c b/flashrom.c +index fc80301..326f725 100644 +--- a/flashrom.c ++++ b/flashrom.c +@@ -33,10 +33,70 @@ + const char *flashrom_version = FLASHROM_VERSION; + char *chip_to_probe = NULL; + int verbose = 0; ++ ++#if INTERNAL_SUPPORT == 1 + enum programmer programmer = PROGRAMMER_INTERNAL; ++#elif DUMMY_SUPPORT == 1 ++enum programmer programmer = PROGRAMMER_DUMMY; ++#else ++/* If neither internal nor dummy are selected, we must pick a sensible default. ++ * Since there is no reason to prefer a particular external programmer, we fail ++ * if more than one of them is selected. If only one is selected, it is clear ++ * that the user wants that one to become the default. ++ */ ++#if NIC3COM_SUPPORT+GFXNVIDIA_SUPPORT+DRKAISER_SUPPORT+SATASII_SUPPORT+FT2232_SPI_SUPPORT+SERPROG_SUPPORT+BUSPIRATE_SPI_SUPPORT+DEDIPROG_SUPPORT > 1 ++#error Please enable either CONFIG_DUMMY or CONFIG_INTERNAL or disable support for all external programmers except one. ++#endif ++enum programmer programmer = ++#if NIC3COM_SUPPORT == 1 ++ PROGRAMMER_NIC3COM ++#endif ++#if GFXNVIDIA_SUPPORT == 1 ++ PROGRAMMER_GFXNVIDIA ++#endif ++#if DRKAISER_SUPPORT == 1 ++ PROGRAMMER_DRKAISER ++#endif ++#if SATASII_SUPPORT == 1 ++ PROGRAMMER_SATASII ++#endif ++#if FT2232_SPI_SUPPORT == 1 ++ PROGRAMMER_FT2232SPI ++#endif ++#if SERPROG_SUPPORT == 1 ++ PROGRAMMER_SERPROG ++#endif ++#if BUSPIRATE_SPI_SUPPORT == 1 ++ PROGRAMMER_BUSPIRATESPI ++#endif ++#if DEDIPROG_SUPPORT == 1 ++ PROGRAMMER_DEDIPROG ++#endif ++; ++#endif ++ + char *programmer_param = NULL; + ++/** ++ * flashrom defaults to Parallel/LPC/FWH flash devices. If a known host ++ * controller is found, the init routine sets the buses_supported bitfield to ++ * contain the supported buses for that controller. ++ */ ++enum chipbustype buses_supported = CHIP_BUSTYPE_NONSPI; ++ ++/** ++ * Programmers supporting multiple buses can have differing size limits on ++ * each bus. Store the limits for each bus in a common struct. ++ */ ++struct decode_sizes max_rom_decode = { ++ .parallel = 0xffffffff, ++ .lpc = 0xffffffff, ++ .fwh = 0xffffffff, ++ .spi = 0xffffffff ++}; ++ + const struct programmer_entry programmer_table[] = { ++#if INTERNAL_SUPPORT == 1 + { + .name = "internal", + .init = internal_init, +@@ -53,7 +113,9 @@ const struct programmer_entry programmer_table[] = { + .chip_writen = fallback_chip_writen, + .delay = internal_delay, + }, ++#endif + ++#if DUMMY_SUPPORT == 1 + { + .name = "dummy", + .init = dummy_init, +@@ -70,7 +132,9 @@ const struct programmer_entry programmer_table[] = { + .chip_writen = dummy_chip_writen, + .delay = internal_delay, + }, ++#endif + ++#if NIC3COM_SUPPORT == 1 + { + .name = "nic3com", + .init = nic3com_init, +@@ -87,7 +151,47 @@ const struct programmer_entry programmer_table[] = { + .chip_writen = fallback_chip_writen, + .delay = internal_delay, + }, ++#endif + ++#if GFXNVIDIA_SUPPORT == 1 ++ { ++ .name = "gfxnvidia", ++ .init = gfxnvidia_init, ++ .shutdown = gfxnvidia_shutdown, ++ .map_flash_region = fallback_map, ++ .unmap_flash_region = fallback_unmap, ++ .chip_readb = gfxnvidia_chip_readb, ++ .chip_readw = fallback_chip_readw, ++ .chip_readl = fallback_chip_readl, ++ .chip_readn = fallback_chip_readn, ++ .chip_writeb = gfxnvidia_chip_writeb, ++ .chip_writew = fallback_chip_writew, ++ .chip_writel = fallback_chip_writel, ++ .chip_writen = fallback_chip_writen, ++ .delay = internal_delay, ++ }, ++#endif ++ ++#if DRKAISER_SUPPORT == 1 ++ { ++ .name = "drkaiser", ++ .init = drkaiser_init, ++ .shutdown = drkaiser_shutdown, ++ .map_flash_region = fallback_map, ++ .unmap_flash_region = fallback_unmap, ++ .chip_readb = drkaiser_chip_readb, ++ .chip_readw = fallback_chip_readw, ++ .chip_readl = fallback_chip_readl, ++ .chip_readn = fallback_chip_readn, ++ .chip_writeb = drkaiser_chip_writeb, ++ .chip_writew = fallback_chip_writew, ++ .chip_writel = fallback_chip_writel, ++ .chip_writen = fallback_chip_writen, ++ .delay = internal_delay, ++ }, ++#endif ++ ++#if SATASII_SUPPORT == 1 + { + .name = "satasii", + .init = satasii_init, +@@ -104,36 +208,39 @@ const struct programmer_entry programmer_table[] = { + .chip_writen = fallback_chip_writen, + .delay = internal_delay, + }, ++#endif + ++#if INTERNAL_SUPPORT == 1 + { + .name = "it87spi", + .init = it87spi_init, +- .shutdown = fallback_shutdown, ++ .shutdown = noop_shutdown, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, +- .chip_readb = dummy_chip_readb, ++ .chip_readb = noop_chip_readb, + .chip_readw = fallback_chip_readw, + .chip_readl = fallback_chip_readl, + .chip_readn = fallback_chip_readn, +- .chip_writeb = fallback_chip_writeb, ++ .chip_writeb = noop_chip_writeb, + .chip_writew = fallback_chip_writew, + .chip_writel = fallback_chip_writel, + .chip_writen = fallback_chip_writen, + .delay = internal_delay, + }, ++#endif + + #if FT2232_SPI_SUPPORT == 1 + { + .name = "ft2232spi", + .init = ft2232_spi_init, +- .shutdown = fallback_shutdown, ++ .shutdown = noop_shutdown, /* Missing shutdown */ + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, +- .chip_readb = dummy_chip_readb, ++ .chip_readb = noop_chip_readb, + .chip_readw = fallback_chip_readw, + .chip_readl = fallback_chip_readl, + .chip_readn = fallback_chip_readn, +- .chip_writeb = fallback_chip_writeb, ++ .chip_writeb = noop_chip_writeb, + .chip_writew = fallback_chip_writew, + .chip_writel = fallback_chip_writel, + .chip_writen = fallback_chip_writen, +@@ -160,6 +267,44 @@ const struct programmer_entry programmer_table[] = { + }, + #endif + ++#if BUSPIRATE_SPI_SUPPORT == 1 ++ { ++ .name = "buspiratespi", ++ .init = buspirate_spi_init, ++ .shutdown = buspirate_spi_shutdown, ++ .map_flash_region = fallback_map, ++ .unmap_flash_region = fallback_unmap, ++ .chip_readb = noop_chip_readb, ++ .chip_readw = fallback_chip_readw, ++ .chip_readl = fallback_chip_readl, ++ .chip_readn = fallback_chip_readn, ++ .chip_writeb = noop_chip_writeb, ++ .chip_writew = fallback_chip_writew, ++ .chip_writel = fallback_chip_writel, ++ .chip_writen = fallback_chip_writen, ++ .delay = internal_delay, ++ }, ++#endif ++ ++#if DEDIPROG_SUPPORT == 1 ++ { ++ .name = "dediprog", ++ .init = dediprog_init, ++ .shutdown = dediprog_shutdown, ++ .map_flash_region = fallback_map, ++ .unmap_flash_region = fallback_unmap, ++ .chip_readb = noop_chip_readb, ++ .chip_readw = fallback_chip_readw, ++ .chip_readl = fallback_chip_readl, ++ .chip_readn = fallback_chip_readn, ++ .chip_writeb = noop_chip_writeb, ++ .chip_writew = fallback_chip_writew, ++ .chip_writel = fallback_chip_writel, ++ .chip_writen = fallback_chip_writen, ++ .delay = internal_delay, ++ }, ++#endif ++ + {}, /* This entry corresponds to PROGRAMMER_INVALID. */ + }; + +@@ -245,6 +390,8 @@ int read_memmapped(struct flashchip *flash, uint8_t *buf, int start, int len) + return 0; + } + ++unsigned long flashbase = 0; ++ + int min(int a, int b) + { + return (a < b) ? a : b; +@@ -255,6 +402,15 @@ int max(int a, int b) + return (a > b) ? a : b; + } + ++int bitcount(unsigned long a) ++{ ++ int i = 0; ++ for (; a != 0; a >>= 1) ++ if (a & 1) ++ i++; ++ return i; ++} ++ + char *strcat_realloc(char *dest, const char *src) + { + dest = realloc(dest, strlen(dest) + strlen(src) + 1); +@@ -264,6 +420,60 @@ char *strcat_realloc(char *dest, const char *src) + return dest; + } + ++/* This is a somewhat hacked function similar in some ways to strtok(). ++ * It will look for needle in haystack, return a copy of needle and remove ++ * everything from the first occurrence of needle to the next delimiter ++ * from haystack. ++ */ ++char *extract_param(char **haystack, char *needle, char *delim) ++{ ++ char *param_pos, *rest, *tmp; ++ char *dev = NULL; ++ int devlen; ++ ++ param_pos = strstr(*haystack, needle); ++ do { ++ if (!param_pos) ++ return NULL; ++ /* Beginning of the string? */ ++ if (param_pos == *haystack) ++ break; ++ /* After a delimiter? */ ++ if (strchr(delim, *(param_pos - 1))) ++ break; ++ /* Continue searching. */ ++ param_pos++; ++ param_pos = strstr(param_pos, needle); ++ } while (1); ++ ++ if (param_pos) { ++ param_pos += strlen(needle); ++ devlen = strcspn(param_pos, delim); ++ if (devlen) { ++ dev = malloc(devlen + 1); ++ if (!dev) { ++ fprintf(stderr, "Out of memory!\n"); ++ exit(1); ++ } ++ strncpy(dev, param_pos, devlen); ++ dev[devlen] = '\0'; ++ } ++ rest = param_pos + devlen; ++ rest += strspn(rest, delim); ++ param_pos -= strlen(needle); ++ memmove(param_pos, rest, strlen(rest) + 1); ++ tmp = realloc(*haystack, strlen(*haystack) + 1); ++ if (!tmp) { ++ fprintf(stderr, "Out of memory!\n"); ++ exit(1); ++ } ++ *haystack = tmp; ++ } ++ ++ ++ return dev; ++} ++ + /* start is an offset to the base address of the flash chip */ + int check_erased_range(struct flashchip *flash, int start, int len) + { +@@ -281,7 +491,8 @@ int check_erased_range(struct flashchip *flash, int start, int len) + } + + /** +- * @cmpbuf buffer to compare against ++ * @cmpbuf buffer to compare against, cmpbuf[0] is expected to match the ++ flash content at location start + * @start offset to the base address of the flash chip + * @len length of the verified area + * @message string to print in the "FAILED" message +@@ -354,10 +565,197 @@ out_free: + return ret; + } + ++/* This function generates various test patterns useful for testing controller ++ * and chip communication as well as chip behaviour. ++ * ++ * If a byte can be written multiple times, each time keeping 0-bits at 0 ++ * and changing 1-bits to 0 if the new value for that bit is 0, the effect ++ * is essentially an AND operation. That's also the reason why this function ++ * provides the result of AND between various patterns. ++ * ++ * Below is a list of patterns (and their block length). ++ * Pattern 0 is 05 15 25 35 45 55 65 75 85 95 a5 b5 c5 d5 e5 f5 (16 Bytes) ++ * Pattern 1 is 0a 1a 2a 3a 4a 5a 6a 7a 8a 9a aa ba ca da ea fa (16 Bytes) ++ * Pattern 2 is 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f (16 Bytes) ++ * Pattern 3 is a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af (16 Bytes) ++ * Pattern 4 is 00 10 20 30 40 50 60 70 80 90 a0 b0 c0 d0 e0 f0 (16 Bytes) ++ * Pattern 5 is 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f (16 Bytes) ++ * Pattern 6 is 00 (1 Byte) ++ * Pattern 7 is ff (1 Byte) ++ * Patterns 0-7 have a big-endian block number in the last 2 bytes of each 256 ++ * byte block. ++ * ++ * Pattern 8 is 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11... (256 B) ++ * Pattern 9 is ff fe fd fc fb fa f9 f8 f7 f6 f5 f4 f3 f2 f1 f0 ef ee... (256 B) ++ * Pattern 10 is 00 00 00 01 00 02 00 03 00 04... (128 kB big-endian counter) ++ * Pattern 11 is ff ff ff fe ff fd ff fc ff fb... (128 kB big-endian downwards) ++ * Pattern 12 is 00 (1 Byte) ++ * Pattern 13 is ff (1 Byte) ++ * Patterns 8-13 have no block number. ++ * ++ * Patterns 0-3 are created to detect and efficiently diagnose communication ++ * slips like missed bits or bytes and their repetitive nature gives good visual ++ * cues to the person inspecting the results. In addition, the following holds: ++ * AND Pattern 0/1 == Pattern 4 ++ * AND Pattern 2/3 == Pattern 5 ++ * AND Pattern 0/1/2/3 == AND Pattern 4/5 == Pattern 6 ++ * A weakness of pattern 0-5 is the inability to detect swaps/copies between ++ * any two 16-byte blocks except for the last 16-byte block in a 256-byte bloc. ++ * They work perfectly for detecting any swaps/aliasing of blocks >= 256 bytes. ++ * 0x5 and 0xa were picked because they are 0101 and 1010 binary. ++ * Patterns 8-9 are best for detecting swaps/aliasing of blocks < 256 bytes. ++ * Besides that, they provide for bit testing of the last two bytes of every ++ * 256 byte block which contains the block number for patterns 0-6. ++ * Patterns 10-11 are special purpose for detecting subblock aliasing with ++ * block sizes >256 bytes (some Dataflash chips etc.) ++ * AND Pattern 8/9 == Pattern 12 ++ * AND Pattern 10/11 == Pattern 12 ++ * Pattern 13 is the completely erased state. ++ * None of the patterns can detect aliasing at boundaries which are a multiple ++ * of 16 MBytes (but such chips do not exist anyway for Parallel/LPC/FWH/SPI). ++ */ ++int generate_testpattern(uint8_t *buf, uint32_t size, int variant) ++{ ++ int i; ++ ++ if (!buf) { ++ fprintf(stderr, "Invalid buffer!\n"); ++ return 1; ++ } ++ ++ switch (variant) { ++ case 0: ++ for (i = 0; i < size; i++) ++ buf[i] = (i & 0xf) << 4 | 0x5; ++ break; ++ case 1: ++ for (i = 0; i < size; i++) ++ buf[i] = (i & 0xf) << 4 | 0xa; ++ break; ++ case 2: ++ for (i = 0; i < size; i++) ++ buf[i] = 0x50 | (i & 0xf); ++ break; ++ case 3: ++ for (i = 0; i < size; i++) ++ buf[i] = 0xa0 | (i & 0xf); ++ break; ++ case 4: ++ for (i = 0; i < size; i++) ++ buf[i] = (i & 0xf) << 4; ++ break; ++ case 5: ++ for (i = 0; i < size; i++) ++ buf[i] = i & 0xf; ++ break; ++ case 6: ++ memset(buf, 0x00, size); ++ break; ++ case 7: ++ memset(buf, 0xff, size); ++ break; ++ case 8: ++ for (i = 0; i < size; i++) ++ buf[i] = i & 0xff; ++ break; ++ case 9: ++ for (i = 0; i < size; i++) ++ buf[i] = ~(i & 0xff); ++ break; ++ case 10: ++ for (i = 0; i < size % 2; i++) { ++ buf[i * 2] = (i >> 8) & 0xff; ++ buf[i * 2 + 1] = i & 0xff; ++ } ++ if (size & 0x1) ++ buf[i * 2] = (i >> 8) & 0xff; ++ break; ++ case 11: ++ for (i = 0; i < size % 2; i++) { ++ buf[i * 2] = ~((i >> 8) & 0xff); ++ buf[i * 2 + 1] = ~(i & 0xff); ++ } ++ if (size & 0x1) ++ buf[i * 2] = ~((i >> 8) & 0xff); ++ break; ++ case 12: ++ memset(buf, 0x00, size); ++ break; ++ case 13: ++ memset(buf, 0xff, size); ++ break; ++ } ++ ++ if ((variant >= 0) && (variant <= 7)) { ++ /* Write block number in the last two bytes of each 256-byte ++ * block, big endian for easier reading of the hexdump. ++ * Note that this wraps around for chips larger than 2^24 bytes ++ * (16 MB). ++ */ ++ for (i = 0; i < size / 256; i++) { ++ buf[i * 256 + 254] = (i >> 8) & 0xff; ++ buf[i * 256 + 255] = i & 0xff; ++ } ++ } ++ ++ return 0; ++} ++ ++int check_max_decode(enum chipbustype buses, uint32_t size) ++{ ++ int limitexceeded = 0; ++ if ((buses & CHIP_BUSTYPE_PARALLEL) && ++ (max_rom_decode.parallel < size)) { ++ limitexceeded++; ++ printf_debug("Chip size %u kB is bigger than supported " ++ "size %u kB of chipset/board/programmer " ++ "for %s interface, " ++ "probe/read/erase/write may fail. ", size / 1024, ++ max_rom_decode.parallel / 1024, "Parallel"); ++ } ++ if ((buses & CHIP_BUSTYPE_LPC) && (max_rom_decode.lpc < size)) { ++ limitexceeded++; ++ printf_debug("Chip size %u kB is bigger than supported " ++ "size %u kB of chipset/board/programmer " ++ "for %s interface, " ++ "probe/read/erase/write may fail. ", size / 1024, ++ max_rom_decode.lpc / 1024, "LPC"); ++ } ++ if ((buses & CHIP_BUSTYPE_FWH) && (max_rom_decode.fwh < size)) { ++ limitexceeded++; ++ printf_debug("Chip size %u kB is bigger than supported " ++ "size %u kB of chipset/board/programmer " ++ "for %s interface, " ++ "probe/read/erase/write may fail. ", size / 1024, ++ max_rom_decode.fwh / 1024, "FWH"); ++ } ++ if ((buses & CHIP_BUSTYPE_SPI) && (max_rom_decode.spi < size)) { ++ limitexceeded++; ++ printf_debug("Chip size %u kB is bigger than supported " ++ "size %u kB of chipset/board/programmer " ++ "for %s interface, " ++ "probe/read/erase/write may fail. ", size / 1024, ++ max_rom_decode.spi / 1024, "SPI"); ++ } ++ if (!limitexceeded) ++ return 0; ++ /* Sometimes chip and programmer have more than one bus in common, ++ * and the limit is not exceeded on all buses. Tell the user. ++ */ ++ if (bitcount(buses) > limitexceeded) ++ /* FIXME: This message is designed towards CLI users. */ ++ printf_debug("There is at least one common chip/programmer " ++ "interface which can support a chip of this size. " ++ "You can try --force at your own risk.\n"); ++ return 1; ++} ++ + struct flashchip *probe_flash(struct flashchip *first_flash, int force) + { + struct flashchip *flash; +- unsigned long base = 0, size; ++ unsigned long base = 0; ++ uint32_t size; ++ enum chipbustype buses_common; + char *tmp; + + for (flash = first_flash; flash && flash->name; flash++) { +@@ -369,7 +767,8 @@ struct flashchip *probe_flash(struct flashchip *first_flash, int force) + printf_debug("failed! flashrom has no probe function for this flash chip.\n"); + continue; + } +- if (!(buses_supported & flash->bustype)) { ++ buses_common = buses_supported & flash->bustype; ++ if (!buses_common) { + tmp = flashbuses_to_text(buses_supported); + printf_debug("skipped. Host bus type %s ", tmp); + free(tmp); +@@ -380,6 +779,7 @@ struct flashchip *probe_flash(struct flashchip *first_flash, int force) + } + + size = flash->total_size * 1024; ++ check_max_decode(buses_common, size); + + base = flashbase ? flashbase : (0xffffffff - size + 1); + flash->virtual_memory = (chipaddr)programmer_map_flash_region("flash chip", base, size); +@@ -434,7 +834,7 @@ int read_flash(struct flashchip *flash, char *filename) + printf("Error: No filename specified.\n"); + return 1; + } +- if ((image = fopen(filename, "w")) == NULL) { ++ if ((image = fopen(filename, "wb")) == NULL) { + perror(filename); + exit(1); + } +@@ -455,110 +855,164 @@ int read_flash(struct flashchip *flash, char *filename) + return 0; + } + ++/* This function shares a lot of its structure with erase_flash(). ++ * Even if an error is found, the function will keep going and check the rest. ++ */ ++int selfcheck_eraseblocks(struct flashchip *flash) ++{ ++ int i, j, k; ++ int ret = 0; ++ ++ for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { ++ unsigned int done = 0; ++ struct block_eraser eraser = flash->block_erasers[k]; ++ ++ for (i = 0; i < NUM_ERASEREGIONS; i++) { ++ /* Blocks with zero size are bugs in flashchips.c. */ ++ if (eraser.eraseblocks[i].count && ++ !eraser.eraseblocks[i].size) { ++ msg_gerr("ERROR: Flash chip %s erase function " ++ "%i region %i has size 0. Please report" ++ " a bug at flashrom@flashrom.org\n", ++ flash->name, k, i); ++ ret = 1; ++ } ++ /* Blocks with zero count are bugs in flashchips.c. */ ++ if (!eraser.eraseblocks[i].count && ++ eraser.eraseblocks[i].size) { ++ msg_gerr("ERROR: Flash chip %s erase function " ++ "%i region %i has count 0. Please report" ++ " a bug at flashrom@flashrom.org\n", ++ flash->name, k, i); ++ ret = 1; ++ } ++ done += eraser.eraseblocks[i].count * ++ eraser.eraseblocks[i].size; ++ } ++ /* Empty eraseblock definition with erase function. */ ++ if (!done && eraser.block_erase) ++ msg_pspew("Strange: Empty eraseblock definition with " ++ "non-empty erase function. Not an error.\n"); ++ if (!done) ++ continue; ++ if (done != flash->total_size * 1024) { ++ msg_gerr("ERROR: Flash chip %s erase function %i " ++ "region walking resulted in 0x%06x bytes total," ++ " expected 0x%06x bytes. Please report a bug at" ++ " flashrom@flashrom.org\n", flash->name, k, ++ done, flash->total_size * 1024); ++ ret = 1; ++ } ++ if (!eraser.block_erase) ++ continue; ++ /* Check if there are identical erase functions for different ++ * layouts. That would imply "magic" erase functions. The ++ * easiest way to check this is with function pointers. ++ */ ++ for (j = k + 1; j < NUM_ERASEFUNCTIONS; j++) ++ if (eraser.block_erase == ++ flash->block_erasers[j].block_erase) { ++ msg_gerr("ERROR: Flash chip %s erase function " ++ "%i and %i are identical. Please report" ++ " a bug at flashrom@flashrom.org\n", ++ flash->name, k, j); ++ ret = 1; ++ } ++ } ++ return ret; ++} ++ + int erase_flash(struct flashchip *flash) + { +- uint32_t erasedbytes; +- unsigned long size = flash->total_size * 1024; +- unsigned char *buf = calloc(size, sizeof(char)); ++ int i, j, k, ret = 0, found = 0; ++ unsigned int start, len; ++ + printf("Erasing flash chip... "); +- if (NULL == flash->erase) { +- printf("FAILED!\n"); ++ for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { ++ unsigned int done = 0; ++ struct block_eraser eraser = flash->block_erasers[k]; ++ ++ printf_debug("Looking at blockwise erase function %i... ", k); ++ if (!eraser.block_erase && !eraser.eraseblocks[0].count) { ++ printf_debug("not defined. " ++ "Looking for another erase function.\n"); ++ continue; ++ } ++ if (!eraser.block_erase && eraser.eraseblocks[0].count) { ++ printf_debug("eraseblock layout is known, but no " ++ "matching block erase function found. " ++ "Looking for another erase function.\n"); ++ continue; ++ } ++ if (eraser.block_erase && !eraser.eraseblocks[0].count) { ++ printf_debug("block erase function found, but " ++ "eraseblock layout is unknown. " ++ "Looking for another erase function.\n"); ++ continue; ++ } ++ found = 1; ++ printf_debug("trying... "); ++ for (i = 0; i < NUM_ERASEREGIONS; i++) { ++ /* count==0 for all automatically initialized array ++ * members so the loop below won't be executed for them. ++ */ ++ for (j = 0; j < eraser.eraseblocks[i].count; j++) { ++ start = done + eraser.eraseblocks[i].size * j; ++ len = eraser.eraseblocks[i].size; ++ printf_debug("0x%06x-0x%06x, ", start, ++ start + len - 1); ++ ret = eraser.block_erase(flash, start, len); ++ if (ret) ++ break; ++ } ++ if (ret) ++ break; ++ done += eraser.eraseblocks[i].count * ++ eraser.eraseblocks[i].size; ++ } ++ printf_debug("\n"); ++ /* If everything is OK, don't try another erase function. */ ++ if (!ret) ++ break; ++ } ++ /* If no block erase function was found or block erase failed, retry. */ ++ if ((!found || ret) && (flash->erase)) { ++ found = 1; ++ printf_debug("Trying whole-chip erase function... "); ++ ret = flash->erase(flash); ++ } ++ if (!found) { + fprintf(stderr, "ERROR: flashrom has no erase function for this flash chip.\n"); + return 1; + } +- flash->erase(flash); +- +- /* FIXME: The lines below are superfluous. We should check the result +- * of flash->erase(flash) instead. +- */ +- if (!flash->read) { +- printf("FAILED!\n"); +- fprintf(stderr, "ERROR: flashrom has no read function for this flash chip.\n"); +- return 1; +- } else +- flash->read(flash, buf, 0, size); + +- for (erasedbytes = 0; erasedbytes < size; erasedbytes++) +- if (0xff != buf[erasedbytes]) { +- printf("FAILED!\n"); +- fprintf(stderr, "ERROR at 0x%08x: Expected=0xff, Read=0x%02x\n", +- erasedbytes, buf[erasedbytes]); +- return 1; +- } +- printf("SUCCESS.\n"); +- return 0; ++ if (ret) { ++ fprintf(stderr, "FAILED!\n"); ++ } else { ++ printf("SUCCESS.\n"); ++ } ++ return ret; + } + +-void emergency_help_message() ++void emergency_help_message(void) + { + fprintf(stderr, "Your flash chip is in an unknown state.\n" +- "Get help on IRC at irc.freenode.net channel #flashrom or\n" +- "mail flashrom@flashrom.org\n" +- "------------------------------------------------------------\n" ++ "Get help on IRC at irc.freenode.net (channel #flashrom) or\n" ++ "mail flashrom@flashrom.org!\n--------------------" ++ "-----------------------------------------------------------\n" + "DO NOT REBOOT OR POWEROFF!\n"); + } + +-void usage(const char *name) ++/* The way to go if you want a delimited list of programmers*/ ++void list_programmers(char *delim) + { +- const char *pname; +- int pnamelen; +- int remaining = 0; + enum programmer p; +- +- printf("usage: %s [-VfLzhR] [-E|-r file|-w file|-v file] [-c chipname]\n" +- " [-m [vendor:]part] [-l file] [-i image] [-p programmer]\n\n", name); +- +- printf("Please note that the command line interface for flashrom will " +- "change before\nflashrom 1.0. Do not use flashrom in scripts " +- "or other automated tools without\nchecking that your flashrom" +- " version won't interpret options in a different way.\n\n"); +- +- printf +- (" -r | --read: read flash and save into file\n" +- " -w | --write: write file into flash\n" +- " -v | --verify: verify flash against file\n" +- " -n | --noverify: don't verify flash against file\n" +- " -E | --erase: erase flash device\n" +- " -V | --verbose: more verbose output\n" +- " -c | --chip : probe only for specified flash chip\n" +- " -m | --mainboard <[vendor:]part>: override mainboard settings\n" +- " -f | --force: force write without checking image\n" +- " -l | --layout : read ROM layout from file\n" +- " -i | --image : only flash image name from flash layout\n" +- " -L | --list-supported: print supported devices\n" +- " -z | --list-supported-wiki: print supported devices in wiki syntax\n" +- " -p | --programmer : specify the programmer device"); +- + for (p = 0; p < PROGRAMMER_INVALID; p++) { +- pname = programmer_table[p].name; +- pnamelen = strlen(pname); +- if (remaining - pnamelen - 2 < 0) { +- printf("\n "); +- remaining = 43; +- } else { +- printf(" "); +- remaining--; +- } +- if (p == 0) { +- printf("("); +- remaining--; +- } +- printf("%s", pname); +- remaining -= pnamelen; +- if (p < PROGRAMMER_INVALID - 1) { +- printf(","); +- remaining--; +- } else { +- printf(")\n"); +- } ++ printf("%s", programmer_table[p].name); ++ if (p < PROGRAMMER_INVALID - 1) ++ printf("%s", delim); + } +- +- printf( +- " -h | --help: print this help text\n" +- " -R | --version: print the version (release)\n" +- "\nYou can specify one of -E, -r, -w, -v or no operation. If no operation is\n" +- "specified, then all that happens is that flash info is dumped.\n\n"); +- exit(1); ++ printf("\n"); + } + + void print_version(void) +@@ -566,251 +1020,36 @@ void print_version(void) + printf("flashrom v%s\n", flashrom_version); + } + +-int main(int argc, char *argv[]) ++int selfcheck(void) + { +- uint8_t *buf; +- unsigned long size, numbytes; +- FILE *image; +- /* Probe for up to three flash chips. */ +- struct flashchip *flash, *flashes[3]; +- const char *name; +- int namelen; +- int opt; +- int option_index = 0; +- int force = 0; +- int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0; +- int dont_verify_it = 0, list_supported = 0, list_supported_wiki = 0; +- int operation_specified = 0; +- int ret = 0, i; +- +- static struct option long_options[] = { +- {"read", 0, 0, 'r'}, +- {"write", 0, 0, 'w'}, +- {"erase", 0, 0, 'E'}, +- {"verify", 0, 0, 'v'}, +- {"noverify", 0, 0, 'n'}, +- {"chip", 1, 0, 'c'}, +- {"mainboard", 1, 0, 'm'}, +- {"verbose", 0, 0, 'V'}, +- {"force", 0, 0, 'f'}, +- {"layout", 1, 0, 'l'}, +- {"image", 1, 0, 'i'}, +- {"list-supported", 0, 0, 'L'}, +- {"list-supported-wiki", 0, 0, 'z'}, +- {"programmer", 1, 0, 'p'}, +- {"help", 0, 0, 'h'}, +- {"version", 0, 0, 'R'}, +- {0, 0, 0, 0} +- }; +- +- char *filename = NULL; +- +- char *tempstr = NULL, *tempstr2 = NULL; +- +- print_version(); +- +- if (argc > 1) { +- /* Yes, print them. */ +- int i; +- printf_debug("The arguments are:\n"); +- for (i = 1; i < argc; ++i) +- printf_debug("%s\n", argv[i]); +- } ++ int ret = 0; ++ struct flashchip *flash; + +- /* Safety check. */ ++ /* Safety check. Instead of aborting after the first error, check ++ * if more errors exist. ++ */ + if (ARRAY_SIZE(programmer_table) - 1 != PROGRAMMER_INVALID) { + fprintf(stderr, "Programmer table miscompilation!\n"); +- exit(1); ++ ret = 1; + } + if (spi_programmer_count - 1 != SPI_CONTROLLER_INVALID) { + fprintf(stderr, "SPI programmer table miscompilation!\n"); +- exit(1); ++ ret = 1; + } +- +- setbuf(stdout, NULL); +- while ((opt = getopt_long(argc, argv, "rRwvnVEfc:m:l:i:p:Lzh", +- long_options, &option_index)) != EOF) { +- switch (opt) { +- case 'r': +- if (++operation_specified > 1) { +- fprintf(stderr, "More than one operation " +- "specified. Aborting.\n"); +- exit(1); +- } +- read_it = 1; +- break; +- case 'w': +- if (++operation_specified > 1) { +- fprintf(stderr, "More than one operation " +- "specified. Aborting.\n"); +- exit(1); +- } +- write_it = 1; +- break; +- case 'v': +- if (++operation_specified > 1) { +- fprintf(stderr, "More than one operation " +- "specified. Aborting.\n"); +- exit(1); +- } +- verify_it = 1; +- break; +- case 'n': +- dont_verify_it = 1; +- break; +- case 'c': +- chip_to_probe = strdup(optarg); +- break; +- case 'V': +- verbose = 1; +- break; +- case 'E': +- if (++operation_specified > 1) { +- fprintf(stderr, "More than one operation " +- "specified. Aborting.\n"); +- exit(1); +- } +- erase_it = 1; +- break; +- case 'm': +- tempstr = strdup(optarg); +- strtok(tempstr, ":"); +- tempstr2 = strtok(NULL, ":"); +- if (tempstr2) { +- lb_vendor = tempstr; +- lb_part = tempstr2; +- } else { +- lb_vendor = NULL; +- lb_part = tempstr; +- } +- break; +- case 'f': +- force = 1; +- break; +- case 'l': +- tempstr = strdup(optarg); +- if (read_romlayout(tempstr)) +- exit(1); +- break; +- case 'i': +- tempstr = strdup(optarg); +- find_romentry(tempstr); +- break; +- case 'L': +- list_supported = 1; +- break; +- case 'z': +- list_supported_wiki = 1; +- break; +- case 'p': +- for (programmer = 0; programmer < PROGRAMMER_INVALID; programmer++) { +- name = programmer_table[programmer].name; +- namelen = strlen(name); +- if (strncmp(optarg, name, namelen) == 0) { +- switch (optarg[namelen]) { +- case ':': +- programmer_param = strdup(optarg + namelen + 1); +- break; +- case '\0': +- break; +- default: +- /* The continue refers to the +- * for loop. It is here to be +- * able to differentiate between +- * foo and foobar. +- */ +- continue; +- } +- break; +- } +- } +- if (programmer == PROGRAMMER_INVALID) { +- printf("Error: Unknown programmer %s.\n", optarg); +- exit(1); +- } +- break; +- case 'R': +- /* print_version() is always called during startup. */ +- exit(0); +- break; +- case 'h': +- default: +- usage(argv[0]); +- break; +- } +- } +- +- if (list_supported) { +- print_supported_chips(); +- print_supported_chipsets(); +- print_supported_boards(); +- printf("\nSupported PCI devices flashrom can use " +- "as programmer:\n\n"); +- print_supported_pcidevs(nics_3com); +- print_supported_pcidevs(satas_sii); +- exit(0); +- } +- +- if (list_supported_wiki) { +- print_wiki_tables(); +- exit(0); +- } +- +- if (read_it && write_it) { +- printf("Error: -r and -w are mutually exclusive.\n"); +- usage(argv[0]); +- } +- +- if (optind < argc) +- filename = argv[optind++]; +- +- if (programmer_init()) { +- fprintf(stderr, "Error: Programmer initialization failed.\n"); +- exit(1); +- } +- +- myusec_calibrate_delay(); +- +- for (i = 0; i < ARRAY_SIZE(flashes); i++) { +- flashes[i] = +- probe_flash(i ? flashes[i - 1] + 1 : flashchips, 0); +- if (!flashes[i]) +- for (i++; i < ARRAY_SIZE(flashes); i++) +- flashes[i] = NULL; ++#if BITBANG_SPI_SUPPORT == 1 ++ if (bitbang_spi_master_count - 1 != BITBANG_SPI_INVALID) { ++ fprintf(stderr, "Bitbanging SPI master table miscompilation!\n"); ++ ret = 1; + } ++#endif ++ for (flash = flashchips; flash && flash->name; flash++) ++ if (selfcheck_eraseblocks(flash)) ++ ret = 1; ++ return ret; ++} + +- if (flashes[1]) { +- printf("Multiple flash chips were detected:"); +- for (i = 0; i < ARRAY_SIZE(flashes) && flashes[i]; i++) +- printf(" %s", flashes[i]->name); +- printf("\nPlease specify which chip to use with the -c option.\n"); +- exit(1); +- } else if (!flashes[0]) { +- printf("No EEPROM/flash device found.\n"); +- if (!force || !chip_to_probe) { +- printf("If you know which flash chip you have, and if this version of flashrom\n"); +- printf("supports a similar flash chip, you can try to force read your chip. Run:\n"); +- printf("flashrom -f -r -c similar_supported_flash_chip filename\n"); +- printf("\n"); +- printf("Note: flashrom can never write when the flash chip isn't found automatically.\n"); +- } +- if (force && read_it && chip_to_probe) { +- printf("Force read (-f -r -c) requested, forcing chip probe success:\n"); +- flashes[0] = probe_flash(flashchips, 1); +- if (!flashes[0]) { +- printf("flashrom does not support a flash chip named '%s'.\n", chip_to_probe); +- printf("Run flashrom -L to view the hardware supported in this flashrom version.\n"); +- exit(1); +- } +- printf("Please note that forced reads most likely contain garbage.\n"); +- return read_flash(flashes[0], filename); +- } +- // FIXME: flash writes stay enabled! +- exit(1); +- } +- +- flash = flashes[0]; +- ++void check_chip_supported(struct flashchip *flash) ++{ + if (TEST_OK_MASK != (flash->tested & TEST_OK_MASK)) { + printf("===\n"); + if (flash->tested & TEST_BAD_MASK) { +@@ -840,29 +1079,32 @@ int main(int argc, char *argv[]) + printf(" WRITE"); + printf("\n"); + } ++ /* FIXME: This message is designed towards CLI users. */ + printf("Please email a report to flashrom@flashrom.org if any " + "of the above operations\nwork correctly for you with " + "this flash part. Please include the flashrom\noutput " + "with the additional -V option for all operations you " + "tested (-V, -rV,\n-wV, -EV), and mention which " +- "mainboard you tested. Thanks for your help!\n===\n"); +- } +- +- if (!(read_it | write_it | verify_it | erase_it)) { +- printf("No operations were specified.\n"); +- // FIXME: flash writes stay enabled! +- exit(1); ++ "mainboard or programmer you tested. Thanks for your " ++ "help!\n===\n"); + } ++} + +- if (!filename && !erase_it) { +- printf("Error: No filename specified.\n"); +- // FIXME: flash writes stay enabled! +- exit(1); +- } ++int main(int argc, char *argv[]) ++{ ++ return cli_classic(argc, argv); ++} + +- /* Always verify write operations unless -n is used. */ +- if (write_it && !dont_verify_it) +- verify_it = 1; ++/* This function signature is horrible. We need to design a better interface, ++ * but right now it allows us to split off the CLI code. ++ */ ++int doit(struct flashchip *flash, int force, char *filename, int read_it, int write_it, int erase_it, int verify_it) ++{ ++ uint8_t *buf; ++ unsigned long numbytes; ++ FILE *image; ++ int ret = 0; ++ unsigned long size; + + size = flash->total_size * 1024; + buf = (uint8_t *) calloc(size, sizeof(char)); +@@ -872,6 +1114,7 @@ int main(int argc, char *argv[]) + fprintf(stderr, "Erase is not working on this chip. "); + if (!force) { + fprintf(stderr, "Aborting.\n"); ++ programmer_shutdown(); + return 1; + } else { + fprintf(stderr, "Continuing anyway.\n"); +@@ -879,11 +1122,14 @@ int main(int argc, char *argv[]) + } + if (erase_flash(flash)) { + emergency_help_message(); ++ programmer_shutdown(); + return 1; + } + } else if (read_it) { +- if (read_flash(flash, filename)) ++ if (read_flash(flash, filename)) { ++ programmer_shutdown(); + return 1; ++ } + } else { + struct stat image_stat; + +@@ -892,6 +1138,7 @@ int main(int argc, char *argv[]) + "and erase is needed for write. "); + if (!force) { + fprintf(stderr, "Aborting.\n"); ++ programmer_shutdown(); + return 1; + } else { + fprintf(stderr, "Continuing anyway.\n"); +@@ -901,29 +1148,36 @@ int main(int argc, char *argv[]) + fprintf(stderr, "Write is not working on this chip. "); + if (!force) { + fprintf(stderr, "Aborting.\n"); ++ programmer_shutdown(); + return 1; + } else { + fprintf(stderr, "Continuing anyway.\n"); + } + } +- if ((image = fopen(filename, "r")) == NULL) { ++ if ((image = fopen(filename, "rb")) == NULL) { + perror(filename); ++ programmer_shutdown(); + exit(1); + } + if (fstat(fileno(image), &image_stat) != 0) { + perror(filename); ++ programmer_shutdown(); + exit(1); + } + if (image_stat.st_size != flash->total_size * 1024) { + fprintf(stderr, "Error: Image size doesn't match\n"); ++ programmer_shutdown(); + exit(1); + } + + numbytes = fread(buf, 1, size, image); ++#if INTERNAL_SUPPORT == 1 + show_id(buf, size, force); ++#endif + fclose(image); + if (numbytes != size) { + fprintf(stderr, "Error: Failed to read file. Got %ld bytes, wanted %ld!\n", numbytes, size); ++ programmer_shutdown(); + return 1; + } + } +@@ -938,12 +1192,14 @@ int main(int argc, char *argv[]) + printf("Writing flash chip... "); + if (!flash->write) { + fprintf(stderr, "Error: flashrom has no write function for this flash chip.\n"); ++ programmer_shutdown(); + return 1; + } + ret = flash->write(flash, buf); + if (ret) { + fprintf(stderr, "FAILED!\n"); + emergency_help_message(); ++ programmer_shutdown(); + return 1; + } else { + printf("COMPLETE.\n"); +@@ -955,7 +1211,7 @@ int main(int argc, char *argv[]) + if (write_it) + programmer_delay(1000*1000); + ret = verify_flash(flash, buf); +- /* If we tried to write, and now we don't properly verify, we ++ /* If we tried to write, and verification now fails, we + * might have an emergency situation. + */ + if (ret && write_it) +diff --git a/ft2232_spi.c b/ft2232_spi.c +index 00490fe..6e029c2 100644 +--- a/ft2232_spi.c ++++ b/ft2232_spi.c +@@ -29,15 +29,20 @@ + #include "spi.h" + #include + +-/* the 'H' chips can run internally at either 12Mhz or 60Mhz. +- * the non-H chips can only run at 12Mhz. */ ++/* ++ * The 'H' chips can run internally at either 12MHz or 60MHz. ++ * The non-H chips can only run at 12MHz. ++ */ + #define CLOCK_5X 1 + +-/* in either case, the divisor is a simple integer clock divider. +- * if CLOCK_5X is set, this divisor divides 30Mhz, else it +- * divides 6Mhz */ +-#define DIVIDE_BY 3 // e.g. '3' will give either 10Mhz or 2Mhz spi clock ++/* ++ * In either case, the divisor is a simple integer clock divider. ++ * If CLOCK_5X is set, this divisor divides 30MHz, else it divides 6MHz. ++ */ ++#define DIVIDE_BY 3 /* e.g. '3' will give either 10MHz or 2MHz SPI clock. */ + ++#define BITMODE_BITBANG_NORMAL 1 ++#define BITMODE_BITBANG_SPI 2 + + static struct ftdi_context ftdic_context; + +@@ -46,7 +51,7 @@ int send_buf(struct ftdi_context *ftdic, const unsigned char *buf, int size) + int r; + r = ftdi_write_data(ftdic, (unsigned char *) buf, size); + if (r < 0) { +- fprintf(stderr, "ftdi_write_data: %d, %s\n", r, ++ msg_perr("ftdi_write_data: %d, %s\n", r, + ftdi_get_error_string(ftdic)); + return 1; + } +@@ -58,7 +63,7 @@ int get_buf(struct ftdi_context *ftdic, const unsigned char *buf, int size) + int r; + r = ftdi_read_data(ftdic, (unsigned char *) buf, size); + if (r < 0) { +- fprintf(stderr, "ftdi_read_data: %d, %s\n", r, ++ msg_perr("ftdi_read_data: %d, %s\n", r, + ftdi_get_error_string(ftdic)); + return 1; + } +@@ -70,14 +75,13 @@ int ft2232_spi_init(void) + int f; + struct ftdi_context *ftdic = &ftdic_context; + unsigned char buf[512]; +- unsigned char port_val = 0; + char *portpos = NULL; + int ft2232_type = FTDI_FT4232H; + enum ftdi_interface ft2232_interface = INTERFACE_B; + + if (ftdi_init(ftdic) < 0) { +- fprintf(stderr, "ftdi_init failed\n"); +- return EXIT_FAILURE; ++ msg_perr("ftdi_init failed\n"); ++ return EXIT_FAILURE; // TODO + } + + if (programmer_param && !strlen(programmer_param)) { +@@ -100,49 +104,49 @@ int ft2232_spi_init(void) + ft2232_interface = INTERFACE_B; + break; + default: +- fprintf(stderr, "Invalid interface specified, " ++ msg_perr("Invalid interface specified, " + "using default.\n"); + } + } + free(programmer_param); + } +- printf_debug("Using device type %s ", ++ msg_pdbg("Using device type %s ", + (ft2232_type == FTDI_FT2232H) ? "2232H" : "4232H"); +- printf_debug("interface %s\n", ++ msg_pdbg("interface %s\n", + (ft2232_interface == INTERFACE_A) ? "A" : "B"); + + f = ftdi_usb_open(ftdic, 0x0403, ft2232_type); + + if (f < 0 && f != -5) { +- fprintf(stderr, "Unable to open ftdi device: %d (%s)\n", f, ++ msg_perr("Unable to open FTDI device: %d (%s)\n", f, + ftdi_get_error_string(ftdic)); +- exit(-1); ++ exit(-1); // TODO + } + + if (ftdi_set_interface(ftdic, ft2232_interface) < 0) { +- fprintf(stderr, "Unable to select interface: %s\n", ++ msg_perr("Unable to select interface: %s\n", + ftdic->error_str); + } + + if (ftdi_usb_reset(ftdic) < 0) { +- fprintf(stderr, "Unable to reset ftdi device\n"); ++ msg_perr("Unable to reset FTDI device\n"); + } + + if (ftdi_set_latency_timer(ftdic, 2) < 0) { +- fprintf(stderr, "Unable to set latency timer\n"); ++ msg_perr("Unable to set latency timer\n"); + } + + if (ftdi_write_data_set_chunksize(ftdic, 512)) { +- fprintf(stderr, "Unable to set chunk size\n"); ++ msg_perr("Unable to set chunk size\n"); + } + +- if (ftdi_set_bitmode(ftdic, 0x00, 2) < 0) { +- fprintf(stderr, "Unable to set bitmode\n"); ++ if (ftdi_set_bitmode(ftdic, 0x00, BITMODE_BITBANG_SPI) < 0) { ++ msg_perr("Unable to set bitmode to SPI\n"); + } + + #if CLOCK_5X +- printf_debug("Disable divide-by-5 front stage\n"); +- buf[0] = 0x8a; /* disable divide-by-5 */ ++ msg_pdbg("Disable divide-by-5 front stage\n"); ++ buf[0] = 0x8a; /* Disable divide-by-5. */ + if (send_buf(ftdic, buf, 1)) + return -1; + #define MPSSE_CLK 60.0 +@@ -152,37 +156,36 @@ int ft2232_spi_init(void) + #define MPSSE_CLK 12.0 + + #endif +- printf_debug("Set clock divisor\n"); ++ msg_pdbg("Set clock divisor\n"); + buf[0] = 0x86; /* command "set divisor" */ + /* valueL/valueH are (desired_divisor - 1) */ +- buf[1] = (DIVIDE_BY-1) & 0xff; +- buf[2] = ((DIVIDE_BY-1) >> 8) & 0xff; ++ buf[1] = (DIVIDE_BY - 1) & 0xff; ++ buf[2] = ((DIVIDE_BY - 1) >> 8) & 0xff; + if (send_buf(ftdic, buf, 3)) + return -1; + +- printf("SPI clock is %fMHz\n", +- (double)(MPSSE_CLK / (((DIVIDE_BY-1) + 1) * 2))); ++ msg_pdbg("SPI clock is %fMHz\n", ++ (double)(MPSSE_CLK / (((DIVIDE_BY - 1) + 1) * 2))); + +- /* Disconnect TDI/DO to TDO/DI for Loopback */ +- printf_debug("No loopback of tdi/do tdo/di\n"); ++ /* Disconnect TDI/DO to TDO/DI for loopback. */ ++ msg_pdbg("No loopback of TDI/DO TDO/DI\n"); + buf[0] = 0x85; + if (send_buf(ftdic, buf, 1)) + return -1; + +- printf_debug("Set data bits\n"); ++ msg_pdbg("Set data bits\n"); + /* Set data bits low-byte command: + * value: 0x08 CS=high, DI=low, DO=low, SK=low + * dir: 0x0b CS=output, DI=input, DO=output, SK=output + */ + #define CS_BIT 0x08 +- + buf[0] = SET_BITS_LOW; +- buf[1] = (port_val = CS_BIT); ++ buf[1] = CS_BIT; + buf[2] = 0x0b; + if (send_buf(ftdic, buf, 3)) + return -1; + +- printf_debug("\nft2232 chosen\n"); ++ // msg_pdbg("\nft2232 chosen\n"); + + buses_supported = CHIP_BUSTYPE_SPI; + spi_controller = SPI_CONTROLLER_FT2232; +@@ -195,39 +198,47 @@ int ft2232_spi_send_command(unsigned int writecnt, unsigned int readcnt, + { + struct ftdi_context *ftdic = &ftdic_context; + static unsigned char *buf = NULL; +- unsigned char port_val = 0; +- int i, ret = 0; ++ /* failed is special. We use bitwise ops, but it is essentially bool. */ ++ int i = 0, ret = 0, failed = 0; ++ int bufsize; ++ static int oldbufsize = 0; + + if (writecnt > 65536 || readcnt > 65536) + return SPI_INVALID_LENGTH; + +- buf = realloc(buf, writecnt + readcnt + 100); +- if (!buf) { +- fprintf(stderr, "Out of memory!\n"); +- exit(1); ++ /* buf is not used for the response from the chip. */ ++ bufsize = max(writecnt + 9, 260 + 9); ++ /* Never shrink. realloc() calls are expensive. */ ++ if (bufsize > oldbufsize) { ++ buf = realloc(buf, bufsize); ++ if (!buf) { ++ msg_perr("Out of memory!\n"); ++ exit(1); ++ } ++ oldbufsize = bufsize; + } + +- i = 0; +- +- /* minimize USB transfers by packing as many commands +- * as possible together. if we're not expecting to +- * read, we can assert CS, write, and deassert CS all +- * in one shot. if reading, we do three separate +- * operations. */ +- printf_debug("Assert CS#\n"); ++ /* ++ * Minimize USB transfers by packing as many commands as possible ++ * together. If we're not expecting to read, we can assert CS#, write, ++ * and deassert CS# all in one shot. If reading, we do three separate ++ * operations. ++ */ ++ msg_pspew("Assert CS#\n"); + buf[i++] = SET_BITS_LOW; +- buf[i++] = (port_val &= ~CS_BIT); ++ buf[i++] = 0 & ~CS_BIT; /* assertive */ + buf[i++] = 0x0b; + + if (writecnt) { + buf[i++] = 0x11; + buf[i++] = (writecnt - 1) & 0xff; + buf[i++] = ((writecnt - 1) >> 8) & 0xff; +- memcpy(buf+i, writearr, writecnt); ++ memcpy(buf + i, writearr, writecnt); + i += writecnt; + } + +- /* optionally terminate this batch of commands with a ++ /* ++ * Optionally terminate this batch of commands with a + * read command, then do the fetch of the results. + */ + if (readcnt) { +@@ -235,26 +246,36 @@ int ft2232_spi_send_command(unsigned int writecnt, unsigned int readcnt, + buf[i++] = (readcnt - 1) & 0xff; + buf[i++] = ((readcnt - 1) >> 8) & 0xff; + ret = send_buf(ftdic, buf, i); ++ failed = ret; ++ /* We can't abort here, we still have to deassert CS#. */ ++ if (ret) ++ msg_perr("send_buf failed before read: %i\n", ++ ret); + i = 0; +- if (ret) goto deassert_cs; +- +- /* FIXME: This is unreliable. There's no guarantee that we read +- * the response directly after sending the read command. +- * We may be scheduled out etc. +- */ +- ret = get_buf(ftdic, readarr, readcnt); +- ++ if (ret == 0) { ++ /* ++ * FIXME: This is unreliable. There's no guarantee that ++ * we read the response directly after sending the read ++ * command. We may be scheduled out etc. ++ */ ++ ret = get_buf(ftdic, readarr, readcnt); ++ failed |= ret; ++ /* We can't abort here either. */ ++ if (ret) ++ msg_perr("get_buf failed: %i\n", ret); ++ } + } + +-deassert_cs: +- printf_debug("De-assert CS#\n"); ++ msg_pspew("De-assert CS#\n"); + buf[i++] = SET_BITS_LOW; +- buf[i++] = (port_val |= CS_BIT); ++ buf[i++] = CS_BIT; + buf[i++] = 0x0b; +- if (send_buf(ftdic, buf, i)) +- return -1; ++ ret = send_buf(ftdic, buf, i); ++ failed |= ret; ++ if (ret) ++ msg_perr("send_buf failed at end: %i\n", ret); + +- return ret; ++ return failed ? -1 : 0; + } + + int ft2232_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len) +@@ -269,14 +290,14 @@ int ft2232_spi_write_256(struct flashchip *flash, uint8_t *buf) + int i; + + spi_disable_blockprotect(); +- /* Erase first */ +- printf("Erasing flash before programming... "); +- if (flash->erase(flash)) { +- fprintf(stderr, "ERASE FAILED!\n"); ++ /* Erase first. */ ++ msg_pinfo("Erasing flash before programming... "); ++ if (erase_flash(flash)) { ++ msg_perr("ERASE FAILED!\n"); + return -1; + } +- printf("done.\n"); +- printf_debug("total_size is %d\n", total_size); ++ msg_pinfo("done.\n"); ++ msg_pdbg("total_size is %d\n", total_size); + for (i = 0; i < total_size; i += 256) { + int l, r; + if (i + 256 <= total_size) +@@ -285,10 +306,10 @@ int ft2232_spi_write_256(struct flashchip *flash, uint8_t *buf) + l = total_size - i; + + if ((r = spi_nbyte_program(i, &buf[i], l))) { +- fprintf(stderr, "%s: write fail %d\n", __FUNCTION__, r); ++ msg_perr("%s: write fail %d\n", __func__, r); + return 1; + } +- ++ + while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP) + /* loop */; + } +diff --git a/gfxnvidia.c b/gfxnvidia.c +new file mode 100644 +index 0000000..cc66ace +--- /dev/null ++++ b/gfxnvidia.c +@@ -0,0 +1,104 @@ ++/* ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2009 Uwe Hermann ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include "flash.h" ++ ++#define PCI_VENDOR_ID_NVIDIA 0x10de ++ ++uint8_t *nvidia_bar; ++ ++struct pcidev_status gfx_nvidia[] = { ++ {0x10de, 0x0010, PCI_NT, "NVIDIA", "Mutara V08 [NV2]" }, ++ {0x10de, 0x0018, PCI_NT, "NVIDIA", "RIVA 128" }, ++ {0x10de, 0x0020, PCI_NT, "NVIDIA", "RIVA TNT" }, ++ {0x10de, 0x0028, PCI_NT, "NVIDIA", "RIVA TNT2/TNT2 Pro" }, ++ {0x10de, 0x0029, PCI_NT, "NVIDIA", "RIVA TNT2 Ultra" }, ++ {0x10de, 0x002c, PCI_NT, "NVIDIA", "Vanta/Vanta LT" }, ++ {0x10de, 0x002d, PCI_OK, "NVIDIA", "RIVA TNT2 Model 64/Model 64 Pro" }, ++ {0x10de, 0x00a0, PCI_NT, "NVIDIA", "Aladdin TNT2" }, ++ {0x10de, 0x0100, PCI_NT, "NVIDIA", "GeForce 256" }, ++ {0x10de, 0x0101, PCI_NT, "NVIDIA", "GeForce DDR" }, ++ {0x10de, 0x0103, PCI_NT, "NVIDIA", "Quadro" }, ++ {0x10de, 0x0110, PCI_NT, "NVIDIA", "GeForce2 MX" }, ++ {0x10de, 0x0111, PCI_NT, "NVIDIA", "GeForce2 MX" }, ++ {0x10de, 0x0112, PCI_NT, "NVIDIA", "GeForce2 GO" }, ++ {0x10de, 0x0113, PCI_NT, "NVIDIA", "Quadro2 MXR" }, ++ {0x10de, 0x0150, PCI_NT, "NVIDIA", "GeForce2 GTS/Pro" }, ++ {0x10de, 0x0151, PCI_NT, "NVIDIA", "GeForce2 GTS" }, ++ {0x10de, 0x0152, PCI_NT, "NVIDIA", "GeForce2 Ultra" }, ++ {0x10de, 0x0153, PCI_NT, "NVIDIA", "Quadro2 Pro" }, ++ {0x10de, 0x0200, PCI_NT, "NVIDIA", "GeForce 3 nFX" }, ++ {0x10de, 0x0201, PCI_NT, "NVIDIA", "GeForce 3 nFX" }, ++ {0x10de, 0x0202, PCI_NT, "NVIDIA", "GeForce 3 nFX Ultra" }, ++ {0x10de, 0x0203, PCI_NT, "NVIDIA", "Quadro 3 DDC" }, ++ ++ {}, ++}; ++ ++int gfxnvidia_init(void) ++{ ++ uint32_t reg32; ++ ++ get_io_perms(); ++ ++ io_base_addr = pcidev_init(PCI_VENDOR_ID_NVIDIA, PCI_BASE_ADDRESS_0, ++ gfx_nvidia, programmer_param); ++ io_base_addr += 0x300000; ++ msg_pinfo("Detected NVIDIA I/O base address: 0x%x.\n", io_base_addr); ++ ++ /* Allow access to flash interface (will disable screen). */ ++ reg32 = pci_read_long(pcidev_dev, 0x50); ++ reg32 &= ~(1 << 0); ++ pci_write_long(pcidev_dev, 0x50, reg32); ++ ++ nvidia_bar = physmap("NVIDIA", io_base_addr, 16 * 1024 * 1024); ++ ++ buses_supported = CHIP_BUSTYPE_PARALLEL; ++ ++ return 0; ++} ++ ++int gfxnvidia_shutdown(void) ++{ ++ uint32_t reg32; ++ ++ /* Disallow access to flash interface (and re-enable screen). */ ++ reg32 = pci_read_long(pcidev_dev, 0x50); ++ reg32 |= (1 << 0); ++ pci_write_long(pcidev_dev, 0x50, reg32); ++ ++ free(programmer_param); ++ pci_cleanup(pacc); ++ release_io_perms(); ++ return 0; ++} ++ ++void gfxnvidia_chip_writeb(uint8_t val, chipaddr addr) ++{ ++ mmio_writeb(val, nvidia_bar + addr); ++} ++ ++uint8_t gfxnvidia_chip_readb(const chipaddr addr) ++{ ++ return mmio_readb(nvidia_bar + addr); ++} +diff --git a/hwaccess.h b/hwaccess.h +new file mode 100644 +index 0000000..0729226 +--- /dev/null ++++ b/hwaccess.h +@@ -0,0 +1,156 @@ ++/* ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2009 Carl-Daniel Hailfinger ++ * ++ * 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; version 2 of the License. ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * ++ * Header file for hardware access and OS abstraction. Included from flash.h. ++ */ ++ ++#ifndef __HWACCESS_H__ ++#define __HWACCESS_H__ 1 ++ ++#if defined(__GLIBC__) ++#include ++#endif ++#if NEED_PCI == 1 ++#include ++#endif ++ ++/* for iopl and outb under Solaris */ ++#if defined (__sun) && (defined(__i386) || defined(__amd64)) ++#include ++#include ++#include ++#include ++#endif ++ ++#if (defined(__MACH__) && defined(__APPLE__)) ++#define __DARWIN__ ++#endif ++ ++#if defined(__FreeBSD__) || defined(__DragonFly__) ++ #include ++ #define off64_t off_t ++ #define lseek64 lseek ++ #define OUTB(x, y) do { u_int outb_tmp = (y); outb(outb_tmp, (x)); } while (0) ++ #define OUTW(x, y) do { u_int outw_tmp = (y); outw(outw_tmp, (x)); } while (0) ++ #define OUTL(x, y) do { u_int outl_tmp = (y); outl(outl_tmp, (x)); } while (0) ++ #define INB(x) __extension__ ({ u_int inb_tmp = (x); inb(inb_tmp); }) ++ #define INW(x) __extension__ ({ u_int inw_tmp = (x); inw(inw_tmp); }) ++ #define INL(x) __extension__ ({ u_int inl_tmp = (x); inl(inl_tmp); }) ++#else ++#if defined(__DARWIN__) ++ #include ++ #define off64_t off_t ++ #define lseek64 lseek ++#endif ++#if defined (__sun) && (defined(__i386) || defined(__amd64)) ++ /* Note different order for outb */ ++ #define OUTB(x,y) outb(y, x) ++ #define OUTW(x,y) outw(y, x) ++ #define OUTL(x,y) outl(y, x) ++ #define INB inb ++ #define INW inw ++ #define INL inl ++#else ++ #define OUTB outb ++ #define OUTW outw ++ #define OUTL outl ++ #define INB inb ++ #define INW inw ++ #define INL inl ++#endif ++#endif ++ ++#if defined(__NetBSD__) ++ #define off64_t off_t ++ #define lseek64 lseek ++ #if defined(__i386__) || defined(__x86_64__) ++ #include ++ #include ++ #if defined(__i386__) ++ #define iopl i386_iopl ++ #elif defined(__x86_64__) ++ #define iopl x86_64_iopl ++ #endif ++ #include ++ ++static inline void ++outb(uint8_t value, uint16_t port) ++{ ++ asm volatile ("outb %b0,%w1": :"a" (value), "Nd" (port)); ++} ++ ++static inline uint8_t ++inb(uint16_t port) ++{ ++ uint8_t value; ++ asm volatile ("inb %w1,%0":"=a" (value):"Nd" (port)); ++ return value; ++} ++ ++static inline void ++outw(uint16_t value, uint16_t port) ++{ ++ asm volatile ("outw %w0,%w1": :"a" (value), "Nd" (port)); ++} ++ ++static inline uint16_t ++inw(uint16_t port) ++{ ++ uint16_t value; ++ asm volatile ("inw %w1,%0":"=a" (value):"Nd" (port)); ++ return value; ++} ++ ++static inline void ++outl(uint32_t value, uint16_t port) ++{ ++ asm volatile ("outl %0,%w1": :"a" (value), "Nd" (port)); ++} ++ ++static inline uint32_t ++inl(uint16_t port) ++{ ++ uint32_t value; ++ asm volatile ("inl %1,%0":"=a" (value):"Nd" (port)); ++ return value; ++} ++ #endif ++#endif ++ ++#if defined(__FreeBSD__) || defined(__DragonFly__) ++extern int io_fd; ++#endif ++ ++#if !defined(__DARWIN__) && !defined(__FreeBSD__) && !defined(__DragonFly__) ++typedef struct { uint32_t hi, lo; } msr_t; ++msr_t rdmsr(int addr); ++int wrmsr(int addr, msr_t msr); ++#endif ++#if defined(__FreeBSD__) || defined(__DragonFly__) ++/* FreeBSD already has conflicting definitions for wrmsr/rdmsr. */ ++#undef rdmsr ++#undef wrmsr ++#define rdmsr freebsd_rdmsr ++#define wrmsr freebsd_wrmsr ++typedef struct { uint32_t hi, lo; } msr_t; ++msr_t freebsd_rdmsr(int addr); ++int freebsd_wrmsr(int addr, msr_t msr); ++#endif ++ ++#endif /* !__HWACCESS_H__ */ +diff --git a/ichspi.c b/ichspi.c +index 831dd9d..a6bf154 100644 +--- a/ichspi.c ++++ b/ichspi.c +@@ -36,6 +36,17 @@ + #include "flash.h" + #include "spi.h" + ++/* Change this to #define if you want lowlevel debugging of commands ++ * sent to the ICH/VIA SPI controller. ++ */ ++#undef COMM_DEBUG ++ ++#ifdef COMM_DEBUG ++#define msg_comm_debug printf_debug ++#else ++#define msg_comm_debug(...) do {} while (0) ++#endif ++ + /* ICH9 controller register definition */ + #define ICH9_REG_FADDR 0x08 /* 32 Bits */ + #define ICH9_REG_FDATA0 0x10 /* 64 Bytes */ +@@ -216,7 +227,7 @@ static int generate_opcodes(OPCODES * op) + uint32_t opmenu[2]; + + if (op == NULL) { +- printf_debug("\n%s: null OPCODES pointer!\n", __FUNCTION__); ++ printf_debug("\n%s: null OPCODES pointer!\n", __func__); + return -1; + } + +@@ -235,7 +246,7 @@ static int generate_opcodes(OPCODES * op) + opmenu[1] = REGREAD32(ICH9_REG_OPMENU + 4); + break; + default: +- printf_debug("%s: unsupported chipset\n", __FUNCTION__); ++ printf_debug("%s: unsupported chipset\n", __func__); + return -1; + } + +@@ -317,7 +328,7 @@ int program_opcodes(OPCODES * op) + REGWRITE32(ICH9_REG_OPMENU + 4, opmenu[1]); + break; + default: +- printf_debug("%s: unsupported chipset\n", __FUNCTION__); ++ printf_debug("%s: unsupported chipset\n", __func__); + return -1; + } + +@@ -610,7 +621,7 @@ static int run_opcode(OPCODE op, uint32_t offset, + return SPI_INVALID_LENGTH; + return ich9_run_opcode(op, offset, datalength, data); + default: +- printf_debug("%s: unsupported chipset\n", __FUNCTION__); ++ printf_debug("%s: unsupported chipset\n", __func__); + } + + /* If we ever get here, something really weird happened */ +@@ -624,7 +635,7 @@ static int ich_spi_write_page(struct flashchip *flash, uint8_t * bytes, + uint32_t remaining = page_size; + int towrite; + +- printf_debug("ich_spi_write_page: offset=%d, number=%d, buf=%p\n", ++ msg_comm_debug("ich_spi_write_page: offset=%d, number=%d, buf=%p\n", + offset, page_size, bytes); + + for (; remaining > 0; remaining -= towrite) { +@@ -658,20 +669,16 @@ int ich_spi_write_256(struct flashchip *flash, uint8_t * buf) + int maxdata = 64; + + spi_disable_blockprotect(); ++ /* Erase first */ ++ printf("Erasing flash before programming... "); ++ if (erase_flash(flash)) { ++ fprintf(stderr, "ERASE FAILED!\n"); ++ return -1; ++ } ++ printf("done.\n"); + + printf("Programming page: \n"); +- + for (i = 0; i < total_size / erase_size; i++) { +- /* FIMXE: call the chip-specific spi_block_erase_XX instead. +- * For this, we need to add a block erase function to +- * struct flashchip. +- */ +- rc = spi_block_erase_d8(flash, i * erase_size, erase_size); +- if (rc) { +- printf("Error erasing block at 0x%x\n", i); +- break; +- } +- + if (spi_controller == SPI_CONTROLLER_VIA) + maxdata = 16; + +@@ -742,22 +749,26 @@ int ich_spi_send_command(unsigned int writecnt, unsigned int readcnt, + return result; + } + +-int ich_spi_send_multicommand(struct spi_command *spicommands) ++int ich_spi_send_multicommand(struct spi_command *cmds) + { + int ret = 0; +- while ((spicommands->writecnt || spicommands->readcnt) && !ret) { +- ret = ich_spi_send_command(spicommands->writecnt, spicommands->readcnt, +- spicommands->writearr, spicommands->readarr); +- /* This awful hack needs to be smarter. +- */ +- if ((ret == SPI_INVALID_OPCODE) && +- ((spicommands->writearr[0] == JEDEC_WREN) || +- (spicommands->writearr[0] == JEDEC_EWSR))) { +- printf_debug(" due to SPI master limitation, ignoring" +- " and hoping it will be run as PREOP\n"); +- ret = 0; +- } +- spicommands++; ++ int oppos, preoppos; ++ for (; (cmds->writecnt || cmds->readcnt) && !ret; cmds++) { ++ /* Is the next command valid or a terminator? */ ++ if ((cmds + 1)->writecnt || (cmds + 1)->readcnt) { ++ preoppos = find_preop(curopcodes, cmds->writearr[0]); ++ oppos = find_opcode(curopcodes, (cmds + 1)->writearr[0]); ++ /* Is the opcode of the current command listed in the ++ * ICH struct OPCODES as associated preopcode for the ++ * opcode of the next command? ++ */ ++ if ((oppos != -1) && (preoppos != -1) && ++ ((curopcodes->opcode[oppos].atomic - 1) == preoppos)) ++ continue; ++ } ++ ++ ret = ich_spi_send_command(cmds->writecnt, cmds->readcnt, ++ cmds->writearr, cmds->readarr); + } + return ret; + } +diff --git a/internal.c b/internal.c +index a3b2ae5..935240d 100644 +--- a/internal.c ++++ b/internal.c +@@ -31,6 +31,7 @@ + int io_fd; + #endif + ++#if NEED_PCI == 1 + struct pci_dev *pci_dev_find_filter(struct pci_filter filter) + { + struct pci_dev *temp; +@@ -42,6 +43,26 @@ struct pci_dev *pci_dev_find_filter(struct pci_filter filter) + return NULL; + } + ++struct pci_dev *pci_dev_find_vendorclass(uint16_t vendor, uint16_t class) ++{ ++ struct pci_dev *temp; ++ struct pci_filter filter; ++ uint16_t tmp2; ++ ++ pci_filter_init(NULL, &filter); ++ filter.vendor = vendor; ++ ++ for (temp = pacc->devices; temp; temp = temp->next) ++ if (pci_filter_match(&filter, temp)) { ++ /* Read PCI class */ ++ tmp2 = pci_read_word(temp, 0x0a); ++ if (tmp2 == class) ++ return temp; ++ } ++ ++ return NULL; ++} ++ + struct pci_dev *pci_dev_find(uint16_t vendor, uint16_t device) + { + struct pci_dev *temp; +@@ -79,6 +100,7 @@ struct pci_dev *pci_card_find(uint16_t vendor, uint16_t device, + + return NULL; + } ++#endif + + void get_io_perms(void) + { +@@ -102,6 +124,18 @@ void release_io_perms(void) + #endif + } + ++#if INTERNAL_SUPPORT == 1 ++struct superio superio = {}; ++ ++void probe_superio(void) ++{ ++ superio = probe_superio_ite(); ++#if 0 /* Winbond SuperI/O code is not yet available. */ ++ if (superio.vendor == SUPERIO_VENDOR_NONE) ++ superio = probe_superio_winbond(); ++#endif ++} ++ + int internal_init(void) + { + int ret = 0; +@@ -118,6 +152,10 @@ int internal_init(void) + * mainboard specific flash enable sequence. + */ + coreboot_init(); ++ dmi_init(); ++ ++ /* Probe for the SuperI/O chip and fill global struct superio. */ ++ probe_superio(); + + /* try to enable it. Failure IS an option, since not all motherboards + * really need this to be done, etc., etc. +@@ -143,6 +181,7 @@ int internal_shutdown(void) + + return 0; + } ++#endif + + void internal_chip_writeb(uint8_t val, chipaddr addr) + { +@@ -209,85 +248,3 @@ uint32_t mmio_readl(void *addr) + { + return *(volatile uint32_t *) addr; + } +- +-void internal_delay(int usecs) +-{ +- /* If the delay is >1 s, use usleep because timing does not need to +- * be so precise. +- */ +- if (usecs > 1000000) { +- usleep(usecs); +- } else { +- myusec_delay(usecs); +- } +-} +- +-/* Fallback shutdown() for programmers which don't need special handling */ +-int fallback_shutdown(void) +-{ +- return 0; +-} +- +-/* Fallback map() for programmers which don't need special handling */ +-void *fallback_map(const char *descr, unsigned long phys_addr, size_t len) +-{ +- return 0; +-} +- +-/* Fallback unmap() for programmers which don't need special handling */ +-void fallback_unmap(void *virt_addr, size_t len) +-{ +-} +- +-/* No-op fallback for drivers not supporting addr/data pair accesses */ +-void fallback_chip_writeb(uint8_t val, chipaddr addr) +-{ +-} +- +-/* Little-endian fallback for drivers not supporting 16 bit accesses */ +-void fallback_chip_writew(uint16_t val, chipaddr addr) +-{ +- chip_writeb(val & 0xff, addr); +- chip_writeb((val >> 8) & 0xff, addr + 1); +-} +- +-/* Little-endian fallback for drivers not supporting 16 bit accesses */ +-uint16_t fallback_chip_readw(const chipaddr addr) +-{ +- uint16_t val; +- val = chip_readb(addr); +- val |= chip_readb(addr + 1) << 8; +- return val; +-} +- +-/* Little-endian fallback for drivers not supporting 32 bit accesses */ +-void fallback_chip_writel(uint32_t val, chipaddr addr) +-{ +- chip_writew(val & 0xffff, addr); +- chip_writew((val >> 16) & 0xffff, addr + 2); +-} +- +-/* Little-endian fallback for drivers not supporting 32 bit accesses */ +-uint32_t fallback_chip_readl(const chipaddr addr) +-{ +- uint32_t val; +- val = chip_readw(addr); +- val |= chip_readw(addr + 2) << 16; +- return val; +-} +- +-void fallback_chip_writen(uint8_t *buf, chipaddr addr, size_t len) +-{ +- size_t i; +- for (i = 0; i < len; i++) +- chip_writeb(buf[i], addr + i); +- return; +-} +- +-void fallback_chip_readn(uint8_t *buf, chipaddr addr, size_t len) +-{ +- size_t i; +- for (i = 0; i < len; i++) +- buf[i] = chip_readb(addr + i); +- return; +-} +diff --git a/it87spi.c b/it87spi.c +index 299725c..fcbf086 100644 +--- a/it87spi.c ++++ b/it87spi.c +@@ -54,46 +54,82 @@ void exit_conf_mode_ite(uint16_t port) + sio_write(port, 0x02, 0x02); + } + +-static uint16_t find_ite_spi_flash_port(uint16_t port) ++uint16_t probe_id_ite(uint16_t port) + { +- uint8_t tmp = 0; +- char *portpos = NULL; +- uint16_t id, flashport = 0; ++ uint16_t id; + + enter_conf_mode_ite(port); +- + id = sio_read(port, CHIP_ID_BYTE1_REG) << 8; + id |= sio_read(port, CHIP_ID_BYTE2_REG); ++ exit_conf_mode_ite(port); + +- /* TODO: Handle more IT87xx if they support flash translation */ +- if (0x8716 == id || 0x8718 == id) { ++ return id; ++} ++ ++struct superio probe_superio_ite(void) ++{ ++ struct superio ret = {}; ++ uint16_t ite_ports[] = {ITE_SUPERIO_PORT1, ITE_SUPERIO_PORT2, 0}; ++ uint16_t *i = ite_ports; ++ ++ ret.vendor = SUPERIO_VENDOR_ITE; ++ for (; *i; i++) { ++ ret.port = *i; ++ ret.model = probe_id_ite(ret.port); ++ switch (ret.model >> 8) { ++ case 0x82: ++ case 0x86: ++ case 0x87: ++ msg_pinfo("Found ITE SuperI/O, id %04hx\n", ++ ret.model); ++ return ret; ++ } ++ } ++ ++ /* No good ID found. */ ++ ret.vendor = SUPERIO_VENDOR_NONE; ++ ret.port = 0; ++ ret.model = 0; ++ return ret; ++} ++ ++static uint16_t find_ite_spi_flash_port(uint16_t port, uint16_t id) ++{ ++ uint8_t tmp = 0; ++ char *portpos = NULL; ++ uint16_t flashport = 0; ++ ++ switch (id) { ++ case 0x8716: ++ case 0x8718: ++ enter_conf_mode_ite(port); + /* NOLDN, reg 0x24, mask out lowest bit (suspend) */ + tmp = sio_read(port, 0x24) & 0xFE; +- printf("Serial flash segment 0x%08x-0x%08x %sabled\n", ++ msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n", + 0xFFFE0000, 0xFFFFFFFF, (tmp & 1 << 1) ? "en" : "dis"); +- printf("Serial flash segment 0x%08x-0x%08x %sabled\n", ++ msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n", + 0x000E0000, 0x000FFFFF, (tmp & 1 << 1) ? "en" : "dis"); +- printf("Serial flash segment 0x%08x-0x%08x %sabled\n", ++ msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n", + 0xFFEE0000, 0xFFEFFFFF, (tmp & 1 << 2) ? "en" : "dis"); +- printf("Serial flash segment 0x%08x-0x%08x %sabled\n", ++ msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n", + 0xFFF80000, 0xFFFEFFFF, (tmp & 1 << 3) ? "en" : "dis"); +- printf("LPC write to serial flash %sabled\n", ++ msg_pdbg("LPC write to serial flash %sabled\n", + (tmp & 1 << 4) ? "en" : "dis"); + /* The LPC->SPI force write enable below only makes sense for + * non-programmer mode. + */ + /* If any serial flash segment is enabled, enable writing. */ + if ((tmp & 0xe) && (!(tmp & 1 << 4))) { +- printf("Enabling LPC write to serial flash\n"); ++ msg_pdbg("Enabling LPC write to serial flash\n"); + tmp |= 1 << 4; + sio_write(port, 0x24, tmp); + } +- printf("Serial flash pin %i\n", (tmp & 1 << 5) ? 87 : 29); ++ msg_pdbg("Serial flash pin %i\n", (tmp & 1 << 5) ? 87 : 29); + /* LDN 0x7, reg 0x64/0x65 */ + sio_write(port, 0x07, 0x7); + flashport = sio_read(port, 0x64) << 8; + flashport |= sio_read(port, 0x65); +- printf("Serial flash port 0x%04x\n", flashport); ++ msg_pdbg("Serial flash port 0x%04x\n", flashport); + if (programmer_param && !strlen(programmer_param)) { + free(programmer_param); + programmer_param = NULL; +@@ -101,21 +137,25 @@ static uint16_t find_ite_spi_flash_port(uint16_t port) + if (programmer_param && (portpos = strstr(programmer_param, "port="))) { + portpos += 5; + flashport = strtol(portpos, (char **)NULL, 0); +- printf("Forcing serial flash port 0x%04x\n", flashport); ++ msg_pinfo("Forcing serial flash port 0x%04x\n", flashport); + sio_write(port, 0x64, (flashport >> 8)); + sio_write(port, 0x65, (flashport & 0xff)); + } ++ exit_conf_mode_ite(port); ++ break; ++ /* TODO: Handle more IT87xx if they support flash translation */ ++ default: ++ msg_pinfo("SuperI/O ID %04hx is not on the controller list.\n", id); + } +- exit_conf_mode_ite(port); + return flashport; + } + + int it87spi_common_init(void) + { +- it8716f_flashport = find_ite_spi_flash_port(ITE_SUPERIO_PORT1); ++ if (superio.vendor != SUPERIO_VENDOR_ITE) ++ return 1; + +- if (!it8716f_flashport) +- it8716f_flashport = find_ite_spi_flash_port(ITE_SUPERIO_PORT2); ++ it8716f_flashport = find_ite_spi_flash_port(superio.port, superio.model); + + if (it8716f_flashport) + spi_controller = SPI_CONTROLLER_IT87XX; +@@ -129,6 +169,8 @@ int it87spi_init(void) + int ret; + + get_io_perms(); ++ /* Probe for the SuperI/O chip and fill global struct superio. */ ++ probe_superio(); + ret = it87spi_common_init(); + if (!ret) { + buses_supported = CHIP_BUSTYPE_SPI; +@@ -167,8 +209,8 @@ int it8716f_spi_send_command(unsigned int writecnt, unsigned int readcnt, + busy = INB(it8716f_flashport) & 0x80; + } while (busy); + if (readcnt > 3) { +- printf("%s called with unsupported readcnt %i.\n", +- __FUNCTION__, readcnt); ++ msg_pinfo("%s called with unsupported readcnt %i.\n", ++ __func__, readcnt); + return SPI_INVALID_LENGTH; + } + switch (writecnt) { +@@ -197,8 +239,8 @@ int it8716f_spi_send_command(unsigned int writecnt, unsigned int readcnt, + writeenc = 0x3; + break; + default: +- printf("%s called with unsupported writecnt %i.\n", +- __FUNCTION__, writecnt); ++ msg_pinfo("%s called with unsupported writecnt %i.\n", ++ __func__, writecnt); + return SPI_INVALID_LENGTH; + } + /* +@@ -278,12 +320,12 @@ int it8716f_spi_chip_write_256(struct flashchip *flash, uint8_t *buf) + } else { + spi_disable_blockprotect(); + /* Erase first */ +- printf("Erasing flash before programming... "); +- if (flash->erase(flash)) { +- fprintf(stderr, "ERASE FAILED!\n"); ++ msg_pinfo("Erasing flash before programming... "); ++ if (erase_flash(flash)) { ++ msg_perr("ERASE FAILED!\n"); + return -1; + } +- printf("done.\n"); ++ msg_pinfo("done.\n"); + for (i = 0; i < total_size / 256; i++) { + it8716f_spi_page_program(flash, i, buf); + } +diff --git a/jedec.c b/jedec.c +index 0e426d6..055e910 100644 +--- a/jedec.c ++++ b/jedec.c +@@ -5,6 +5,7 @@ + * Copyright (C) 2006 Giampiero Giancipoli + * Copyright (C) 2006 coresystems GmbH + * Copyright (C) 2007 Carl-Daniel Hailfinger ++ * Copyright (C) 2009 Sean Nelson + * + * 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 +@@ -24,6 +25,9 @@ + #include "flash.h" + + #define MAX_REFLASH_TRIES 0x10 ++#define MASK_FULL 0xffff ++#define MASK_2AA 0x7ff ++#define MASK_AAA 0xfff + + /* Check one byte for odd parity */ + uint8_t oddparity(uint8_t val) +@@ -33,7 +37,7 @@ uint8_t oddparity(uint8_t val) + return (val ^ (val >> 1)) & 0x1; + } + +-void toggle_ready_jedec(chipaddr dst) ++void toggle_ready_jedec_common(chipaddr dst, int delay) + { + unsigned int i = 0; + uint8_t tmp1, tmp2; +@@ -41,12 +45,33 @@ void toggle_ready_jedec(chipaddr dst) + tmp1 = chip_readb(dst) & 0x40; + + while (i++ < 0xFFFFFFF) { ++ if (delay) ++ programmer_delay(delay); + tmp2 = chip_readb(dst) & 0x40; + if (tmp1 == tmp2) { + break; + } + tmp1 = tmp2; + } ++ if (i > 0x100000) ++ printf_debug("%s: excessive loops, i=0x%x\n", __func__, i); ++} ++ ++void toggle_ready_jedec(chipaddr dst) ++{ ++ toggle_ready_jedec_common(dst, 0); ++} ++ ++/* Some chips require a minimum delay between toggle bit reads. ++ * The Winbond W39V040C wants 50 ms between reads on sector erase toggle, ++ * but experiments show that 2 ms are already enough. Pick a safety factor ++ * of 4 and use an 8 ms delay. ++ * Given that erase is slow on all chips, it is recommended to use ++ * toggle_ready_jedec_slow in erase functions. ++ */ ++void toggle_ready_jedec_slow(chipaddr dst) ++{ ++ toggle_ready_jedec_common(dst, 8 * 1000); + } + + void data_polling_jedec(chipaddr dst, uint8_t data) +@@ -62,30 +87,19 @@ void data_polling_jedec(chipaddr dst, uint8_t data) + break; + } + } ++ if (i > 0x100000) ++ printf_debug("%s: excessive loops, i=0x%x\n", __func__, i); + } + +-void unprotect_jedec(chipaddr bios) ++void start_program_jedec_common(struct flashchip *flash, unsigned int mask) + { +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0x80, bios + 0x5555); +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0x20, bios + 0x5555); +- +- programmer_delay(200); +-} +- +-void protect_jedec(chipaddr bios) +-{ +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0xA0, bios + 0x5555); +- +- programmer_delay(200); ++ chipaddr bios = flash->virtual_memory; ++ chip_writeb(0xAA, bios + (0x5555 & mask)); ++ chip_writeb(0x55, bios + (0x2AAA & mask)); ++ chip_writeb(0xA0, bios + (0x5555 & mask)); + } + +-int probe_jedec(struct flashchip *flash) ++int probe_jedec_common(struct flashchip *flash, unsigned int mask) + { + chipaddr bios = flash->virtual_memory; + uint8_t id1, id2; +@@ -109,15 +123,15 @@ int probe_jedec(struct flashchip *flash) + } + + /* Issue JEDEC Product ID Entry command */ +- chip_writeb(0xAA, bios + 0x5555); +- programmer_delay(10); +- chip_writeb(0x55, bios + 0x2AAA); +- programmer_delay(10); +- chip_writeb(0x90, bios + 0x5555); +- /* Older chips may need up to 100 us to respond. The ATMEL 29C020 +- * needs 10 ms according to the data sheet. +- */ +- programmer_delay(probe_timing_enter); ++ chip_writeb(0xAA, bios + (0x5555 & mask)); ++ if (probe_timing_enter) ++ programmer_delay(10); ++ chip_writeb(0x55, bios + (0x2AAA & mask)); ++ if (probe_timing_enter) ++ programmer_delay(10); ++ chip_writeb(0x90, bios + (0x5555 & mask)); ++ if (probe_timing_enter) ++ programmer_delay(probe_timing_enter); + + /* Read product ID */ + id1 = chip_readb(bios); +@@ -138,14 +152,20 @@ int probe_jedec(struct flashchip *flash) + } + + /* Issue JEDEC Product ID Exit command */ +- chip_writeb(0xAA, bios + 0x5555); +- programmer_delay(10); +- chip_writeb(0x55, bios + 0x2AAA); +- programmer_delay(10); +- chip_writeb(0xF0, bios + 0x5555); +- programmer_delay(probe_timing_exit); ++ if ((flash->feature_bits & FEATURE_SHORT_RESET) == FEATURE_LONG_RESET) ++ { ++ chip_writeb(0xAA, bios + (0x5555 & mask)); ++ if (probe_timing_exit) ++ programmer_delay(10); ++ chip_writeb(0x55, bios + (0x2AAA & mask)); ++ if (probe_timing_exit) ++ programmer_delay(10); ++ } ++ chip_writeb(0xF0, bios + (0x5555 & mask)); ++ if (probe_timing_exit) ++ programmer_delay(probe_timing_exit); + +- printf_debug("%s: id1 0x%02x, id2 0x%02x", __FUNCTION__, largeid1, largeid2); ++ printf_debug("%s: id1 0x%02x, id2 0x%02x", __func__, largeid1, largeid2); + if (!oddparity(id1)) + printf_debug(", id1 parity violation"); + +@@ -169,33 +189,37 @@ int probe_jedec(struct flashchip *flash) + printf_debug(", id2 is normal flash content"); + + printf_debug("\n"); +- if (largeid1 == flash->manufacture_id && largeid2 == flash->model_id) +- return 1; ++ if (largeid1 != flash->manufacture_id || largeid2 != flash->model_id) ++ return 0; + +- return 0; ++ if (flash->feature_bits & FEATURE_REGISTERMAP) ++ map_flash_registers(flash); ++ ++ return 1; + } + +-int erase_sector_jedec(struct flashchip *flash, unsigned int page, int pagesize) ++int erase_sector_jedec_common(struct flashchip *flash, unsigned int page, ++ unsigned int pagesize, unsigned int mask) + { + chipaddr bios = flash->virtual_memory; + + /* Issue the Sector Erase command */ +- chip_writeb(0xAA, bios + 0x5555); ++ chip_writeb(0xAA, bios + (0x5555 & mask)); + programmer_delay(10); +- chip_writeb(0x55, bios + 0x2AAA); ++ chip_writeb(0x55, bios + (0x2AAA & mask)); + programmer_delay(10); +- chip_writeb(0x80, bios + 0x5555); ++ chip_writeb(0x80, bios + (0x5555 & mask)); + programmer_delay(10); + +- chip_writeb(0xAA, bios + 0x5555); ++ chip_writeb(0xAA, bios + (0x5555 & mask)); + programmer_delay(10); +- chip_writeb(0x55, bios + 0x2AAA); ++ chip_writeb(0x55, bios + (0x2AAA & mask)); + programmer_delay(10); + chip_writeb(0x30, bios + page); + programmer_delay(10); + + /* wait for Toggle bit ready */ +- toggle_ready_jedec(bios); ++ toggle_ready_jedec_slow(bios); + + if (check_erased_range(flash, page, pagesize)) { + fprintf(stderr,"ERASE FAILED!\n"); +@@ -204,27 +228,28 @@ int erase_sector_jedec(struct flashchip *flash, unsigned int page, int pagesize) + return 0; + } + +-int erase_block_jedec(struct flashchip *flash, unsigned int block, int blocksize) ++int erase_block_jedec_common(struct flashchip *flash, unsigned int block, ++ unsigned int blocksize, unsigned int mask) + { + chipaddr bios = flash->virtual_memory; + + /* Issue the Sector Erase command */ +- chip_writeb(0xAA, bios + 0x5555); ++ chip_writeb(0xAA, bios + (0x5555 & mask)); + programmer_delay(10); +- chip_writeb(0x55, bios + 0x2AAA); ++ chip_writeb(0x55, bios + (0x2AAA & mask)); + programmer_delay(10); +- chip_writeb(0x80, bios + 0x5555); ++ chip_writeb(0x80, bios + (0x5555 & mask)); + programmer_delay(10); + +- chip_writeb(0xAA, bios + 0x5555); ++ chip_writeb(0xAA, bios + (0x5555 & mask)); + programmer_delay(10); +- chip_writeb(0x55, bios + 0x2AAA); ++ chip_writeb(0x55, bios + (0x2AAA & mask)); + programmer_delay(10); + chip_writeb(0x50, bios + block); + programmer_delay(10); + + /* wait for Toggle bit ready */ +- toggle_ready_jedec(bios); ++ toggle_ready_jedec_slow(bios); + + if (check_erased_range(flash, block, blocksize)) { + fprintf(stderr,"ERASE FAILED!\n"); +@@ -233,27 +258,27 @@ int erase_block_jedec(struct flashchip *flash, unsigned int block, int blocksize + return 0; + } + +-int erase_chip_jedec(struct flashchip *flash) ++int erase_chip_jedec_common(struct flashchip *flash, unsigned int mask) + { + int total_size = flash->total_size * 1024; + chipaddr bios = flash->virtual_memory; + + /* Issue the JEDEC Chip Erase command */ +- chip_writeb(0xAA, bios + 0x5555); ++ chip_writeb(0xAA, bios + (0x5555 & mask)); + programmer_delay(10); +- chip_writeb(0x55, bios + 0x2AAA); ++ chip_writeb(0x55, bios + (0x2AAA & mask)); + programmer_delay(10); +- chip_writeb(0x80, bios + 0x5555); ++ chip_writeb(0x80, bios + (0x5555 & mask)); + programmer_delay(10); + +- chip_writeb(0xAA, bios + 0x5555); ++ chip_writeb(0xAA, bios + (0x5555 & mask)); + programmer_delay(10); +- chip_writeb(0x55, bios + 0x2AAA); ++ chip_writeb(0x55, bios + (0x2AAA & mask)); + programmer_delay(10); +- chip_writeb(0x10, bios + 0x5555); ++ chip_writeb(0x10, bios + (0x5555 & mask)); + programmer_delay(10); + +- toggle_ready_jedec(bios); ++ toggle_ready_jedec_slow(bios); + + if (check_erased_range(flash, 0, total_size)) { + fprintf(stderr,"ERASE FAILED!\n"); +@@ -262,23 +287,68 @@ int erase_chip_jedec(struct flashchip *flash) + return 0; + } + +-int write_page_write_jedec(struct flashchip *flash, uint8_t *src, +- int start, int page_size) ++int write_byte_program_jedec_common(struct flashchip *flash, uint8_t *src, ++ chipaddr dst, unsigned int mask) + { +- int i, tried = 0, start_index = 0, ok; ++ int tried = 0, failed = 0; ++ chipaddr bios = flash->virtual_memory; ++ ++ /* If the data is 0xFF, don't program it and don't complain. */ ++ if (*src == 0xFF) { ++ return 0; ++ } ++ ++retry: ++ /* Issue JEDEC Byte Program command */ ++ start_program_jedec_common(flash, mask); ++ ++ /* transfer data from source to destination */ ++ chip_writeb(*src, dst); ++ toggle_ready_jedec(bios); ++ ++ if (chip_readb(dst) != *src && tried++ < MAX_REFLASH_TRIES) { ++ goto retry; ++ } ++ ++ if (tried >= MAX_REFLASH_TRIES) ++ failed = 1; ++ ++ return failed; ++} ++ ++int write_sector_jedec_common(struct flashchip *flash, uint8_t *src, ++ chipaddr dst, unsigned int page_size, unsigned int mask) ++{ ++ int i, failed = 0; ++ chipaddr olddst; ++ ++ olddst = dst; ++ for (i = 0; i < page_size; i++) { ++ if (write_byte_program_jedec_common(flash, src, dst, mask)) ++ failed = 1; ++ dst++, src++; ++ } ++ if (failed) ++ fprintf(stderr, " writing sector at 0x%lx failed!\n", olddst); ++ ++ return failed; ++} ++ ++int write_page_write_jedec_common(struct flashchip *flash, uint8_t *src, ++ int start, int page_size, unsigned int mask) ++{ ++ int i, tried = 0, failed; + uint8_t *s = src; + chipaddr bios = flash->virtual_memory; + chipaddr dst = bios + start; + chipaddr d = dst; + + retry: +- /* Issue JEDEC Data Unprotect comand */ +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0xA0, bios + 0x5555); ++ /* Issue JEDEC Start Program comand */ ++ start_program_jedec_common(flash, mask); + + /* transfer data from source to destination */ +- for (i = start_index; i < page_size; i++) { ++ for (i = 0; i < page_size; i++) { + /* If the data is 0xFF, don't program it */ + if (*src != 0xFF) + chip_writeb(*src, dst); +@@ -290,68 +360,46 @@ retry: + + dst = d; + src = s; +- ok = !verify_range(flash, src, start, page_size, NULL); ++ failed = verify_range(flash, src, start, page_size, NULL); + +- if (!ok && tried++ < MAX_REFLASH_TRIES) { +- start_index = i; ++ if (failed && tried++ < MAX_REFLASH_TRIES) { ++ fprintf(stderr, "retrying.\n"); + goto retry; + } +- if (!ok) { ++ if (failed) { + fprintf(stderr, " page 0x%lx failed!\n", + (d - bios) / page_size); + } +- return !ok; +-} +- +-int write_byte_program_jedec(chipaddr bios, uint8_t *src, +- chipaddr dst) +-{ +- int tried = 0, ok = 1; +- +- /* If the data is 0xFF, don't program it */ +- if (*src == 0xFF) { +- return -1; +- } +- +-retry: +- /* Issue JEDEC Byte Program command */ +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0xA0, bios + 0x5555); +- +- /* transfer data from source to destination */ +- chip_writeb(*src, dst); +- toggle_ready_jedec(bios); +- +- if (chip_readb(dst) != *src && tried++ < MAX_REFLASH_TRIES) { +- goto retry; +- } +- +- if (tried >= MAX_REFLASH_TRIES) +- ok = 0; +- +- return !ok; ++ return failed; + } + +-int write_sector_jedec(chipaddr bios, uint8_t *src, +- chipaddr dst, unsigned int page_size) ++int getaddrmask(struct flashchip *flash) + { +- int i; +- +- for (i = 0; i < page_size; i++) { +- write_byte_program_jedec(bios, src, dst); +- dst++, src++; ++ switch (flash->feature_bits & FEATURE_ADDR_MASK) { ++ case FEATURE_ADDR_FULL: ++ return MASK_FULL; ++ break; ++ case FEATURE_ADDR_2AA: ++ return MASK_2AA; ++ break; ++ case FEATURE_ADDR_AAA: ++ return MASK_AAA; ++ break; ++ default: ++ fprintf(stderr, "%s called with unknown mask\n", __func__); ++ return 0; ++ break; + } +- +- return 0; + } + + int write_jedec(struct flashchip *flash, uint8_t *buf) + { +- int i; ++ int mask; ++ int i, failed = 0; + int total_size = flash->total_size * 1024; + int page_size = flash->page_size; +- chipaddr bios = flash->virtual_memory; ++ ++ mask = getaddrmask(flash); + + if (erase_chip_jedec(flash)) { + fprintf(stderr,"ERASE FAILED!\n"); +@@ -361,12 +409,89 @@ int write_jedec(struct flashchip *flash, uint8_t *buf) + printf("Programming page: "); + for (i = 0; i < total_size / page_size; i++) { + printf("%04d at address: 0x%08x", i, i * page_size); +- write_page_write_jedec(flash, buf + i * page_size, +- i * page_size, page_size); ++ if (write_page_write_jedec_common(flash, buf + i * page_size, ++ i * page_size, page_size, mask)) ++ failed = 1; + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + } + printf("\n"); +- protect_jedec(bios); + ++ return failed; ++} ++ ++int write_jedec_1(struct flashchip *flash, uint8_t * buf) ++{ ++ int i; ++ chipaddr bios = flash->virtual_memory; ++ chipaddr dst = bios; ++ int mask; ++ ++ mask = getaddrmask(flash); ++ ++ programmer_delay(10); ++ if (erase_flash(flash)) { ++ fprintf(stderr, "ERASE FAILED!\n"); ++ return -1; ++ } ++ ++ printf("Programming page: "); ++ for (i = 0; i < flash->total_size; i++) { ++ if ((i & 0x3) == 0) ++ printf("address: 0x%08lx", (unsigned long)i * 1024); ++ ++ write_sector_jedec_common(flash, buf + i * 1024, dst + i * 1024, 1024, mask); ++ ++ if ((i & 0x3) == 0) ++ printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); ++ } ++ ++ printf("\n"); + return 0; + } ++ ++/* erase chip with block_erase() prototype */ ++int erase_chip_block_jedec(struct flashchip *flash, unsigned int addr, ++ unsigned int blocksize) ++{ ++ int mask; ++ ++ mask = getaddrmask(flash); ++ if ((addr != 0) || (blocksize != flash->total_size * 1024)) { ++ fprintf(stderr, "%s called with incorrect arguments\n", ++ __func__); ++ return -1; ++ } ++ return erase_chip_jedec_common(flash, mask); ++} ++ ++int probe_jedec(struct flashchip *flash) ++{ ++ int mask; ++ ++ mask = getaddrmask(flash); ++ return probe_jedec_common(flash, mask); ++} ++ ++int erase_sector_jedec(struct flashchip *flash, unsigned int page, unsigned int size) ++{ ++ int mask; ++ ++ mask = getaddrmask(flash); ++ return erase_sector_jedec_common(flash, page, size, mask); ++} ++ ++int erase_block_jedec(struct flashchip *flash, unsigned int page, unsigned int size) ++{ ++ int mask; ++ ++ mask = getaddrmask(flash); ++ return erase_block_jedec_common(flash, page, size, mask); ++} ++ ++int erase_chip_jedec(struct flashchip *flash) ++{ ++ int mask; ++ ++ mask = getaddrmask(flash); ++ return erase_chip_jedec_common(flash, mask); ++} +diff --git a/layout.c b/layout.c +index c432ebe..26b7c6a 100644 +--- a/layout.c ++++ b/layout.c +@@ -23,8 +23,10 @@ + #include + #include "flash.h" + ++#if INTERNAL_SUPPORT == 1 + char *mainboard_vendor = NULL; + char *mainboard_part = NULL; ++#endif + int romimages = 0; + + #define MAX_ROMLAYOUT 16 +@@ -38,6 +40,7 @@ typedef struct { + + romlayout_t rom_entries[MAX_ROMLAYOUT]; + ++#if INTERNAL_SUPPORT == 1 /* FIXME: Move the whole block to cbtable.c? */ + static char *def_name = "DEFAULT"; + + int show_id(uint8_t *bios, int size, int force) +@@ -126,6 +129,7 @@ int show_id(uint8_t *bios, int size, int force) + + return 0; + } ++#endif + + int read_romlayout(char *name) + { +diff --git a/m29f002.c b/m29f002.c +index 00cbbc1..01a7a50 100644 +--- a/m29f002.c ++++ b/m29f002.c +@@ -45,6 +45,7 @@ static int rewrite_block(struct flashchip *flash, uint8_t *src, + chipaddr dst = bios + start; + + /* erase */ ++ /* FIXME: use erase_sector_jedec? */ + chip_writeb(0xaa, bios + 0x555); + chip_writeb(0x55, bios + 0xaaa); + chip_writeb(0x80, bios + 0x555); +@@ -59,6 +60,7 @@ static int rewrite_block(struct flashchip *flash, uint8_t *src, + } + + /* program */ ++ /* FIXME: use write_sector_jedec? */ + while (size--) { + chip_writeb(0xaa, bios + 0x555); + chip_writeb(0x55, bios + 0xaaa); +diff --git a/m29f400bt.c b/m29f400bt.c +index ace6dae..e77572a 100644 +--- a/m29f400bt.c ++++ b/m29f400bt.c +@@ -20,14 +20,11 @@ + + #include "flash.h" + +-void protect_m29f400bt(chipaddr bios) +-{ +- chip_writeb(0xAA, bios + 0xAAA); +- chip_writeb(0x55, bios + 0x555); +- chip_writeb(0xA0, bios + 0xAAA); +- +- programmer_delay(200); +-} ++/* WARNING! ++ This chip uses the standard JEDEC Addresses in 16-bit mode as word ++ addresses. In byte mode, 0xAAA has to be used instead of 0x555 and ++ 0x555 instead of 0x2AA. Do *not* blindly replace with standard JEDEC ++ functions. */ + + void write_page_m29f400bt(chipaddr bios, uint8_t *src, + chipaddr dst, int page_size) +@@ -75,7 +72,7 @@ int probe_m29f400bt(struct flashchip *flash) + + programmer_delay(10); + +- printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __FUNCTION__, id1, id2); ++ printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2); + + if (id1 == flash->manufacture_id && id2 == flash->model_id) + return 1; +@@ -105,7 +102,7 @@ int erase_m29f400bt(struct flashchip *flash) + return 0; + } + +-int block_erase_m29f400bt(struct flashchip *flash, int start, int len) ++int block_erase_m29f400bt(struct flashchip *flash, unsigned int start, unsigned int len) + { + chipaddr bios = flash->virtual_memory; + chipaddr dst = bios + start; +@@ -129,6 +126,16 @@ int block_erase_m29f400bt(struct flashchip *flash, int start, int len) + return 0; + } + ++int block_erase_chip_m29f400bt(struct flashchip *flash, unsigned int address, unsigned int blocklen) ++{ ++ if ((address != 0) || (blocklen != flash->total_size * 1024)) { ++ fprintf(stderr, "%s called with incorrect arguments\n", ++ __func__); ++ return -1; ++ } ++ return erase_m29f400bt(flash); ++} ++ + int write_m29f400bt(struct flashchip *flash, uint8_t *buf) + { + int i; +@@ -194,7 +201,6 @@ int write_m29f400bt(struct flashchip *flash, uint8_t *buf) + write_page_m29f400bt(bios, buf + 0x7c000, bios + 0x7c000, 16 * 1024); + + printf("\n"); +- //protect_m29f400bt (bios); + + return 0; + } +@@ -248,7 +254,6 @@ int write_coreboot_m29f400bt(struct flashchip *flash, uint8_t *buf) + write_page_m29f400bt(bios, buf + 0x30000, bios + 0x30000, 64 * 1024); + + printf("\n"); +- //protect_m29f400bt (bios); + + return 0; + } +diff --git a/mx29f002.c b/mx29f002.c +index d5c6041..411e539 100644 +--- a/mx29f002.c ++++ b/mx29f002.c +@@ -36,65 +36,67 @@ int probe_29f002(struct flashchip *flash) + + programmer_delay(10); + +- printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __FUNCTION__, id1, id2); ++ printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2); + if (id1 == flash->manufacture_id && id2 == flash->model_id) + return 1; + + return 0; + } + +-int erase_29f002(struct flashchip *flash) ++int erase_sector_29f002(struct flashchip *flash, unsigned int address, unsigned int blocklen) + { + chipaddr bios = flash->virtual_memory; + +- chip_writeb(0xF0, bios + 0x555); + chip_writeb(0xAA, bios + 0x555); + chip_writeb(0x55, bios + 0x2AA); + chip_writeb(0x80, bios + 0x555); + chip_writeb(0xAA, bios + 0x555); + chip_writeb(0x55, bios + 0x2AA); +- chip_writeb(0x10, bios + 0x555); ++ chip_writeb(0x30, bios + address); + +- programmer_delay(100); +- toggle_ready_jedec(bios); ++ programmer_delay(10); + +- if (check_erased_range(flash, 0, flash->total_size * 1024)) { ++ /* wait for Toggle bit ready */ ++ toggle_ready_jedec(bios + address); ++ ++ if (check_erased_range(flash, address, blocklen)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } + return 0; + } + +-int write_29f002(struct flashchip *flash, uint8_t *buf) ++int erase_chip_29f002(struct flashchip *flash, unsigned int addr, unsigned int blocklen) ++{ ++ if ((addr != 0) || (blocklen != flash->total_size * 1024)) { ++ fprintf(stderr, "%s called with incorrect arguments\n", ++ __func__); ++ return -1; ++ } ++ return erase_29f002(flash); ++} ++ ++/* FIXME: Use erase_chip_jedec? ++ * erase_29f002 uses shorter addresses, sends F0 (exit ID mode) and ++ * and has a bigger delay before polling the toggle bit */ ++int erase_29f002(struct flashchip *flash) + { +- int i; +- int total_size = flash->total_size * 1024; + chipaddr bios = flash->virtual_memory; +- chipaddr dst = bios; + +- chip_writeb(0xF0, bios); +- programmer_delay(10); +- if (erase_29f002(flash)) { ++ chip_writeb(0xF0, bios + 0x555); ++ chip_writeb(0xAA, bios + 0x555); ++ chip_writeb(0x55, bios + 0x2AA); ++ chip_writeb(0x80, bios + 0x555); ++ chip_writeb(0xAA, bios + 0x555); ++ chip_writeb(0x55, bios + 0x2AA); ++ chip_writeb(0x10, bios + 0x555); ++ ++ programmer_delay(100); ++ toggle_ready_jedec(bios); ++ ++ if (check_erased_range(flash, 0, flash->total_size * 1024)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } +- printf("Programming page: "); +- for (i = 0; i < total_size; i++) { +- /* write to the sector */ +- if ((i & 0xfff) == 0) +- printf("address: 0x%08lx", (unsigned long)i); +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0xA0, bios + 0x5555); +- chip_writeb(*buf++, dst++); +- +- /* wait for Toggle bit ready */ +- toggle_ready_jedec(dst); +- +- if ((i & 0xfff) == 0) +- printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); +- } +- printf("\n"); +- + return 0; + } +diff --git a/nic3com.c b/nic3com.c +index 075e760..a4878d2 100644 +--- a/nic3com.c ++++ b/nic3com.c +@@ -58,7 +58,8 @@ int nic3com_init(void) + { + get_io_perms(); + +- io_base_addr = pcidev_init(PCI_VENDOR_ID_3COM, nics_3com, programmer_param); ++ io_base_addr = pcidev_init(PCI_VENDOR_ID_3COM, PCI_BASE_ADDRESS_0, ++ nics_3com, programmer_param); + id = pcidev_dev->device_id; + + /* 3COM 3C90xB cards need a special fixup. */ +diff --git a/pcidev.c b/pcidev.c +index 580ebae..6c92c0a 100644 +--- a/pcidev.c ++++ b/pcidev.c +@@ -28,7 +28,8 @@ struct pci_access *pacc; + struct pci_filter filter; + struct pci_dev *pcidev_dev = NULL; + +-uint32_t pcidev_validate(struct pci_dev *dev, struct pcidev_status *devs) ++uint32_t pcidev_validate(struct pci_dev *dev, uint32_t bar, ++ struct pcidev_status *devs) + { + int i; + uint32_t addr; +@@ -37,12 +38,16 @@ uint32_t pcidev_validate(struct pci_dev *dev, struct pcidev_status *devs) + if (dev->device_id != devs[i].device_id) + continue; + +- /* Don't use dev->base_addr[0], won't work on older libpci. */ +- addr = pci_read_long(dev, PCI_BASE_ADDRESS_0) & ~0x03; +- ++ /* ++ * Don't use dev->base_addr[x] (as value for 'bar'), won't ++ * work on older libpci. ++ */ ++ addr = pci_read_long(dev, bar) & ~0x03; ++ + printf("Found \"%s %s\" (%04x:%04x, BDF %02x:%02x.%x).\n", +- devs[i].vendor_name, devs[i].device_name, dev->vendor_id, +- dev->device_id, dev->bus, dev->dev, dev->func); ++ devs[i].vendor_name, devs[i].device_name, ++ dev->vendor_id, dev->device_id, dev->bus, dev->dev, ++ dev->func); + + if (devs[i].status == PCI_NT) { + printf("===\nThis PCI device is UNTESTED. Please " +@@ -57,7 +62,8 @@ uint32_t pcidev_validate(struct pci_dev *dev, struct pcidev_status *devs) + return 0; + } + +-uint32_t pcidev_init(uint16_t vendor_id, struct pcidev_status *devs, char *pcidev_bdf) ++uint32_t pcidev_init(uint16_t vendor_id, uint32_t bar, ++ struct pcidev_status *devs, char *pcidev_bdf) + { + struct pci_dev *dev; + char *msg = NULL; +@@ -80,7 +86,7 @@ uint32_t pcidev_init(uint16_t vendor_id, struct pcidev_status *devs, char *pcide + + for (dev = pacc->devices; dev; dev = dev->next) { + if (pci_filter_match(&filter, dev)) { +- if ((addr = pcidev_validate(dev, devs)) != 0) { ++ if ((addr = pcidev_validate(dev, bar, devs)) != 0) { + curaddr = addr; + pcidev_dev = dev; + found++; +@@ -94,7 +100,7 @@ uint32_t pcidev_init(uint16_t vendor_id, struct pcidev_status *devs, char *pcide + exit(1); + } else if (found > 1) { + fprintf(stderr, "Error: Multiple supported PCI devices found. " +- "Use 'flashrom -p xxxx=bb:dd.f' \n" ++ "Use 'flashrom -p xxxx:bb:dd.f' \n" + "to explicitly select the card with the given BDF " + "(PCI bus, device, function).\n"); + exit(1); +diff --git a/physmap.c b/physmap.c +index 778a783..fba7a53 100644 +--- a/physmap.c ++++ b/physmap.c +@@ -3,6 +3,7 @@ + * + * Copyright (C) 2009 Peter Stuge + * Copyright (C) 2009 coresystems GmbH ++ * Copyright (C) 2010 Carl-Daniel Hailfinger + * + * 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 +@@ -36,6 +37,10 @@ void *sys_physmap(unsigned long phys_addr, size_t len) + return map_physical(phys_addr, len); + } + ++/* The OS X driver does not differentiate between mapping types. */ ++#define sys_physmap_rw_uncached sys_physmap ++#define sys_physmap_ro_cached sys_physmap ++ + void physunmap(void *virt_addr, size_t len) + { + unmap_physical(virt_addr, len); +@@ -51,8 +56,10 @@ void physunmap(void *virt_addr, size_t len) + #endif + + static int fd_mem = -1; ++static int fd_mem_cached = -1; + +-void *sys_physmap(unsigned long phys_addr, size_t len) ++/* For MMIO access. Must be uncached, doesn't make sense to restrict to ro. */ ++void *sys_physmap_rw_uncached(unsigned long phys_addr, size_t len) + { + void *virt_addr; + +@@ -69,6 +76,26 @@ void *sys_physmap(unsigned long phys_addr, size_t len) + return MAP_FAILED == virt_addr ? NULL : virt_addr; + } + ++/* For reading DMI/coreboot/whatever tables. We should never write, and we ++ * do not care about caching. ++ */ ++void *sys_physmap_ro_cached(unsigned long phys_addr, size_t len) ++{ ++ void *virt_addr; ++ ++ if (-1 == fd_mem_cached) { ++ /* Open the memory device CACHED. */ ++ if (-1 == (fd_mem_cached = open(MEM_DEV, O_RDWR))) { ++ perror("Critical error: open(" MEM_DEV ")"); ++ exit(2); ++ } ++ } ++ ++ virt_addr = mmap(0, len, PROT_READ, MAP_SHARED, ++ fd_mem_cached, (off_t)phys_addr); ++ return MAP_FAILED == virt_addr ? NULL : virt_addr; ++} ++ + void physunmap(void *virt_addr, size_t len) + { + if (len == 0) { +@@ -80,7 +107,12 @@ void physunmap(void *virt_addr, size_t len) + } + #endif + +-void *physmap(const char *descr, unsigned long phys_addr, size_t len) ++#define PHYSMAP_NOFAIL 0 ++#define PHYSMAP_MAYFAIL 1 ++#define PHYSMAP_RW 0 ++#define PHYSMAP_RO 1 ++ ++void *physmap_common(const char *descr, unsigned long phys_addr, size_t len, int mayfail, int readonly) + { + void *virt_addr; + +@@ -100,7 +132,11 @@ void *physmap(const char *descr, unsigned long phys_addr, size_t len) + descr, (unsigned long)len, phys_addr); + } + +- virt_addr = sys_physmap(phys_addr, len); ++ if (readonly) { ++ virt_addr = sys_physmap_ro_cached(phys_addr, len); ++ } else { ++ virt_addr = sys_physmap_rw_uncached(phys_addr, len); ++ } + + if (NULL == virt_addr) { + if (NULL == descr) +@@ -114,12 +150,23 @@ void *physmap(const char *descr, unsigned long phys_addr, size_t len) + fprintf(stderr, "You can override CONFIG_X86_PAT at boot with the nopat kernel parameter but\n"); + fprintf(stderr, "disabling the other option unfortunately requires a kernel recompile. Sorry!\n"); + } +- exit(3); ++ if (!mayfail) ++ exit(3); + } + + return virt_addr; + } + ++void *physmap(const char *descr, unsigned long phys_addr, size_t len) ++{ ++ return physmap_common(descr, phys_addr, len, PHYSMAP_NOFAIL, PHYSMAP_RW); ++} ++ ++void *physmap_try_ro(const char *descr, unsigned long phys_addr, size_t len) ++{ ++ return physmap_common(descr, phys_addr, len, PHYSMAP_MAYFAIL, PHYSMAP_RO); ++} ++ + #ifdef __linux__ + /* + * Reading and writing to MSRs, however requires instructions rdmsr/wrmsr, +@@ -133,7 +180,7 @@ static int fd_msr = -1; + + msr_t rdmsr(int addr) + { +- uint8_t buf[8]; ++ uint32_t buf[2]; + msr_t msr = { 0xffffffff, 0xffffffff }; + + if (lseek(fd_msr, (off_t) addr, SEEK_SET) == -1) { +@@ -143,8 +190,8 @@ msr_t rdmsr(int addr) + } + + if (read(fd_msr, buf, 8) == 8) { +- msr.lo = *(uint32_t *)buf; +- msr.hi = *(uint32_t *)(buf + 4); ++ msr.lo = buf[0]; ++ msr.hi = buf[1]; + + return msr; + } +diff --git a/pm29f002.c b/pm29f002.c +index a01df88..bf78d13 100644 +--- a/pm29f002.c ++++ b/pm29f002.c +@@ -20,18 +20,20 @@ + + #include "flash.h" + ++/* if write_sector_jedec is used, ++ this is write_jedec_1 */ + int write_pm29f002(struct flashchip *flash, uint8_t *buf) + { + int i, total_size = flash->total_size * 1024; + chipaddr bios = flash->virtual_memory; + chipaddr dst = bios; + +- /* Pm29F002T/B use the same erase method... */ +- if (erase_29f040b(flash)) { ++ if (erase_flash(flash)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } + ++ /* FIXME: use write_sector_jedec? */ + printf("Programming page: "); + for (i = 0; i < total_size; i++) { + if ((i & 0xfff) == 0) +diff --git a/pm49fl00x.c b/pm49fl00x.c +index 27a1163..9d104e2 100644 +--- a/pm49fl00x.c ++++ b/pm49fl00x.c +@@ -36,16 +36,6 @@ void write_lockbits_49fl00x(chipaddr bios, int size, + } + } + +-int probe_49fl00x(struct flashchip *flash) +-{ +- int ret = probe_jedec(flash); +- +- if (ret == 1) +- map_flash_registers(flash); +- +- return ret; +-} +- + int erase_49fl00x(struct flashchip *flash) + { + int i; +@@ -101,8 +91,8 @@ int write_49fl00x(struct flashchip *flash, uint8_t *buf) + + /* write to the sector */ + printf("%04d at address: 0x%08x", i, i * page_size); +- write_sector_jedec(bios, buf + i * page_size, +- bios + i * page_size, page_size); ++ write_sector_jedec_common(flash, buf + i * page_size, ++ bios + i * page_size, page_size, 0xffff); + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + fflush(stdout); + } +diff --git a/print.c b/print.c +index ce46e1b..fce72fd 100644 +--- a/print.c ++++ b/print.c +@@ -21,22 +21,9 @@ + + #include + #include +-#include + #include "flash.h" + #include "flashchips.h" + +-struct board_info_url { +- const char *vendor; +- const char *name; +- const char *url; +-}; +- +-struct board_info_notes { +- const char *vendor; +- const char *name; +- const char *note; +-}; +- + /* + * Return a string corresponding to the bustype parameter. + * Memory is obtained with malloc() and can be freed with free(). +@@ -157,6 +144,7 @@ void print_supported_chips(void) + } + } + ++#if INTERNAL_SUPPORT == 1 + void print_supported_chipsets(void) + { + int i, j, chipsetcount = 0; +@@ -194,7 +182,7 @@ void print_supported_boards_helper(const struct board_info *b, const char *msg) + for (j = 0; j < 25 - strlen(b[i].vendor); j++) + printf(" "); + printf("%s", b[i].name); +- for (j = 0; j < 23 - strlen(b[i].name); j++) ++ for (j = 0; j < 28 - strlen(b[i].name); j++) + printf(" "); + printf("\n"); + } +@@ -209,7 +197,7 @@ void print_supported_boards(void) + boardcount++; + + printf("\nSupported boards which need write-enable code (total: %d):" +- "\n\nVendor: Board: " ++ "\n\nVendor: Board: " + "Required option:\n\n", boardcount); + + for (i = 0; b[i].vendor_name != NULL; i++) { +@@ -217,7 +205,7 @@ void print_supported_boards(void) + for (j = 0; j < 25 - strlen(b[i].vendor_name); j++) + printf(" "); + printf("%s", b[i].board_name); +- for (j = 0; j < 25 - strlen(b[i].board_name); j++) ++ for (j = 0; j < 30 - strlen(b[i].board_name); j++) + printf(" "); + if (b[i].lb_vendor != NULL) + printf("-m %s:%s\n", b[i].lb_vendor, b[i].lb_part); +@@ -234,505 +222,187 @@ void print_supported_boards(void) + print_supported_boards_helper(laptops_bad, + "Laptops which have been verified to NOT work yet"); + } ++#endif + +-const char *wiki_header = "= Supported devices =\n\n\ +-
\n\ +-Please do '''not''' edit these tables in the wiki directly, they are \ +-generated by pasting '''flashrom -z''' output.
\ +-'''Last update:''' %s(generated by flashrom %s)\n
\n"; +- +-const char *chipset_th = "{| border=\"0\" style=\"font-size: smaller\"\n\ +-|- bgcolor=\"#6699dd\"\n! align=\"left\" | Vendor\n\ +-! align=\"left\" | Southbridge\n! align=\"left\" | PCI IDs\n\ +-! align=\"left\" | Status\n\n"; +- +-const char *board_th = "{| border=\"0\" style=\"font-size: smaller\" \ +-valign=\"top\"\n|- bgcolor=\"#6699dd\"\n! align=\"left\" | Vendor\n\ +-! align=\"left\" | Mainboard\n! align=\"left\" | Status\n\n"; +- +-const char *board_th2 = "{| border=\"0\" style=\"font-size: smaller\" \ +-valign=\"top\"\n|- bgcolor=\"#6699dd\"\n! align=\"left\" | Vendor\n\ +-! align=\"left\" | Mainboard\n! align=\"left\" | Required option\n\ +-! align=\"left\" | Status\n\n"; +- +-const char *board_intro = "\ +-\n== Supported mainboards ==\n\n\ +-In general, it is very likely that flashrom works out of the box even if your \ +-mainboard is not listed below.\n\nThis is a list of mainboards where we have \ +-verified that they either do or do not need any special initialization to \ +-make flashrom work (given flashrom supports the respective chipset and flash \ +-chip), or that they do not yet work at all. If they do not work, support may \ +-or may not be added later.\n\n\ +-Mainboards which don't appear in the list may or may not work (we don't \ +-know, someone has to give it a try). Please report any further verified \ +-mainboards on the [[Mailinglist|mailing list]].\n"; +- +-const char *chip_th = "{| border=\"0\" style=\"font-size: smaller\" \ +-valign=\"top\"\n|- bgcolor=\"#6699dd\"\n! align=\"left\" | Vendor\n\ +-! align=\"left\" | Device\n! align=\"left\" | Size / KB\n\ +-! align=\"left\" | Type\n! align=\"left\" colspan=\"4\" | Status\n\n\ +-|- bgcolor=\"#6699ff\"\n| colspan=\"4\" |  \n\ +-| Probe\n| Read\n| Write\n| Erase\n\n"; +- +-const char *programmer_section = "\ +-\n== Supported programmers ==\n\nThis is a list \ +-of supported PCI devices flashrom can use as programmer:\n\n{| border=\"0\" \ +-valign=\"top\"\n| valign=\"top\"|\n\n{| border=\"0\" style=\"font-size: \ +-smaller\" valign=\"top\"\n|- bgcolor=\"#6699dd\"\n! align=\"left\" | Vendor\n\ +-! align=\"left\" | Device\n! align=\"left\" | PCI IDs\n\ +-! align=\"left\" | Status\n\n"; +- +-const char *laptop_intro = "\n== Supported laptops/notebooks ==\n\n\ +-In general, flashing laptops is more difficult because laptops\n\n\ +-* often use the flash chip for stuff besides the BIOS,\n\ +-* often have special protection stuff which has to be handled by flashrom,\n\ +-* often use flash translation circuits which need drivers in flashrom.\n\n\ +-
\n\ +-'''IMPORTANT:''' At this point we recommend to '''not''' use flashrom on \ +-untested laptops unless you have a means to recover from a flashing that goes \ +-wrong (a working backup flash chip and/or good soldering skills).\n
\n"; +- +-/* Please keep these lists alphabetically ordered by vendor/board. */ +-const struct board_info_url boards_url[] = { +- /* Verified working boards that don't need write-enables. */ +- { "Abit", "AX8", "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?DEFTITLE=Y&fMTYPE=Socket%20939&pMODEL_NAME=AX8" }, +- { "Abit", "Fatal1ty F-I90HD", "http://www.abit.com.tw/page/de/motherboard/motherboard_detail.php?pMODEL_NAME=Fatal1ty+F-I90HD&fMTYPE=LGA775" }, +- { "Advantech", "PCM-5820", "http://www.emacinc.com/sbc_pc_compatible/pcm_5820.htm" }, +- { "ASI", "MB-5BLMP", "http://www.hojerteknik.com/winnet.htm" }, +- { "ASRock", "A770CrossFire", "http://www.asrock.com/mb/overview.asp?Model=A770CrossFire&s=AM2\%2b" }, +- { "ASUS", "A7N8X Deluxe", "http://www.asus.com/Product.aspx?P_ID=wAsRYm41KTp78MFC" }, +- { "ASUS", "A7N8X-E Deluxe", "http://www.asus.com/products.aspx?l1=3&l2=13&l3=56&l4=0&model=217&modelmenu=1" }, +- { "ASUS", "A7V400-MX", "http://www.asus.com.tw/products.aspx?l1=3&l2=13&l3=63&l4=0&model=228&modelmenu=1" }, +- { "ASUS", "A7V8X-MX", "http://www.asus.com.tw/products.aspx?l1=3&l2=13&l3=64&l4=0&model=229&modelmenu=1" }, +- { "ASUS", "A8N-E", "http://www.asus.com.tw/products.aspx?l1=3&l2=15&l3=171&l4=0&model=455&modelmenu=2" }, +- { "ASUS", "A8NE-FM/S", "http://www.hardwareschotte.de/hardware/preise/proid_1266090/preis_ASUS+A8NE-FM" }, +- { "ASUS", "A8N-SLI", "http://asus.com/product.aspx?P_ID=J9FKa8z2xVId3pDK" }, +- { "ASUS", "A8N-SLI Premium", "http://www.asus.com.tw/products.aspx?l1=3&l2=15&l3=148&l4=0&model=539&modelmenu=1" }, +- { "ASUS", "A8V Deluxe", "http://www.asus.com/product.aspx?P_ID=tvpdgPNCPaABZRVU" }, +- { "ASUS", "A8V-E Deluxe", "http://www.asus.com.tw/products.aspx?l1=3&l2=15&l3=143&l4=0&model=376&modelmenu=1" }, +- { "ASUS", "A8V-E SE", "http://www.asus.com.tw/products.aspx?l1=3&l2=15&l3=143&l4=0&model=576&modelmenu=1" }, +- { "ASUS", "M2A-MX", "http://www.asus.com/products.aspx?l1=3&l2=101&l3=583&l4=0&model=1909&modelmenu=1" }, +- { "ASUS", "M2A-VM", "http://www.asus.com.tw/products.aspx?l1=3&l2=101&l3=496&l4=0&model=1568&modelmenu=1" }, +- { "ASUS", "M2N-E", "http://www.asus.com/products.aspx?l1=3&l2=101&l3=308&l4=0&model=1181&modelmenu=1" }, +- { "ASUS", "M2V", "http://asus.com/Product.aspx?P_ID=OqYlEDFfF6ZqZGvp" }, +- { "ASUS", "P2B", "http://www.motherboard.cz/mb/asus/P2B.htm" }, +- { "ASUS", "P2B-D", "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p2b-d/" }, +- { "ASUS", "P2B-DS", "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p2b-ds/" }, +- { "ASUS", "P2B-F", "http://www.motherboard.cz/mb/asus/P2B-F.htm" }, +- { "ASUS", "P2L97-S", "http://www.motherboard.cz/mb/asus/P2L97-S.htm" }, +- { "ASUS", "P5B-Deluxe", "ftp://ftp.asus.com.tw/pub/ASUS/mb/socket775/P5B-Deluxe/" }, +- { "ASUS", "P5KC", "http://www.asus.com/product.aspx?P_ID=fFZ8oUIGmLpwNMjj" }, +- { "ASUS", "P5L-MX", "http://www.asus.com/product.aspx?P_ID=X70d3NCzH2DE9vWH" }, +- { "ASUS", "P6T Deluxe V2", "http://www.asus.com/product.aspx?P_ID=iRlP8RG9han6saZx" }, +- { "A-Trend", "ATC-6220", "http://www.motherboard.cz/mb/atrend/atc6220.htm" }, +- { "BCOM", "WinNET100", "http://www.coreboot.org/BCOM_WINNET100" }, +- { "GIGABYTE", "GA-6BXC", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ClassValue=Motherboard&ProductID=1445&ProductName=GA-6BXC" }, +- { "GIGABYTE", "GA-6BXDU", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=1429" }, +- { "GIGABYTE", "GA-6ZMA", "http://www.gigabyte.de/Support/Motherboard/BIOS_Model.aspx?ProductID=3289" }, +- { "GIGABYTE", "GA-EX58-UD4P", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Overview.aspx?ProductID=2986" }, +- { "GIGABYTE", "GA-EP35-DS3L", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Overview.aspx?ProductID=2778" }, +- { "GIGABYTE", "GA-MA790GP-DS4H", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2887" }, +- { "GIGABYTE", "GA-MA78GPM-DS2H", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2859" }, +- { "Intel", "EP80759", NULL }, +- { "Jetway", "J7F4K1G5D-PB", "http://www.jetway.com.tw/jetway/system/productshow2.asp?id=389&proname=J7F4K1G5D-P" }, +- { "MSI", "MS-6570 (K7N2)", "http://www.msi-computer.de/index.php?func=proddesc&prod_no=519&maincat_no=1" }, +- { "MSI", "MS-7065", "http://browse.geekbench.ca/geekbench2/view/53114" }, +- { "MSI", "MS-7168 (Orion)", "http://support.packardbell.co.uk/uk/item/index.php?i=spec_orion&pi=platform_honeymoon_istart" }, +- { "MSI", "MS-7236 (945PL Neo3)", "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1173" }, +- { "MSI", "MS-7255 (P4M890M)", "http://www.tcsbahamas.com/P4M89.htm" }, +- { "MSI", "MS-7345 (P35 Neo2-FIR)","http://www.msi.com/index.php?func=prodcpusupport&maincat_no=1&cat2_no=170&cat3_no=&prod_no=1261" }, +- { "NEC", "PowerMate 2000", "http://support.necam.com/mobilesolutions/hardware/Desktops/pm2000/celeron/" }, +- { "PC Engines", "Alix.1c", "http://pcengines.ch/alix1c.htm" }, +- { "PC Engines", "Alix.2c2", "http://pcengines.ch/alix2c2.htm" }, +- { "PC Engines", "Alix.2c3", "http://pcengines.ch/alix2c3.htm" }, +- { "PC Engines", "Alix.3c3", "http://pcengines.ch/alix3c3.htm" }, +- { "PC Engines", "Alix.3d3", "http://pcengines.ch/alix3d3.htm" }, +- { "RCA", "RM4100", "http://www.settoplinux.org/index.php?title=RCA_RM4100" }, +- { "Sun", "Blade x6250", "http://www.sun.com/servers/blades/x6250/" }, +- { "Supermicro", "H8QC8", "http://www.supermicro.com/Aplus/motherboard/Opteron/nforce/H8QC8.cfm" }, +- { "Thomson", "IP1000", "http://www.settoplinux.org/index.php?title=Thomson_IP1000" }, +- { "TriGem", "Lomita", "http://www.e4allupgraders.info/dir1/motherboards/socket370/lomita.shtml" }, +- { "T-Online", "S-100", "http://wiki.freifunk-hannover.de/T-Online_S_100" }, +- { "Tyan", "iS5375-1U", "http://www.tyan.com/product_board_detail.aspx?pid=610" }, +- { "Tyan", "S1846", "http://www.tyan.com/archive/products/html/tsunamiatx.html" }, +- { "Tyan", "S2466", "http://www.tyan.com/product_board_detail.aspx?pid=461" }, +- { "Tyan", "S2881", "http://www.tyan.com/product_board_detail.aspx?pid=115" }, +- { "Tyan", "S2882", "http://www.tyan.com/product_board_detail.aspx?pid=121" }, +- { "Tyan", "S2882-D", "http://www.tyan.com/product_board_detail.aspx?pid=127" }, +- { "Tyan", "S2891", "http://www.tyan.com/product_board_detail.aspx?pid=144" }, +- { "Tyan", "S2892", "http://www.tyan.com/product_board_detail.aspx?pid=145" }, +- { "Tyan", "S2895", "http://www.tyan.com/archive/products/html/thunderk8we.html" }, +- { "Tyan", "S3095", "http://www.tyan.com/product_board_detail.aspx?pid=181" }, +- { "Tyan", "S5180", "http://www.tyan.com/product_board_detail.aspx?pid=456" }, +- { "Tyan", "S5191", "http://www.tyan.com/product_board_detail.aspx?pid=343" }, +- { "Tyan", "S5197", "http://www.tyan.com/product_board_detail.aspx?pid=349" }, +- { "Tyan", "S5211", "http://www.tyan.com/product_board_detail.aspx?pid=591" }, +- { "Tyan", "S5211-1U", "http://www.tyan.com/product_board_detail.aspx?pid=593" }, +- { "Tyan", "S5220", "http://www.tyan.com/product_board_detail.aspx?pid=597" }, +- { "Tyan", "S5375", "http://www.tyan.com/product_board_detail.aspx?pid=566" }, +- { "Tyan", "S5376G2NR/S5376WAG2NR","http://www.tyan.com/product_board_detail.aspx?pid=605" }, +- { "Tyan", "S5377", "http://www.tyan.com/product_SKU_spec.aspx?ProductType=MB&pid=642&SKU=600000017" }, +- { "Tyan", "S5397", "http://www.tyan.com/product_board_detail.aspx?pid=560" }, +- { "VIA", "EPIA-EX15000G", "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=450" }, +- { "VIA", "EPIA-LN", "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=473" }, +- { "VIA", "EPIA-M700", "http://via.com.tw/servlet/downloadSvl?motherboard_id=670&download_file_id=3700" }, +- { "VIA", "EPIA-NX15000G", "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=470" }, +- { "VIA", "NAB74X0", "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=590" }, +- { "VIA", "pc2500e", "http://www.via.com.tw/en/initiatives/empowered/pc2500_mainboard/index.jsp" }, +- { "VIA", "VB700X", "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=490" }, +- +- /* Verified working boards that DO need write-enables. */ +- { "Acorp", "6A815EPD", "http://web.archive.org/web/20021206163652/www.acorp.com.tw/English/default.asp" }, +- { "agami", "Aruma", "http://web.archive.org/web/20080212111524/http://www.agami.com/site/ais-6000-series" }, +- { "Albatron", "PM266A Pro", "http://www.albatron.com.tw/English/Product/MB/pro_detail.asp?rlink=Overview&no=56" }, /* FIXME */ +- { "AOpen", "vKM400Am-S", "http://usa.aopen.com/products_detail.aspx?Auno=824" }, +- { "Artec Group", "DBE61", "http://wiki.thincan.org/DBE61" }, +- { "Artec Group", "DBE62", "http://wiki.thincan.org/DBE62" }, +- { "ASUS", "A7V600-X", "http://www.asus.com/product.aspx?P_ID=L2XYS0rmtCjeOr4k" }, +- { "ASUS", "A7V8X-MX SE", "http://www.asus.com/product.aspx?P_ID=1guVBT1qV5oqhHyZ" }, +- { "ASUS", "P4B266", "http://www.ciao.co.uk/ASUS_Intel_845D_Chipset_P4B266__5409807#productdetail" }, +- { "ASUS", "P4P800-E Deluxe", "http://www.asus.com/product.aspx?P_ID=INIJUvLlif7LHp3g" }, +- { "ASUS", "P5A", "ftp://ftp.asus.com.tw/pub/ASUS/mb/sock7/ali/p5a/" }, +- { "Biostar", "P4M80-M4", "http://www.biostar-usa.com/mbdetails.asp?model=p4m80-m4" }, +- { "Elitegroup", "K7VTA3", "http://www.ecs.com.tw/ECSWebSite/Products/ProductsDetail.aspx?detailid=264&CategoryID=1&DetailName=Specification&MenuID=52&LanID=0" }, +- { "EPoX", "EP-8K5A2", "http://www.epox.com/product.asp?ID=EP-8K5A2" }, +- { "EPoX", "EP-BX3", "http://www.epox.com/product.asp?ID=EP-BX3" }, +- { "GIGABYTE", "GA-2761GXDK", "http://www.computerbase.de/news/hardware/mainboards/amd-systeme/2007/mai/gigabyte_dtx-mainboard/" }, +- { "GIGABYTE", "GA-7VT600", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=1666" }, +- { "GIGABYTE", "GA-7ZM", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=1366" }, +- { "GIGABYTE", "GA-K8N-SLI", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=1928" }, +- { "GIGABYTE", "GA-M57SLI-S4", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Overview.aspx?ProductID=2287&ModelName=GA-M57SLI-S4" }, +- { "GIGABYTE", "GA-M61P-S3", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2434" }, +- { "GIGABYTE", "GA-MA78G-DS3H", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2800" }, /* TODO: Rev 1.x or 2.x? */ +- { "GIGABYTE", "GA-MA78GM-S2H", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2758" }, /* TODO: Rev. 1.0, 1.1, or 2.x? */ +- { "GIGABYTE", "GA-MA790FX-DQ6", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2690" }, +- { "HP", "DL145 G3", "http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c00816835&lang=en&cc=us&taskId=101&prodSeriesId=3219755&prodTypeId=15351" }, +- { "IBM", "x3455", "http://www-03.ibm.com/systems/x/hardware/rack/x3455/index.html" }, +- { "Intel", "D201GLY", "http://www.intel.com/support/motherboards/desktop/d201gly/index.htm" }, +- { "IWILL", "DK8-HTX", "http://web.archive.org/web/20060507170150/http://www.iwill.net/product_2.asp?p_id=98" }, +- { "Kontron", "986LCD-M", "http://de.kontron.com/products/boards+and+mezzanines/embedded+motherboards/miniitx+motherboards/986lcdmmitx.html" }, +- { "Mitac", "6513WU", "http://web.archive.org/web/20050313054828/http://www.mitac.com/micweb/products/tyan/6513wu/6513wu.htm" }, +- { "MSI", "MS-6590 (KT4 Ultra)", "http://www.msicomputer.com/product/p_spec.asp?model=KT4_Ultra&class=mb" }, +- { "MSI", "MS-6702E (K8T Neo2-F)","http://www.msicomputer.com/product/p_spec.asp?model=K8T_Neo2-F&class=mb" }, +- { "MSI", "MS-6712 (KT4V)", "http://www.msi.com/index.php?func=proddesc&maincat_no=1&cat2_no=&cat3_no=&prod_no=505" }, +- { "MSI", "MS-7046", "http://www.heimir.de/ms7046/" }, +- { "MSI", "MS-7135 (K8N Neo3)", "http://www.msi-computer.de/index.php?func=proddesc&prod_no=170&maincat_no=1" }, +- { "Shuttle", "AK38N", "http://eu.shuttle.com/en/desktopdefault.aspx/tabid-36/558_read-9889/" }, +- { "Soyo", "SY-7VCA", "http://www.tomshardware.com/reviews/12-socket-370-motherboards,196-15.html" }, +- { "Tyan", "S2498 (Tomcat K7M)", "http://www.tyan.com/archive/products/html/tomcatk7m.html" }, +- { "VIA", "EPIA-CN", "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=400" }, +- { "VIA", "EPIA M/MII/...", "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=202" }, /* EPIA-MII link for now */ +- { "VIA", "EPIA-N/NL", "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=221" }, /* EPIA-N link for now */ +- { "VIA", "EPIA SP", "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=261" }, +- { "VIA", "PC3500G", "http://www.via.com.tw/en/initiatives/empowered/pc3500_mainboard/index.jsp" }, +- +- /* Verified non-working boards (for now). */ +- { "Abit", "IS-10", "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?pMODEL_NAME=IS-10&fMTYPE=Socket+478" }, +- { "ASRock", "K7VT4A+", "http://www.asrock.com/mb/overview.asp?Model=K7VT4A%%2b&s=" }, +- { "ASUS", "MEW-AM", "ftp://ftp.asus.com.tw/pub/ASUS/mb/sock370/810/mew-am/" }, +- { "ASUS", "MEW-VM", "http://www.elhvb.com/mboards/OEM/HP/manual/ASUS%20MEW-VM.htm" }, +- { "ASUS", "P3B-F", "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p3b-f/" }, +- { "ASUS", "P5B", "ftp://ftp.asus.com.tw/pub/ASUS/mb/socket775/P5B/" }, +- { "ASUS", "P5BV-M", "ftp://ftp.asus.com.tw/pub/ASUS/mb/socket775/P5B-VM/" }, +- { "Biostar", "M6TBA", "ftp://ftp.biostar-usa.com/manuals/M6TBA/" }, +- { "Boser", "HS-6637", "http://www.boser.com.tw/manual/HS-62376637v3.4.pdf" }, +- { "DFI", "855GME-MGF", "http://www.dfi.com.tw/portal/CM/cmproduct/XX_cmproddetail/XX_WbProdsWindow?action=e&downloadType=&windowstate=normal&mode=view&downloadFlag=false&itemId=433" }, +- { "FIC", "VA-502", "ftp://ftp.fic.com.tw/motherboard/manual/socket7/va-502/" }, +- { "MSI", "MS-6178", "http://www.msi-technology.de/index.php?func=proddesc&prod_no=343&maincat_no=1" }, +- { "MSI", "MS-7260 (K9N Neo)", "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=255" }, +- { "Soyo", "SY-5VD", "http://www.soyo.com/content/Downloads/163/&c=80&p=464&l=English" }, +- { "Sun", "Fire x4540", "http://www.sun.com/servers/x64/x4540/" }, +- { "Sun", "Fire x4150", "http://www.sun.com/servers/x64/x4150/" }, +- { "Sun", "Fire x4200", "http://www.sun.com/servers/entry/x4200/" }, +- { "Sun", "Fire x4600", "http://www.sun.com/servers/x64/x4600/" }, +- +- /* Verified working laptops. */ +- { "Lenovo", "3000 V100 TF05Cxx", "http://www5.pc.ibm.com/europe/products.nsf/products?openagent&brand=Lenovo3000Notebook&series=Lenovo+3000+V+Series#viewallmodelstop" }, ++void print_supported(void) ++{ ++ print_supported_chips(); ++#if INTERNAL_SUPPORT == 1 ++ print_supported_chipsets(); ++ print_supported_boards(); ++#endif ++#if (NIC3COM_SUPPORT == 1) || (GFXNVIDIA_SUPPORT == 1) || (DRKAISER_SUPPORT == 1) || (SATASII_SUPPORT == 1) ++ printf("\nSupported PCI devices flashrom can use " ++ "as programmer:\n\n"); ++#endif ++#if NIC3COM_SUPPORT == 1 ++ print_supported_pcidevs(nics_3com); ++#endif ++#if GFXNVIDIA_SUPPORT == 1 ++ print_supported_pcidevs(gfx_nvidia); ++#endif ++#if DRKAISER_SUPPORT == 1 ++ print_supported_pcidevs(drkaiser_pcidev); ++#endif ++#if SATASII_SUPPORT == 1 ++ print_supported_pcidevs(satas_sii); ++#endif ++} + +- /* Verified non-working laptops (for now). */ +- { "Acer", "Aspire One", NULL }, +- { "ASUS", "Eee PC 701 4G", "http://www.asus.com/product.aspx?P_ID=h6SPd3tEzLEsrEiS" }, +- { "Dell", "Latitude CPi A366XT", "http://www.coreboot.org/Dell_Latitude_CPi_A366XT" }, +- { "HP/Compaq", "nx9010", "http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?lang=en&cc=us&objectID=c00348514" }, +- { "IBM/Lenovo", "Thinkpad T40p", "http://www.thinkwiki.org/wiki/Category:T40p" }, +- { "IBM/Lenovo", "240", "http://www.stanford.edu/~bresnan//tp240.html" }, +- +- { NULL, NULL, 0 }, +-}; + +-/* Please keep these lists alphabetically ordered by vendor/board. */ +-const struct board_info_notes boards_notes[] = { ++#if INTERNAL_SUPPORT == 1 ++/* Please keep this list alphabetically ordered by vendor/board. */ ++const struct board_info boards_ok[] = { + /* Verified working boards that don't need write-enables. */ +- { "ASI", "MB-5BLMP", "Used in the IGEL WinNET III thin client." }, +- { "ASUS", "A8V-E SE", "See http://www.coreboot.org/pipermail/coreboot/2007-October/026496.html." }, +- { "ASUS", "M2A-VM", "See http://www.coreboot.org/pipermail/coreboot/2007-September/025281.html." }, +- { "BCOM", "WinNET100", "Used in the IGEL-316 thin client." }, +- { "GIGABYTE", "GA-7ZM", "Works fine iff you remove jumper JP9 on the board and disable the flash protection BIOS option." }, +- +- /* Verified working boards that DO need write-enables. */ +- { "Acer", "Aspire One", "See http://www.coreboot.org/pipermail/coreboot/2009-May/048041.html." }, ++ { "Abit", "AX8", }, ++ { "Abit", "Fatal1ty F-I90HD", }, ++ { "Advantech", "PCM-5820", }, ++ { "ASI", "MB-5BLMP", }, ++ { "ASRock", "A770CrossFire", }, ++ { "ASRock", "K8S8X", }, ++ { "ASRock", "M3A790GXH/128M" }, ++ { "ASUS", "A7N8X Deluxe", }, ++ { "ASUS", "A7N8X-E Deluxe", }, ++ { "ASUS", "A7V400-MX", }, ++ { "ASUS", "A7V8X-MX", }, ++ { "ASUS", "A8N-E", }, ++ { "ASUS", "A8NE-FM/S", }, ++ { "ASUS", "A8N-SLI", }, ++ { "ASUS", "A8N-SLI Premium", }, ++ { "ASUS", "A8V Deluxe", }, ++ { "ASUS", "A8V-E Deluxe", }, ++ { "ASUS", "A8V-E SE", }, ++ { "ASUS", "K8V", }, ++ { "ASUS", "K8V SE Deluxe", }, ++ { "ASUS", "K8V-X SE", }, ++ { "ASUS", "M2A-MX", }, ++ { "ASUS", "M2A-VM", }, ++ { "ASUS", "M2N-E", }, ++ { "ASUS", "M2V", }, ++ { "ASUS", "M3A78-EM", }, ++ { "ASUS", "P2B", }, ++ { "ASUS", "P2B-D", }, ++ { "ASUS", "P2B-DS", }, ++ { "ASUS", "P2B-F", }, ++ { "ASUS", "P2L97-S", }, ++ { "ASUS", "P5B-Deluxe", }, ++ { "ASUS", "P5KC", }, ++ { "ASUS", "P5L-MX", }, ++ { "ASUS", "P6T Deluxe V2", }, ++ { "A-Trend", "ATC-6220", }, ++ { "BCOM", "WinNET100", }, ++ { "DFI", "Blood-Iron P35 T2RL", }, ++ { "Elitegroup", "K7S5A", }, ++ { "Elitegroup", "P6VAP-A+", }, ++ { "GIGABYTE", "GA-6BXC", }, ++ { "GIGABYTE", "GA-6BXDU", }, ++ { "GIGABYTE", "GA-6ZMA", }, ++ { "GIGABYTE", "GA-7ZM", }, ++ { "GIGABYTE", "GA-EP35-DS3L", }, ++ { "GIGABYTE", "GA-EX58-UD4P", }, ++ { "GIGABYTE", "GA-MA78GPM-DS2H", }, ++ { "GIGABYTE", "GA-MA790GP-DS4H", }, ++ { "GIGABYTE", "GA-MA770T-UD3P", }, ++ { "Intel", "EP80759", }, ++ { "Jetway", "J7F4K1G5D-PB", }, ++ { "MSI", "MS-6153", }, ++ { "MSI", "MS-6156", }, ++ { "MSI", "MS-6570 (K7N2)", }, ++ { "MSI", "MS-7065", }, ++ { "MSI", "MS-7168 (Orion)", }, ++ { "MSI", "MS-7236 (945PL Neo3)", }, ++ { "MSI", "MS-7255 (P4M890M)", }, ++ { "MSI", "MS-7345 (P35 Neo2-FIR)", }, ++ { "MSI", "MS-7368 (K9AG Neo2-Digital)", }, ++ { "NEC", "PowerMate 2000", }, ++ { "PC Engines", "Alix.1c", }, ++ { "PC Engines", "Alix.2c2", }, ++ { "PC Engines", "Alix.2c3", }, ++ { "PC Engines", "Alix.3c3", }, ++ { "PC Engines", "Alix.3d3", }, ++ { "RCA", "RM4100", }, ++ { "Sun", "Blade x6250", }, ++ { "Supermicro", "H8QC8", }, ++ { "Thomson", "IP1000", }, ++ { "TriGem", "Lomita", }, ++ { "T-Online", "S-100", }, ++ { "Tyan", "iS5375-1U", }, ++ { "Tyan", "S1846", }, ++ { "Tyan", "S2466", }, ++ { "Tyan", "S2881", }, ++ { "Tyan", "S2882", }, ++ { "Tyan", "S2882-D", }, ++ { "Tyan", "S2891", }, ++ { "Tyan", "S2892", }, ++ { "Tyan", "S2895", }, ++ { "Tyan", "S3095", }, ++ { "Tyan", "S5180", }, ++ { "Tyan", "S5191", }, ++ { "Tyan", "S5197", }, ++ { "Tyan", "S5211", }, ++ { "Tyan", "S5211-1U", }, ++ { "Tyan", "S5220", }, ++ { "Tyan", "S5375", }, ++ { "Tyan", "S5376G2NR/S5376WAG2NR", }, ++ { "Tyan", "S5377", }, ++ { "Tyan", "S5397", }, ++ { "VIA", "EPIA-CN", }, ++ { "VIA", "EPIA-EX15000G", }, ++ { "VIA", "EPIA-LN", }, ++ { "VIA", "EPIA-M700", }, ++ { "VIA", "EPIA-NX15000G", }, ++ { "VIA", "EPIA-SP", }, ++ { "VIA", "NAB74X0", }, ++ { "VIA", "pc2500e", }, ++ { "VIA", "VB700X", }, ++ ++ {}, ++}; + ++/* Please keep this list alphabetically ordered by vendor/board. */ ++const struct board_info boards_bad[] = { + /* Verified non-working boards (for now). */ +- { "MSI", "MS-6178", "Immediately powers off if you try to hot-plug the chip. However, this does '''not''' happen if you use coreboot." }, +- { "MSI", "MS-7260 (K9N Neo)", "Interestingly flashrom does not work when the vendor BIOS is booted, but it ''does'' work flawlessly when the machine is booted with coreboot." }, ++ { "Abit", "IS-10", }, ++ { "ASRock", "K7VT4A+", }, ++ { "ASUS", "MEW-AM", }, ++ { "ASUS", "MEW-VM", }, ++ { "ASUS", "P3B-F", }, ++ { "ASUS", "P5B", }, ++ { "ASUS", "P5BV-M", }, ++ { "Biostar", "M6TBA", }, ++ { "Boser", "HS-6637", }, ++ { "DFI", "855GME-MGF", }, ++ { "FIC", "VA-502", }, ++ { "MSI", "MS-6178", }, ++ { "MSI", "MS-7260 (K9N Neo)", }, ++ { "Soyo", "SY-5VD", }, ++ { "Sun", "Fire x4150", }, ++ { "Sun", "Fire x4200", }, ++ { "Sun", "Fire x4540", }, ++ { "Sun", "Fire x4600", }, ++ ++ {}, ++}; + ++/* Please keep this list alphabetically ordered by vendor/board. */ ++const struct board_info laptops_ok[] = { + /* Verified working laptops. */ +- /* None which need comments, yet... */ +- +- /* Verified non-working laptops (for now). */ +- { "Acer", "Aspire One", "http://www.coreboot.org/pipermail/coreboot/2009-May/048041.html" }, +- { "ASUS", "Eee PC 701 4G", "It seems the chip (25X40VSIG) is behind some SPI flash translation layer (likely in the EC, the ENE KB3310)." }, +- { "Dell", "Latitude CPi A366XT", "The laptop immediately powers off if you try to hot-swap the chip. It's not yet tested if write/erase would work on this laptop." }, +- { "HP/Compaq", "nx9010", "Hangs upon '''flashrom -V''' (needs hard power-cycle then)." }, +- { "IBM/Lenovo", "Thinkpad T40p", "Seems to (partially) work at first, but one block/sector cannot be written which then leaves you with a bricked laptop. Maybe this can be investigated and fixed in software later." }, ++ { "Lenovo", "3000 V100 TF05Cxx", }, + +- { NULL, NULL, 0 }, ++ {}, + }; + +-static int url(const char *vendor, const char *board) +-{ +- int i; +- const struct board_info_url *b = boards_url; +- +- for (i = 0; b[i].vendor != NULL; i++) { +- if (!strcmp(vendor, b[i].vendor) && !strcmp(board, b[i].name)) +- return i; +- } +- +- return -1; +-} +- +-static int note(const char *vendor, const char *board) +-{ +- int i; +- const struct board_info_notes *n = boards_notes; +- +- for (i = 0; n[i].vendor != NULL; i++) { +- if (!strcmp(vendor, n[i].vendor) && !strcmp(board, n[i].name)) +- return i; +- } +- +- return -1; +-} +- +-void print_supported_chipsets_wiki(void) +-{ +- int i, j, enablescount = 0, color = 1; +- const struct penable *e; +- +- for (e = chipset_enables; e->vendor_name != NULL; e++) +- enablescount++; +- +- printf("\n== Supported chipsets ==\n\nTotal amount of supported " +- "chipsets: '''%d'''\n\n{| border=\"0\" valign=\"top\"\n| " +- "valign=\"top\"|\n\n%s", enablescount, chipset_th); +- +- e = chipset_enables; +- for (i = 0, j = 0; e[i].vendor_name != NULL; i++, j++) { +- /* Alternate colors if the vendor changes. */ +- if (i > 0 && strcmp(e[i].vendor_name, e[i - 1].vendor_name)) +- color = !color; +- +- printf("|- bgcolor=\"#%s\" valign=\"top\"\n| %s || %s " +- "|| %04x:%04x || %s\n", (color) ? "eeeeee" : "dddddd", +- e[i].vendor_name, e[i].device_name, +- e[i].vendor_id, e[i].device_id, +- (e[i].status == OK) ? "{{OK}}" : "?"); +- +- /* Split table in three columns. */ +- if (j >= (enablescount / 3 + 1)) { +- printf("\n|}\n\n| valign=\"top\"|\n\n%s", chipset_th); +- j = 0; +- } +- } +- +- printf("\n|}\n\n|}\n"); +-} +- +-static void wiki_helper(const char *heading, const char *status, +- int cols, const struct board_info boards[]) +-{ +- int i, j, k, c, boardcount = 0, color = 1, num_notes = 0; +- const struct board_info *b; +- const struct board_info_url *u = boards_url; +- char *notes = calloc(1, 1); +- char tmp[900 + 1]; +- +- for (b = boards; b->vendor != NULL; b++) +- boardcount++; +- +- printf("\n'''%s'''\n\nTotal amount of boards: '''%d'''\n\n" +- "{| border=\"0\" valign=\"top\"\n| valign=\"top\"|\n\n%s", +- heading, boardcount, board_th); +- +- for (i = 0, j = 0, b = boards; b[i].vendor != NULL; i++, j++) { +- /* Alternate colors if the vendor changes. */ +- if (i > 0 && strcmp(b[i].vendor, b[i - 1].vendor)) +- color = !color; +- +- k = url(b[i].vendor, b[i].name); +- c = note(b[i].vendor, b[i].name); +- +- printf("|- bgcolor=\"#%s\" valign=\"top\"\n| %s || %s%s %s%s ||" +- " {{%s}}", (color) ? "eeeeee" : "dddddd", b[i].vendor, +- (k != -1 && u[k].url) ? "[" : "", +- (k != -1 && u[k].url) ? u[k].url : "", +- b[i].name, (k != -1 && u[k].url) ? "]" : "", status); +- +- if (c != -1) { +- printf("%d\n", num_notes + 1); +- snprintf((char *)&tmp, 900, "%d %s
\n", +- 1 + num_notes++, boards_notes[c].note); +- notes = strcat_realloc(notes, (char *)&tmp); +- } else { +- printf("\n"); +- } +- +- /* Split table in 'cols' columns. */ +- if (j >= (boardcount / cols + 1)) { +- printf("\n|}\n\n| valign=\"top\"|\n\n%s", board_th); +- j = 0; +- } +- } +- +- printf("\n|}\n\n|}\n"); +- +- if (num_notes > 0) +- printf("\n\n%s\n", notes); +- free(notes); +-} +- +-static void wiki_helper2(const char *heading, int cols) +-{ +- int i, j, k, boardcount = 0, color = 1; +- struct board_pciid_enable *b; +- const struct board_info_url *u = boards_url; +- +- for (b = board_pciid_enables; b->vendor_name != NULL; b++) +- boardcount++; +- +- printf("\n'''%s'''\n\nTotal amount of boards: '''%d'''\n\n" +- "{| border=\"0\" valign=\"top\"\n| valign=\"top\"|\n\n%s", +- heading, boardcount, board_th2); +- +- b = board_pciid_enables; +- for (i = 0, j = 0; b[i].vendor_name != NULL; i++, j++) { +- /* Alternate colors if the vendor changes. */ +- if (i > 0 && strcmp(b[i].vendor_name, b[i - 1].vendor_name)) +- color = !color; +- +- k = url(b[i].vendor_name, b[i].board_name); +- +- printf("|- bgcolor=\"#%s\" valign=\"top\"\n| %s || %s%s %s%s " +- "|| %s%s%s%s || {{OK}}\n", (color) ? "eeeeee" : "dddddd", +- b[i].vendor_name, (k != -1 && u[k].url) ? "[" : "", +- (k != -1 && u[k].url) ? u[k].url : "", b[i].board_name, +- (k != -1 && u[k].url) ? "]" : "", +- (b[i].lb_vendor) ? "-m " : "—", +- (b[i].lb_vendor) ? b[i].lb_vendor : "", +- (b[i].lb_vendor) ? ":" : "", +- (b[i].lb_vendor) ? b[i].lb_part : ""); +- +- /* Split table in three columns. */ +- if (j >= (boardcount / cols + 1)) { +- printf("\n|}\n\n| valign=\"top\"|\n\n%s", board_th2); +- j = 0; +- } +- } +- +- printf("\n|}\n\n|}\n"); +-} +- +-void print_supported_boards_wiki(void) +-{ +- printf("%s", board_intro); +- wiki_helper("Known good (worked out of the box)", "OK", 3, boards_ok); +- wiki_helper2("Known good (with write-enable code in flashrom)", 3); +- wiki_helper("Not supported (yet)", "No", 3, boards_bad); +- +- printf("%s", laptop_intro); +- wiki_helper("Known good (worked out of the box)", "OK", 1, laptops_ok); +- wiki_helper("Not supported (yet)", "No", 1, laptops_bad); +-} +- +-void print_supported_chips_wiki(void) +-{ +- int i = 0, c = 1, chipcount = 0; +- struct flashchip *f, *old = NULL; +- uint32_t t; +- +- for (f = flashchips; f->name != NULL; f++) +- chipcount++; +- +- printf("\n== Supported chips ==\n\nTotal amount of supported " +- "chips: '''%d'''\n\n{| border=\"0\" valign=\"top\"\n" +- "| valign=\"top\"|\n\n%s", chipcount, chip_th); +- +- for (f = flashchips; f->name != NULL; f++, i++) { +- /* Don't print "unknown XXXX SPI chip" entries. */ +- if (!strncmp(f->name, "unknown", 7)) +- continue; +- +- /* Alternate colors if the vendor changes. */ +- if (old != NULL && strcmp(old->vendor, f->vendor)) +- c = !c; +- +- t = f->tested; +- printf("|- bgcolor=\"#%s\" valign=\"top\"\n| %s || %s || %d " +- "|| %s || {{%s}} || {{%s}} || {{%s}} || {{%s}}\n", +- (c == 1) ? "eeeeee" : "dddddd", f->vendor, f->name, +- f->total_size, flashbuses_to_text(f->bustype), +- (t & TEST_OK_PROBE) ? "OK" : +- (t & TEST_BAD_PROBE) ? "No" : ((c) ? "?2" : "?"), +- (t & TEST_OK_READ) ? "OK" : +- (t & TEST_BAD_READ) ? "No" : ((c) ? "?2" : "?"), +- (t & TEST_OK_ERASE) ? "OK" : +- (t & TEST_BAD_ERASE) ? "No" : ((c) ? "?2" : "?"), +- (t & TEST_OK_WRITE) ? "OK" : +- (t & TEST_BAD_WRITE) ? "No" : ((c) ? "?2" : "?")); +- +- /* Split table into three columns. */ +- if (i >= (chipcount / 3 + 1)) { +- printf("\n|}\n\n| valign=\"top\"|\n\n%s", chip_th); +- i = 0; +- } +- +- old = f; +- } +- +- printf("\n|}\n\n|}\n"); +-} +- +-void print_supported_pcidevs_wiki(struct pcidev_status *devs) +-{ +- int i = 0; +- static int c = 0; +- +- /* Alternate colors if the vendor changes. */ +- c = !c; +- +- for (i = 0; devs[i].vendor_name != NULL; i++) { +- printf("|- bgcolor=\"#%s\" valign=\"top\"\n| %s || %s || " +- "%04x:%04x || {{%s}}\n", (c) ? "eeeeee" : "dddddd", +- devs[i].vendor_name, devs[i].device_name, +- devs[i].vendor_id, devs[i].device_id, +- (devs[i].status == PCI_NT) ? (c) ? "?2" : "?" : "OK"); +- } +-} +- +-void print_wiki_tables(void) +-{ +- time_t t = time(NULL); +- +- printf(wiki_header, ctime(&t), flashrom_version); +- print_supported_chips_wiki(); +- print_supported_chipsets_wiki(); +- print_supported_boards_wiki(); +- printf("%s", programmer_section); +- print_supported_pcidevs_wiki(nics_3com); +- print_supported_pcidevs_wiki(satas_sii); +- printf("\n|}\n"); +-} ++/* Please keep this list alphabetically ordered by vendor/board. */ ++const struct board_info laptops_bad[] = { ++ /* Verified non-working laptops (for now). */ ++ { "Acer", "Aspire One", }, ++ { "ASUS", "Eee PC 701 4G", }, ++ { "Dell", "Latitude CPi A366XT", }, ++ { "HP/Compaq", "nx9010", }, ++ { "IBM/Lenovo", "Thinkpad T40p", }, ++ { "IBM/Lenovo", "240", }, ++ ++ {}, ++}; ++#endif + +diff --git a/print_wiki.c b/print_wiki.c +new file mode 100644 +index 0000000..aa5475c +--- /dev/null ++++ b/print_wiki.c +@@ -0,0 +1,571 @@ ++/* ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2009 Uwe Hermann ++ * Copyright (C) 2009 Carl-Daniel Hailfinger ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include "flash.h" ++#include "flashchips.h" ++ ++struct board_info_url { ++ const char *vendor; ++ const char *name; ++ const char *url; ++}; ++ ++struct board_info_notes { ++ const char *vendor; ++ const char *name; ++ const char *note; ++}; ++ ++const char *wiki_header = "= Supported devices =\n\n\ ++
\n\ ++Please do '''not''' edit these tables in the wiki directly, they are \ ++generated by pasting '''flashrom -z''' output.
\ ++'''Last update:''' %s(generated by flashrom %s)\n
\n"; ++ ++const char *chipset_th = "{| border=\"0\" style=\"font-size: smaller\"\n\ ++|- bgcolor=\"#6699dd\"\n! align=\"left\" | Vendor\n\ ++! align=\"left\" | Southbridge\n! align=\"left\" | PCI IDs\n\ ++! align=\"left\" | Status\n\n"; ++ ++const char *board_th = "{| border=\"0\" style=\"font-size: smaller\" \ ++valign=\"top\"\n|- bgcolor=\"#6699dd\"\n! align=\"left\" | Vendor\n\ ++! align=\"left\" | Mainboard\n! align=\"left\" | Status\n\n"; ++ ++const char *board_th2 = "{| border=\"0\" style=\"font-size: smaller\" \ ++valign=\"top\"\n|- bgcolor=\"#6699dd\"\n! align=\"left\" | Vendor\n\ ++! align=\"left\" | Mainboard\n! align=\"left\" | Required option\n\ ++! align=\"left\" | Status\n\n"; ++ ++const char *board_intro = "\ ++\n== Supported mainboards ==\n\n\ ++In general, it is very likely that flashrom works out of the box even if your \ ++mainboard is not listed below.\n\nThis is a list of mainboards where we have \ ++verified that they either do or do not need any special initialization to \ ++make flashrom work (given flashrom supports the respective chipset and flash \ ++chip), or that they do not yet work at all. If they do not work, support may \ ++or may not be added later.\n\n\ ++Mainboards which don't appear in the list may or may not work (we don't \ ++know, someone has to give it a try). Please report any further verified \ ++mainboards on the [[Mailinglist|mailing list]].\n"; ++ ++const char *chip_th = "{| border=\"0\" style=\"font-size: smaller\" \ ++valign=\"top\"\n|- bgcolor=\"#6699dd\"\n! align=\"left\" | Vendor\n\ ++! align=\"left\" | Device\n! align=\"left\" | Size / KB\n\ ++! align=\"left\" | Type\n! align=\"left\" colspan=\"4\" | Status\n\n\ ++|- bgcolor=\"#6699ff\"\n| colspan=\"4\" |  \n\ ++| Probe\n| Read\n| Erase\n| Write\n\n"; ++ ++const char *programmer_section = "\ ++\n== Supported programmers ==\n\nThis is a list \ ++of supported PCI devices flashrom can use as programmer:\n\n{| border=\"0\" \ ++valign=\"top\"\n| valign=\"top\"|\n\n{| border=\"0\" style=\"font-size: \ ++smaller\" valign=\"top\"\n|- bgcolor=\"#6699dd\"\n! align=\"left\" | Vendor\n\ ++! align=\"left\" | Device\n! align=\"left\" | PCI IDs\n\ ++! align=\"left\" | Status\n\n"; ++ ++const char *laptop_intro = "\n== Supported laptops/notebooks ==\n\n\ ++In general, flashing laptops is more difficult because laptops\n\n\ ++* often use the flash chip for stuff besides the BIOS,\n\ ++* often have special protection stuff which has to be handled by flashrom,\n\ ++* often use flash translation circuits which need drivers in flashrom.\n\n\ ++
\n\ ++'''IMPORTANT:''' At this point we recommend to '''not''' use flashrom on \ ++untested laptops unless you have a means to recover from a flashing that goes \ ++wrong (a working backup flash chip and/or good soldering skills).\n
\n"; ++ ++/* Please keep these lists alphabetically ordered by vendor/board. */ ++const struct board_info_url boards_url[] = { ++ /* Verified working boards that don't need write-enables. */ ++ { "Abit", "AX8", "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?DEFTITLE=Y&fMTYPE=Socket%20939&pMODEL_NAME=AX8" }, ++ { "Abit", "Fatal1ty F-I90HD", "http://www.abit.com.tw/page/de/motherboard/motherboard_detail.php?pMODEL_NAME=Fatal1ty+F-I90HD&fMTYPE=LGA775" }, ++ { "Advantech", "PCM-5820", "http://www.emacinc.com/sbc_pc_compatible/pcm_5820.htm" }, ++ { "ASI", "MB-5BLMP", "http://www.hojerteknik.com/winnet.htm" }, ++ { "ASRock", "A770CrossFire", "http://www.asrock.com/mb/overview.asp?Model=A770CrossFire&s=AM2\%2b" }, ++ { "ASRock", "K8S8X", "http://www.asrock.com/mb/overview.asp?Model=K8S8X" }, ++ { "ASRock", "M3A790GXH/128M" "http://www.asrock.com/MB/overview.asp?Model=M3A790GXH/128M" }, ++ { "ASUS", "A7N8X Deluxe", "http://www.asus.com/Product.aspx?P_ID=wAsRYm41KTp78MFC" }, ++ { "ASUS", "A7N8X-E Deluxe", "http://www.asus.com/products.aspx?l1=3&l2=13&l3=56&l4=0&model=217&modelmenu=1" }, ++ { "ASUS", "A7V400-MX", "http://www.asus.com.tw/products.aspx?l1=3&l2=13&l3=63&l4=0&model=228&modelmenu=1" }, ++ { "ASUS", "A7V8X-MX", "http://www.asus.com.tw/products.aspx?l1=3&l2=13&l3=64&l4=0&model=229&modelmenu=1" }, ++ { "ASUS", "A8N-E", "http://www.asus.com.tw/products.aspx?l1=3&l2=15&l3=171&l4=0&model=455&modelmenu=2" }, ++ { "ASUS", "A8NE-FM/S", "http://www.hardwareschotte.de/hardware/preise/proid_1266090/preis_ASUS+A8NE-FM" }, ++ { "ASUS", "A8N-SLI", "http://asus.com/product.aspx?P_ID=J9FKa8z2xVId3pDK" }, ++ { "ASUS", "A8N-SLI Premium", "http://www.asus.com.tw/products.aspx?l1=3&l2=15&l3=148&l4=0&model=539&modelmenu=1" }, ++ { "ASUS", "A8V Deluxe", "http://www.asus.com/product.aspx?P_ID=tvpdgPNCPaABZRVU" }, ++ { "ASUS", "A8V-E Deluxe", "http://www.asus.com.tw/products.aspx?l1=3&l2=15&l3=143&l4=0&model=376&modelmenu=1" }, ++ { "ASUS", "A8V-E SE", "http://www.asus.com.tw/products.aspx?l1=3&l2=15&l3=143&l4=0&model=576&modelmenu=1" }, ++ { "ASUS", "K8V", "http://www.asus.com/product.aspx?P_ID=fG2KZOWF7v6MRFRm" }, ++ { "ASUS", "K8V SE Deluxe", "http://www.asus.com/product.aspx?P_ID=65HeDI8XM1u6Uy6o" }, ++ { "ASUS", "K8V-X SE", "http://asus.com/product.aspx?P_ID=lzDXlbBVHkdckHVr" }, ++ { "ASUS", "M2A-MX", "http://www.asus.com/products.aspx?l1=3&l2=101&l3=583&l4=0&model=1909&modelmenu=1" }, ++ { "ASUS", "M2A-VM", "http://www.asus.com.tw/products.aspx?l1=3&l2=101&l3=496&l4=0&model=1568&modelmenu=1" }, ++ { "ASUS", "M2N-E", "http://www.asus.com/products.aspx?l1=3&l2=101&l3=308&l4=0&model=1181&modelmenu=1" }, ++ { "ASUS", "M2V", "http://asus.com/Product.aspx?P_ID=OqYlEDFfF6ZqZGvp" }, ++ { "ASUS", "M3A78-EM", "http://www.asus.com/product.aspx?P_ID=KjpYqzmAd9vsTM2D" }, ++ { "ASUS", "P2B", "http://www.motherboard.cz/mb/asus/P2B.htm" }, ++ { "ASUS", "P2B-D", "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p2b-d/" }, ++ { "ASUS", "P2B-DS", "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p2b-ds/" }, ++ { "ASUS", "P2B-F", "http://www.motherboard.cz/mb/asus/P2B-F.htm" }, ++ { "ASUS", "P2L97-S", "http://www.motherboard.cz/mb/asus/P2L97-S.htm" }, ++ { "ASUS", "P5B-Deluxe", "ftp://ftp.asus.com.tw/pub/ASUS/mb/socket775/P5B-Deluxe/" }, ++ { "ASUS", "P5KC", "http://www.asus.com/product.aspx?P_ID=fFZ8oUIGmLpwNMjj" }, ++ { "ASUS", "P5L-MX", "http://www.asus.com/product.aspx?P_ID=X70d3NCzH2DE9vWH" }, ++ { "ASUS", "P6T Deluxe V2", "http://www.asus.com/product.aspx?P_ID=iRlP8RG9han6saZx" }, ++ { "A-Trend", "ATC-6220", "http://www.motherboard.cz/mb/atrend/atc6220.htm" }, ++ { "BCOM", "WinNET100", "http://www.coreboot.org/BCOM_WINNET100" }, ++ { "DFI", "Blood-Iron P35 T2RL", "http://lp.lanparty.com.tw/portal/CM/cmproduct/XX_cmproddetail/XX_WbProdsWindow?itemId=516&downloadFlag=false&action=1" }, ++ { "Elitegroup", "K7S5A", "http://www.ecs.com.tw/ECSWebSite/Products/ProductsDetail.aspx?detailid=279&CategoryID=1&DetailName=Specification&MenuID=1&LanID=0" }, ++ { "Elitegroup", "P6VAP-A+", "http://www.ecs.com.tw/ECSWebSite/Products/ProductsDetail.aspx?detailid=117&CategoryID=1&DetailName=Specification&MenuID=1&LanID=0" }, ++ { "GIGABYTE", "GA-6BXC", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ClassValue=Motherboard&ProductID=1445&ProductName=GA-6BXC" }, ++ { "GIGABYTE", "GA-6BXDU", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=1429" }, ++ { "GIGABYTE", "GA-6ZMA", "http://www.gigabyte.de/Support/Motherboard/BIOS_Model.aspx?ProductID=3289" }, ++ { "GIGABYTE", "GA-EX58-UD4P", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Overview.aspx?ProductID=2986" }, ++ { "GIGABYTE", "GA-EP35-DS3L", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Overview.aspx?ProductID=2778" }, ++ { "GIGABYTE", "GA-MA790GP-DS4H", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2887" }, ++ { "GIGABYTE", "GA-MA78GPM-DS2H", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2859" }, ++ { "GIGABYTE", "GA-MA770T-UD3P", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=3096" }, ++ { "Intel", "EP80759", NULL }, ++ { "Jetway", "J7F4K1G5D-PB", "http://www.jetway.com.tw/jetway/system/productshow2.asp?id=389&proname=J7F4K1G5D-P" }, ++ { "MSI", "MS-6153", "http://www.msi.com/index.php?func=proddesc&maincat_no=1&cat2_no=&cat3_no=&prod_no=336" }, ++ { "MSI", "MS-6156", "http://uk.ts.fujitsu.com/rl/servicesupport/techsupport/boards/Motherboards/MicroStar/Ms6156/MS6156.htm" }, ++ { "MSI", "MS-6570 (K7N2)", "http://www.msi-computer.de/index.php?func=proddesc&prod_no=519&maincat_no=1" }, ++ { "MSI", "MS-7065", "http://browse.geekbench.ca/geekbench2/view/53114" }, ++ { "MSI", "MS-7168 (Orion)", "http://support.packardbell.co.uk/uk/item/index.php?i=spec_orion&pi=platform_honeymoon_istart" }, ++ { "MSI", "MS-7236 (945PL Neo3)", "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1173" }, ++ { "MSI", "MS-7255 (P4M890M)", "http://www.tcsbahamas.com/P4M89.htm" }, ++ { "MSI", "MS-7345 (P35 Neo2-FIR)","http://www.msi.com/index.php?func=prodcpusupport&maincat_no=1&cat2_no=170&cat3_no=&prod_no=1261" }, ++ { "MSI", "MS-7368 (K9AG Neo2-Digital)", "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1241" }, ++ { "NEC", "PowerMate 2000", "http://support.necam.com/mobilesolutions/hardware/Desktops/pm2000/celeron/" }, ++ { "PC Engines", "Alix.1c", "http://pcengines.ch/alix1c.htm" }, ++ { "PC Engines", "Alix.2c2", "http://pcengines.ch/alix2c2.htm" }, ++ { "PC Engines", "Alix.2c3", "http://pcengines.ch/alix2c3.htm" }, ++ { "PC Engines", "Alix.3c3", "http://pcengines.ch/alix3c3.htm" }, ++ { "PC Engines", "Alix.3d3", "http://pcengines.ch/alix3d3.htm" }, ++ { "RCA", "RM4100", "http://www.settoplinux.org/index.php?title=RCA_RM4100" }, ++ { "Sun", "Blade x6250", "http://www.sun.com/servers/blades/x6250/" }, ++ { "Supermicro", "H8QC8", "http://www.supermicro.com/Aplus/motherboard/Opteron/nforce/H8QC8.cfm" }, ++ { "Tekram", "P6Pro-A5", "http://www.motherboard.cz/mb/tekram/P6Pro-A5.htm" }, ++ { "Thomson", "IP1000", "http://www.settoplinux.org/index.php?title=Thomson_IP1000" }, ++ { "TriGem", "Lomita", "http://www.e4allupgraders.info/dir1/motherboards/socket370/lomita.shtml" }, ++ { "T-Online", "S-100", "http://wiki.freifunk-hannover.de/T-Online_S_100" }, ++ { "Tyan", "iS5375-1U", "http://www.tyan.com/product_board_detail.aspx?pid=610" }, ++ { "Tyan", "S1846", "http://www.tyan.com/archive/products/html/tsunamiatx.html" }, ++ { "Tyan", "S2466", "http://www.tyan.com/product_board_detail.aspx?pid=461" }, ++ { "Tyan", "S2881", "http://www.tyan.com/product_board_detail.aspx?pid=115" }, ++ { "Tyan", "S2882", "http://www.tyan.com/product_board_detail.aspx?pid=121" }, ++ { "Tyan", "S2882-D", "http://www.tyan.com/product_board_detail.aspx?pid=127" }, ++ { "Tyan", "S2891", "http://www.tyan.com/product_board_detail.aspx?pid=144" }, ++ { "Tyan", "S2892", "http://www.tyan.com/product_board_detail.aspx?pid=145" }, ++ { "Tyan", "S2895", "http://www.tyan.com/archive/products/html/thunderk8we.html" }, ++ { "Tyan", "S3095", "http://www.tyan.com/product_board_detail.aspx?pid=181" }, ++ { "Tyan", "S5180", "http://www.tyan.com/product_board_detail.aspx?pid=456" }, ++ { "Tyan", "S5191", "http://www.tyan.com/product_board_detail.aspx?pid=343" }, ++ { "Tyan", "S5197", "http://www.tyan.com/product_board_detail.aspx?pid=349" }, ++ { "Tyan", "S5211", "http://www.tyan.com/product_board_detail.aspx?pid=591" }, ++ { "Tyan", "S5211-1U", "http://www.tyan.com/product_board_detail.aspx?pid=593" }, ++ { "Tyan", "S5220", "http://www.tyan.com/product_board_detail.aspx?pid=597" }, ++ { "Tyan", "S5375", "http://www.tyan.com/product_board_detail.aspx?pid=566" }, ++ { "Tyan", "S5376G2NR/S5376WAG2NR","http://www.tyan.com/product_board_detail.aspx?pid=605" }, ++ { "Tyan", "S5377", "http://www.tyan.com/product_SKU_spec.aspx?ProductType=MB&pid=642&SKU=600000017" }, ++ { "Tyan", "S5397", "http://www.tyan.com/product_board_detail.aspx?pid=560" }, ++ { "VIA", "EPIA-EX15000G", "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=450" }, ++ { "VIA", "EPIA-LN", "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=473" }, ++ { "VIA", "EPIA-M700", "http://via.com.tw/servlet/downloadSvl?motherboard_id=670&download_file_id=3700" }, ++ { "VIA", "EPIA-NX15000G", "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=470" }, ++ { "VIA", "NAB74X0", "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=590" }, ++ { "VIA", "pc2500e", "http://www.via.com.tw/en/initiatives/empowered/pc2500_mainboard/index.jsp" }, ++ { "VIA", "VB700X", "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=490" }, ++ ++ /* Verified working boards that DO need write-enables. */ ++ { "Abit", "IP35", "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?fMTYPE=LGA775&pMODEL_NAME=IP35" }, ++ { "Acorp", "6A815EPD", "http://web.archive.org/web/20021206163652/www.acorp.com.tw/English/default.asp" }, ++ { "agami", "Aruma", "http://web.archive.org/web/20080212111524/http://www.agami.com/site/ais-6000-series" }, ++ { "Albatron", "PM266A Pro", "http://www.albatron.com.tw/English/Product/MB/pro_detail.asp?rlink=Overview&no=56" }, /* FIXME */ ++ { "AOpen", "vKM400Am-S", "http://usa.aopen.com/products_detail.aspx?Auno=824" }, ++ { "Artec Group", "DBE61", "http://wiki.thincan.org/DBE61" }, ++ { "Artec Group", "DBE62", "http://wiki.thincan.org/DBE62" }, ++ { "ASUS", "A7V600-X", "http://www.asus.com/product.aspx?P_ID=L2XYS0rmtCjeOr4k" }, ++ { "ASUS", "A7V8X", "http://www.asus.com/product.aspx?P_ID=qfpaGrAy2kLVo0f2" }, ++ { "ASUS", "A7V8X-MX SE", "http://www.asus.com/product.aspx?P_ID=1guVBT1qV5oqhHyZ" }, ++ { "ASUS", "P4B266", "http://www.ciao.co.uk/ASUS_Intel_845D_Chipset_P4B266__5409807#productdetail" }, ++ { "ASUS", "P4P800-E Deluxe", "http://www.asus.com/product.aspx?P_ID=INIJUvLlif7LHp3g" }, ++ { "ASUS", "P5ND2-SLI Deluxe", "http://www.asus.com/product.aspx?P_ID=WY7XroDuUImVbgp5" }, ++ { "ASUS", "P5A", "ftp://ftp.asus.com.tw/pub/ASUS/mb/sock7/ali/p5a/" }, ++ { "Biostar", "P4M80-M4", "http://www.biostar-usa.com/mbdetails.asp?model=p4m80-m4" }, ++ { "Dell", "PowerEdge 1850", "http://support.dell.com/support/edocs/systems/pe1850/en/index.htm" }, ++ { "Elitegroup", "K7VTA3", "http://www.ecs.com.tw/ECSWebSite/Products/ProductsDetail.aspx?detailid=264&CategoryID=1&DetailName=Specification&MenuID=52&LanID=0" }, ++ { "EPoX", "EP-8K5A2", "http://www.epox.com/product.asp?ID=EP-8K5A2" }, ++ { "EPoX", "EP-8RDA3+", "http://www.epox.com/product.asp?ID=EP-8RDA3plus" }, ++ { "EPoX", "EP-BX3", "http://www.epox.com/product.asp?ID=EP-BX3" }, ++ { "GIGABYTE", "GA-2761GXDK", "http://www.computerbase.de/news/hardware/mainboards/amd-systeme/2007/mai/gigabyte_dtx-mainboard/" }, ++ { "GIGABYTE", "GA-7VT600", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=1666" }, ++ { "GIGABYTE", "GA-7ZM", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=1366" }, ++ { "GIGABYTE", "GA-K8N-SLI", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=1928" }, ++ { "GIGABYTE", "GA-M57SLI-S4", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Overview.aspx?ProductID=2287&ModelName=GA-M57SLI-S4" }, ++ { "GIGABYTE", "GA-M61P-S3", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2434" }, ++ { "GIGABYTE", "GA-MA78G-DS3H", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2800" }, /* TODO: Rev 1.x or 2.x? */ ++ { "GIGABYTE", "GA-MA78GM-S2H", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2758" }, /* TODO: Rev. 1.0, 1.1, or 2.x? */ ++ { "GIGABYTE", "GA-MA790FX-DQ6", "http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2690" }, ++ { "HP", "DL145 G3", "http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c00816835&lang=en&cc=us&taskId=101&prodSeriesId=3219755&prodTypeId=15351" }, ++ { "IBM", "x3455", "http://www-03.ibm.com/systems/x/hardware/rack/x3455/index.html" }, ++ { "Intel", "D201GLY", "http://www.intel.com/support/motherboards/desktop/d201gly/index.htm" }, ++ { "IWILL", "DK8-HTX", "http://web.archive.org/web/20060507170150/http://www.iwill.net/product_2.asp?p_id=98" }, ++ { "Kontron", "986LCD-M", "http://de.kontron.com/products/boards+and+mezzanines/embedded+motherboards/miniitx+motherboards/986lcdmmitx.html" }, ++ { "Mitac", "6513WU", "http://web.archive.org/web/20050313054828/http://www.mitac.com/micweb/products/tyan/6513wu/6513wu.htm" }, ++ { "MSI", "MS-6590 (KT4 Ultra)", "http://www.msicomputer.com/product/p_spec.asp?model=KT4_Ultra&class=mb" }, ++ { "MSI", "MS-6702E (K8T Neo2-F)","http://www.msicomputer.com/product/p_spec.asp?model=K8T_Neo2-F&class=mb" }, ++ { "MSI", "MS-6712 (KT4V)", "http://www.msi.com/index.php?func=proddesc&maincat_no=1&cat2_no=&cat3_no=&prod_no=505" }, ++ { "MSI", "MS-7046", "http://www.heimir.de/ms7046/" }, ++ { "MSI", "MS-7135 (K8N Neo3)", "http://www.msi-computer.de/index.php?func=proddesc&prod_no=170&maincat_no=1" }, ++ { "Shuttle", "AK38N", "http://eu.shuttle.com/en/desktopdefault.aspx/tabid-36/558_read-9889/" }, ++ { "Soyo", "SY-7VCA", "http://www.tomshardware.com/reviews/12-socket-370-motherboards,196-15.html" }, ++ { "Tyan", "S2498 (Tomcat K7M)", "http://www.tyan.com/archive/products/html/tomcatk7m.html" }, ++ { "VIA", "EPIA-CN", "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=400" }, ++ { "VIA", "EPIA M/MII/...", "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=202" }, /* EPIA-MII link for now */ ++ { "VIA", "EPIA-N/NL", "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=221" }, /* EPIA-N link for now */ ++ { "VIA", "EPIA SP", "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=261" }, ++ { "VIA", "PC3500G", "http://www.via.com.tw/en/initiatives/empowered/pc3500_mainboard/index.jsp" }, ++ ++ /* Verified non-working boards (for now). */ ++ { "Abit", "IS-10", "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?pMODEL_NAME=IS-10&fMTYPE=Socket+478" }, ++ { "ASRock", "K7VT4A+", "http://www.asrock.com/mb/overview.asp?Model=K7VT4A%%2b&s=" }, ++ { "ASUS", "MEW-AM", "ftp://ftp.asus.com.tw/pub/ASUS/mb/sock370/810/mew-am/" }, ++ { "ASUS", "MEW-VM", "http://www.elhvb.com/mboards/OEM/HP/manual/ASUS%20MEW-VM.htm" }, ++ { "ASUS", "P3B-F", "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p3b-f/" }, ++ { "ASUS", "P5B", "ftp://ftp.asus.com.tw/pub/ASUS/mb/socket775/P5B/" }, ++ { "ASUS", "P5BV-M", "ftp://ftp.asus.com.tw/pub/ASUS/mb/socket775/P5B-VM/" }, ++ { "Biostar", "M6TBA", "ftp://ftp.biostar-usa.com/manuals/M6TBA/" }, ++ { "Boser", "HS-6637", "http://www.boser.com.tw/manual/HS-62376637v3.4.pdf" }, ++ { "DFI", "855GME-MGF", "http://www.dfi.com.tw/portal/CM/cmproduct/XX_cmproddetail/XX_WbProdsWindow?action=e&downloadType=&windowstate=normal&mode=view&downloadFlag=false&itemId=433" }, ++ { "FIC", "VA-502", "ftp://ftp.fic.com.tw/motherboard/manual/socket7/va-502/" }, ++ { "MSI", "MS-6178", "http://www.msi-technology.de/index.php?func=proddesc&prod_no=343&maincat_no=1" }, ++ { "MSI", "MS-7260 (K9N Neo)", "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=255" }, ++ { "Soyo", "SY-5VD", "http://www.soyo.com/content/Downloads/163/&c=80&p=464&l=English" }, ++ { "Sun", "Fire x4540", "http://www.sun.com/servers/x64/x4540/" }, ++ { "Sun", "Fire x4150", "http://www.sun.com/servers/x64/x4150/" }, ++ { "Sun", "Fire x4200", "http://www.sun.com/servers/entry/x4200/" }, ++ { "Sun", "Fire x4600", "http://www.sun.com/servers/x64/x4600/" }, ++ ++ /* Verified working laptops. */ ++ { "Lenovo", "3000 V100 TF05Cxx", "http://www5.pc.ibm.com/europe/products.nsf/products?openagent&brand=Lenovo3000Notebook&series=Lenovo+3000+V+Series#viewallmodelstop" }, ++ ++ /* Verified non-working laptops (for now). */ ++ { "Acer", "Aspire One", NULL }, ++ { "ASUS", "Eee PC 701 4G", "http://www.asus.com/product.aspx?P_ID=h6SPd3tEzLEsrEiS" }, ++ { "Dell", "Latitude CPi A366XT", "http://www.coreboot.org/Dell_Latitude_CPi_A366XT" }, ++ { "HP/Compaq", "nx9010", "http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?lang=en&cc=us&objectID=c00348514" }, ++ { "IBM/Lenovo", "Thinkpad T40p", "http://www.thinkwiki.org/wiki/Category:T40p" }, ++ { "IBM/Lenovo", "240", "http://www.stanford.edu/~bresnan//tp240.html" }, ++ ++ { NULL, NULL, 0 }, ++}; ++ ++/* Please keep these lists alphabetically ordered by vendor/board. */ ++const struct board_info_notes boards_notes[] = { ++ /* Verified working boards that don't need write-enables. */ ++ { "ASI", "MB-5BLMP", "Used in the IGEL WinNET III thin client." }, ++ { "ASRock", "K8S8X", "The Super I/O isn't found on this board. See http://www.flashrom.org/pipermail/flashrom/2009-November/000937.html." }, ++ { "ASUS", "A8V-E SE", "See http://www.coreboot.org/pipermail/coreboot/2007-October/026496.html." }, ++ { "ASUS", "M2A-VM", "See http://www.coreboot.org/pipermail/coreboot/2007-September/025281.html." }, ++ { "BCOM", "WinNET100", "Used in the IGEL-316 thin client." }, ++ { "GIGABYTE", "GA-7ZM", "Works fine if you remove jumper JP9 on the board and disable the flash protection BIOS option." }, ++ { "ASUS", "M2N-E", "If the machine doesn't come up again after flashing, try resetting the NVRAM(CMOS). The MAC address of the onboard network card will change to the value stored in the new image, so backup the old address first. See http://www.flashrom.org/pipermail/flashrom/2009-November/000879.html" }, ++ ++ /* Verified working boards that DO need write-enables. */ ++ { "Acer", "Aspire One", "See http://www.coreboot.org/pipermail/coreboot/2009-May/048041.html." }, ++ ++ /* Verified non-working boards (for now). */ ++ { "MSI", "MS-6178", "Immediately powers off if you try to hot-plug the chip. However, this does '''not''' happen if you use coreboot." }, ++ { "MSI", "MS-7260 (K9N Neo)", "Interestingly flashrom does not work when the vendor BIOS is booted, but it ''does'' work flawlessly when the machine is booted with coreboot." }, ++ ++ /* Verified working laptops. */ ++ /* None which need comments, yet... */ ++ ++ /* Verified non-working laptops (for now). */ ++ { "Acer", "Aspire One", "http://www.coreboot.org/pipermail/coreboot/2009-May/048041.html" }, ++ { "ASUS", "Eee PC 701 4G", "It seems the chip (25X40VSIG) is behind some SPI flash translation layer (likely in the EC, the ENE KB3310)." }, ++ { "Dell", "Latitude CPi A366XT", "The laptop immediately powers off if you try to hot-swap the chip. It's not yet tested if write/erase would work on this laptop." }, ++ { "HP/Compaq", "nx9010", "Hangs upon '''flashrom -V''' (needs hard power-cycle then)." }, ++ { "IBM/Lenovo", "Thinkpad T40p", "Seems to (partially) work at first, but one block/sector cannot be written which then leaves you with a bricked laptop. Maybe this can be investigated and fixed in software later." }, ++ ++ { NULL, NULL, 0 }, ++}; ++ ++static int url(const char *vendor, const char *board) ++{ ++ int i; ++ const struct board_info_url *b = boards_url; ++ ++ for (i = 0; b[i].vendor != NULL; i++) { ++ if (!strcmp(vendor, b[i].vendor) && !strcmp(board, b[i].name)) ++ return i; ++ } ++ ++ return -1; ++} ++ ++static int note(const char *vendor, const char *board) ++{ ++ int i; ++ const struct board_info_notes *n = boards_notes; ++ ++ for (i = 0; n[i].vendor != NULL; i++) { ++ if (!strcmp(vendor, n[i].vendor) && !strcmp(board, n[i].name)) ++ return i; ++ } ++ ++ return -1; ++} ++ ++void print_supported_chipsets_wiki(void) ++{ ++ int i, j, enablescount = 0, color = 1; ++ const struct penable *e; ++ ++ for (e = chipset_enables; e->vendor_name != NULL; e++) ++ enablescount++; ++ ++ printf("\n== Supported chipsets ==\n\nTotal amount of supported " ++ "chipsets: '''%d'''\n\n{| border=\"0\" valign=\"top\"\n| " ++ "valign=\"top\"|\n\n%s", enablescount, chipset_th); ++ ++ e = chipset_enables; ++ for (i = 0, j = 0; e[i].vendor_name != NULL; i++, j++) { ++ /* Alternate colors if the vendor changes. */ ++ if (i > 0 && strcmp(e[i].vendor_name, e[i - 1].vendor_name)) ++ color = !color; ++ ++ printf("|- bgcolor=\"#%s\" valign=\"top\"\n| %s || %s " ++ "|| %04x:%04x || %s\n", (color) ? "eeeeee" : "dddddd", ++ e[i].vendor_name, e[i].device_name, ++ e[i].vendor_id, e[i].device_id, ++ (e[i].status == OK) ? "{{OK}}" : "?"); ++ ++ /* Split table in three columns. */ ++ if (j >= (enablescount / 3 + 1)) { ++ printf("\n|}\n\n| valign=\"top\"|\n\n%s", chipset_th); ++ j = 0; ++ } ++ } ++ ++ printf("\n|}\n\n|}\n"); ++} ++ ++static void wiki_helper(const char *heading, const char *status, ++ int cols, const struct board_info boards[]) ++{ ++ int i, j, k, c, boardcount = 0, color = 1, num_notes = 0; ++ const struct board_info *b; ++ const struct board_info_url *u = boards_url; ++ char *notes = calloc(1, 1); ++ char tmp[900 + 1]; ++ ++ for (b = boards; b->vendor != NULL; b++) ++ boardcount++; ++ ++ printf("\n'''%s'''\n\nTotal amount of boards: '''%d'''\n\n" ++ "{| border=\"0\" valign=\"top\"\n| valign=\"top\"|\n\n%s", ++ heading, boardcount, board_th); ++ ++ for (i = 0, j = 0, b = boards; b[i].vendor != NULL; i++, j++) { ++ /* Alternate colors if the vendor changes. */ ++ if (i > 0 && strcmp(b[i].vendor, b[i - 1].vendor)) ++ color = !color; ++ ++ k = url(b[i].vendor, b[i].name); ++ c = note(b[i].vendor, b[i].name); ++ ++ printf("|- bgcolor=\"#%s\" valign=\"top\"\n| %s || %s%s %s%s ||" ++ " {{%s}}", (color) ? "eeeeee" : "dddddd", b[i].vendor, ++ (k != -1 && u[k].url) ? "[" : "", ++ (k != -1 && u[k].url) ? u[k].url : "", ++ b[i].name, (k != -1 && u[k].url) ? "]" : "", status); ++ ++ if (c != -1) { ++ printf("%d\n", num_notes + 1); ++ snprintf((char *)&tmp, 900, "%d %s
\n", ++ 1 + num_notes++, boards_notes[c].note); ++ notes = strcat_realloc(notes, (char *)&tmp); ++ } else { ++ printf("\n"); ++ } ++ ++ /* Split table in 'cols' columns. */ ++ if (j >= (boardcount / cols + 1)) { ++ printf("\n|}\n\n| valign=\"top\"|\n\n%s", board_th); ++ j = 0; ++ } ++ } ++ ++ printf("\n|}\n\n|}\n"); ++ ++ if (num_notes > 0) ++ printf("\n\n%s\n", notes); ++ free(notes); ++} ++ ++static void wiki_helper2(const char *heading, int cols) ++{ ++ int i, j, k, boardcount = 0, color = 1; ++ struct board_pciid_enable *b; ++ const struct board_info_url *u = boards_url; ++ ++ for (b = board_pciid_enables; b->vendor_name != NULL; b++) ++ boardcount++; ++ ++ printf("\n'''%s'''\n\nTotal amount of boards: '''%d'''\n\n" ++ "{| border=\"0\" valign=\"top\"\n| valign=\"top\"|\n\n%s", ++ heading, boardcount, board_th2); ++ ++ b = board_pciid_enables; ++ for (i = 0, j = 0; b[i].vendor_name != NULL; i++, j++) { ++ /* Alternate colors if the vendor changes. */ ++ if (i > 0 && strcmp(b[i].vendor_name, b[i - 1].vendor_name)) ++ color = !color; ++ ++ k = url(b[i].vendor_name, b[i].board_name); ++ ++ printf("|- bgcolor=\"#%s\" valign=\"top\"\n| %s || %s%s %s%s " ++ "|| %s%s%s%s || {{OK}}\n", (color) ? "eeeeee" : "dddddd", ++ b[i].vendor_name, (k != -1 && u[k].url) ? "[" : "", ++ (k != -1 && u[k].url) ? u[k].url : "", b[i].board_name, ++ (k != -1 && u[k].url) ? "]" : "", ++ (b[i].lb_vendor) ? "-m " : "—", ++ (b[i].lb_vendor) ? b[i].lb_vendor : "", ++ (b[i].lb_vendor) ? ":" : "", ++ (b[i].lb_vendor) ? b[i].lb_part : ""); ++ ++ /* Split table in three columns. */ ++ if (j >= (boardcount / cols + 1)) { ++ printf("\n|}\n\n| valign=\"top\"|\n\n%s", board_th2); ++ j = 0; ++ } ++ } ++ ++ printf("\n|}\n\n|}\n"); ++} ++ ++void print_supported_boards_wiki(void) ++{ ++ printf("%s", board_intro); ++ wiki_helper("Known good (worked out of the box)", "OK", 3, boards_ok); ++ wiki_helper2("Known good (with write-enable code in flashrom)", 3); ++ wiki_helper("Not supported (yet)", "No", 3, boards_bad); ++ ++ printf("%s", laptop_intro); ++ wiki_helper("Known good (worked out of the box)", "OK", 1, laptops_ok); ++ wiki_helper("Not supported (yet)", "No", 1, laptops_bad); ++} ++ ++void print_supported_chips_wiki(void) ++{ ++ int i = 0, c = 1, chipcount = 0; ++ struct flashchip *f, *old = NULL; ++ uint32_t t; ++ ++ for (f = flashchips; f->name != NULL; f++) ++ chipcount++; ++ ++ printf("\n== Supported chips ==\n\nTotal amount of supported " ++ "chips: '''%d'''\n\n{| border=\"0\" valign=\"top\"\n" ++ "| valign=\"top\"|\n\n%s", chipcount, chip_th); ++ ++ for (f = flashchips; f->name != NULL; f++, i++) { ++ /* Don't print "unknown XXXX SPI chip" entries. */ ++ if (!strncmp(f->name, "unknown", 7)) ++ continue; ++ ++ /* Alternate colors if the vendor changes. */ ++ if (old != NULL && strcmp(old->vendor, f->vendor)) ++ c = !c; ++ ++ t = f->tested; ++ printf("|- bgcolor=\"#%s\" valign=\"top\"\n| %s || %s || %d " ++ "|| %s || {{%s}} || {{%s}} || {{%s}} || {{%s}}\n", ++ (c == 1) ? "eeeeee" : "dddddd", f->vendor, f->name, ++ f->total_size, flashbuses_to_text(f->bustype), ++ (t & TEST_OK_PROBE) ? "OK" : ++ (t & TEST_BAD_PROBE) ? "No" : ((c) ? "?2" : "?"), ++ (t & TEST_OK_READ) ? "OK" : ++ (t & TEST_BAD_READ) ? "No" : ((c) ? "?2" : "?"), ++ (t & TEST_OK_ERASE) ? "OK" : ++ (t & TEST_BAD_ERASE) ? "No" : ((c) ? "?2" : "?"), ++ (t & TEST_OK_WRITE) ? "OK" : ++ (t & TEST_BAD_WRITE) ? "No" : ((c) ? "?2" : "?")); ++ ++ /* Split table into three columns. */ ++ if (i >= (chipcount / 3 + 1)) { ++ printf("\n|}\n\n| valign=\"top\"|\n\n%s", chip_th); ++ i = 0; ++ } ++ ++ old = f; ++ } ++ ++ printf("\n|}\n\n|}\n"); ++} ++ ++void print_supported_pcidevs_wiki(struct pcidev_status *devs) ++{ ++ int i = 0; ++ static int c = 0; ++ ++ /* Alternate colors if the vendor changes. */ ++ c = !c; ++ ++ for (i = 0; devs[i].vendor_name != NULL; i++) { ++ printf("|- bgcolor=\"#%s\" valign=\"top\"\n| %s || %s || " ++ "%04x:%04x || {{%s}}\n", (c) ? "eeeeee" : "dddddd", ++ devs[i].vendor_name, devs[i].device_name, ++ devs[i].vendor_id, devs[i].device_id, ++ (devs[i].status == PCI_NT) ? (c) ? "?2" : "?" : "OK"); ++ } ++} ++ ++void print_supported_wiki(void) ++{ ++ time_t t = time(NULL); ++ ++ printf(wiki_header, ctime(&t), flashrom_version); ++ print_supported_chips_wiki(); ++ print_supported_chipsets_wiki(); ++ print_supported_boards_wiki(); ++ printf("%s", programmer_section); ++#if NIC3COM_SUPPORT == 1 ++ print_supported_pcidevs_wiki(nics_3com); ++#endif ++#if GFXNVIDIA_SUPPORT == 1 ++ print_supported_pcidevs_wiki(gfx_nvidia); ++#endif ++#if DRKAISER_SUPPORT == 1 ++ print_supported_pcidevs_wiki(drkaiser_pcidev); ++#endif ++#if SATASII_SUPPORT == 1 ++ print_supported_pcidevs_wiki(satas_sii); ++#endif ++ printf("\n|}\n"); ++} ++ +diff --git a/programmer.c b/programmer.c +new file mode 100644 +index 0000000..ca641e1 +--- /dev/null ++++ b/programmer.c +@@ -0,0 +1,99 @@ ++/* ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2009,2010 Carl-Daniel Hailfinger ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include "flash.h" ++ ++/* No-op shutdown() for programmers which don't need special handling */ ++int noop_shutdown(void) ++{ ++ return 0; ++} ++ ++/* Fallback map() for programmers which don't need special handling */ ++void *fallback_map(const char *descr, unsigned long phys_addr, size_t len) ++{ ++ /* FIXME: Should return phys_addr. */ ++ return 0; ++} ++ ++/* No-op/fallback unmap() for programmers which don't need special handling */ ++void fallback_unmap(void *virt_addr, size_t len) ++{ ++} ++ ++/* No-op chip_writeb() for drivers not supporting addr/data pair accesses */ ++uint8_t noop_chip_readb(const chipaddr addr) ++{ ++ return 0xff; ++} ++ ++/* No-op chip_writeb() for drivers not supporting addr/data pair accesses */ ++void noop_chip_writeb(uint8_t val, chipaddr addr) ++{ ++} ++ ++/* Little-endian fallback for drivers not supporting 16 bit accesses */ ++void fallback_chip_writew(uint16_t val, chipaddr addr) ++{ ++ chip_writeb(val & 0xff, addr); ++ chip_writeb((val >> 8) & 0xff, addr + 1); ++} ++ ++/* Little-endian fallback for drivers not supporting 16 bit accesses */ ++uint16_t fallback_chip_readw(const chipaddr addr) ++{ ++ uint16_t val; ++ val = chip_readb(addr); ++ val |= chip_readb(addr + 1) << 8; ++ return val; ++} ++ ++/* Little-endian fallback for drivers not supporting 32 bit accesses */ ++void fallback_chip_writel(uint32_t val, chipaddr addr) ++{ ++ chip_writew(val & 0xffff, addr); ++ chip_writew((val >> 16) & 0xffff, addr + 2); ++} ++ ++/* Little-endian fallback for drivers not supporting 32 bit accesses */ ++uint32_t fallback_chip_readl(const chipaddr addr) ++{ ++ uint32_t val; ++ val = chip_readw(addr); ++ val |= chip_readw(addr + 2) << 16; ++ return val; ++} ++ ++void fallback_chip_writen(uint8_t *buf, chipaddr addr, size_t len) ++{ ++ size_t i; ++ for (i = 0; i < len; i++) ++ chip_writeb(buf[i], addr + i); ++ return; ++} ++ ++void fallback_chip_readn(uint8_t *buf, chipaddr addr, size_t len) ++{ ++ size_t i; ++ for (i = 0; i < len; i++) ++ buf[i] = chip_readb(addr + i); ++ return; ++} +diff --git a/satasii.c b/satasii.c +index 2564436..90995c9 100644 +--- a/satasii.c ++++ b/satasii.c +@@ -47,7 +47,8 @@ int satasii_init(void) + + get_io_perms(); + +- pcidev_init(PCI_VENDOR_ID_SII, satas_sii, programmer_param); ++ pcidev_init(PCI_VENDOR_ID_SII, PCI_BASE_ADDRESS_0, satas_sii, ++ programmer_param); + id = pcidev_dev->device_id; + + if ((id == 0x3132) || (id == 0x3124)) { +@@ -62,7 +63,7 @@ int satasii_init(void) + + /* Check if ROM cycle are OK. */ + if ((id != 0x0680) && (!(mmio_readl(sii_bar) & (1 << 26)))) +- printf("Warning: Flash seems unconnected.\n"); ++ msg_pinfo("Warning: Flash seems unconnected.\n"); + + buses_supported = CHIP_BUSTYPE_PARALLEL; + +diff --git a/sb600spi.c b/sb600spi.c +index c853f42..9172eac 100644 +--- a/sb600spi.c ++++ b/sb600spi.c +@@ -48,30 +48,35 @@ int sb600_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len) + /* FIXME: SB600 can write 5 bytes per transaction. */ + int sb600_spi_write_1(struct flashchip *flash, uint8_t *buf) + { +- int rc = 0, i; ++ int i; + int total_size = flash->total_size * 1024; +- int result; ++ int result = 0; + + spi_disable_blockprotect(); + /* Erase first */ +- printf("Erasing flash before programming... "); +- if (flash->erase(flash)) { +- fprintf(stderr, "ERASE FAILED!\n"); ++ msg_pinfo("Erasing flash before programming... "); ++ if (erase_flash(flash)) { ++ msg_perr("ERASE FAILED!\n"); + return -1; + } +- printf("done.\n"); ++ msg_pinfo("done.\n"); + +- printf("Programming flash"); ++ msg_pinfo("Programming flash"); + for (i = 0; i < total_size; i++, buf++) { +- result = spi_byte_program(i, *buf); ++ result = spi_nbyte_program(i, buf, 1); ++ if (result) { ++ msg_perr("Write error!\n"); ++ return result; ++ } ++ + /* wait program complete. */ + if (i % 0x8000 == 0) +- printf("."); ++ msg_pspew("."); + while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP) + ; + } +- printf(" done.\n"); +- return rc; ++ msg_pinfo(" done.\n"); ++ return result; + } + + static void reset_internal_fifo_pointer(void) +@@ -79,7 +84,7 @@ static void reset_internal_fifo_pointer(void) + mmio_writeb(mmio_readb(sb600_spibar + 2) | 0x10, sb600_spibar + 2); + + while (mmio_readb(sb600_spibar + 0xD) & 0x7) +- printf("reset\n"); ++ msg_pspew("reset\n"); + } + + static void execute_command(void) +@@ -100,17 +105,17 @@ int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt, + + writecnt--; + +- printf_debug("%s, cmd=%x, writecnt=%x, readcnt=%x\n", +- __func__, cmd, writecnt, readcnt); ++ msg_pspew("%s, cmd=%x, writecnt=%x, readcnt=%x\n", ++ __func__, cmd, writecnt, readcnt); + + if (readcnt > 8) { +- printf("%s, SB600 SPI controller can not receive %d bytes, " ++ msg_pinfo("%s, SB600 SPI controller can not receive %d bytes, " + "it is limited to 8 bytes\n", __func__, readcnt); + return SPI_INVALID_LENGTH; + } + + if (writecnt > 8) { +- printf("%s, SB600 SPI controller can not send %d bytes, " ++ msg_pinfo("%s, SB600 SPI controller can not send %d bytes, " + "it is limited to 8 bytes\n", __func__, writecnt); + return SPI_INVALID_LENGTH; + } +@@ -130,10 +135,10 @@ int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt, + + /* Send the write byte to FIFO. */ + for (count = 0; count < writecnt; count++, writearr++) { +- printf_debug(" [%x]", *writearr); ++ msg_pspew(" [%x]", *writearr); + mmio_writeb(*writearr, sb600_spibar + 0xC); + } +- printf_debug("\n"); ++ msg_pspew("\n"); + + /* + * We should send the data by sequence, which means we need to reset +@@ -159,16 +164,16 @@ int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt, + /* Skip the bytes we sent. */ + for (count = 0; count < writecnt; count++) { + cmd = mmio_readb(sb600_spibar + 0xC); +- printf_debug("[ %2x]", cmd); ++ msg_pspew("[ %2x]", cmd); + } + +- printf_debug("The FIFO pointer after skipping is %d.\n", +- mmio_readb(sb600_spibar + 0xd) & 0x07); ++ msg_pspew("The FIFO pointer after skipping is %d.\n", ++ mmio_readb(sb600_spibar + 0xd) & 0x07); + for (count = 0; count < readcnt; count++, readarr++) { + *readarr = mmio_readb(sb600_spibar + 0xC); +- printf_debug("[%02x]", *readarr); ++ msg_pspew("[%02x]", *readarr); + } +- printf_debug("\n"); ++ msg_pspew("\n"); + + return 0; + } +diff --git a/serial.c b/serial.c +new file mode 100644 +index 0000000..4d49e08 +--- /dev/null ++++ b/serial.c +@@ -0,0 +1,231 @@ ++/* ++ * This file is part of the flashrom project. ++ * ++ * Copyright (C) 2009 Urja Rannikko ++ * Copyright (C) 2009,2010 Carl-Daniel Hailfinger ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include "flash.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef _WIN32 ++#include ++#else ++#include ++#endif ++ ++fdtype sp_fd; ++ ++void __attribute__((noreturn)) sp_die(char *msg) ++{ ++ perror(msg); ++ exit(1); ++} ++ ++#ifndef _WIN32 ++struct baudentry { ++ int flag; ++ unsigned int baud; ++}; ++ ++/* I'd like if the C preprocessor could have directives in macros */ ++#define BAUDENTRY(baud) { B##baud, baud }, ++static const struct baudentry sp_baudtable[] = { ++ BAUDENTRY(9600) ++ BAUDENTRY(19200) ++ BAUDENTRY(38400) ++ BAUDENTRY(57600) ++ BAUDENTRY(115200) ++#ifdef B230400 ++ BAUDENTRY(230400) ++#endif ++#ifdef B460800 ++ BAUDENTRY(460800) ++#endif ++#ifdef B500000 ++ BAUDENTRY(500000) ++#endif ++#ifdef B576000 ++ BAUDENTRY(576000) ++#endif ++#ifdef B921600 ++ BAUDENTRY(921600) ++#endif ++#ifdef B1000000 ++ BAUDENTRY(1000000) ++#endif ++#ifdef B1152000 ++ BAUDENTRY(1152000) ++#endif ++#ifdef B1500000 ++ BAUDENTRY(1500000) ++#endif ++#ifdef B2000000 ++ BAUDENTRY(2000000) ++#endif ++#ifdef B2500000 ++ BAUDENTRY(2500000) ++#endif ++#ifdef B3000000 ++ BAUDENTRY(3000000) ++#endif ++#ifdef B3500000 ++ BAUDENTRY(3500000) ++#endif ++#ifdef B4000000 ++ BAUDENTRY(4000000) ++#endif ++ {0, 0} /* Terminator */ ++}; ++#endif ++ ++fdtype sp_openserport(char *dev, unsigned int baud) ++{ ++#ifdef _WIN32 ++ HANDLE fd; ++ char* dev2 = dev; ++ if ((strlen(dev) > 3) && (tolower(dev[0])=='c') && (tolower(dev[1])=='o') && (tolower(dev[2])=='m')) { ++ dev2 = malloc(strlen(dev)+5); ++ strcpy(dev2, "\\\\.\\"); ++ strcpy(dev2+4, dev); ++ } ++ fd = CreateFile(dev2, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); ++ if (dev2 != dev) ++ free(dev2); ++ if (fd == INVALID_HANDLE_VALUE) { ++ sp_die("Error: cannot open serial port"); ++ } ++ DCB dcb; ++ if (!GetCommState(fd, &dcb)) { ++ sp_die("Error: Could not fetch serial port configuration"); ++ } ++ switch (baud) { ++ case 9600: dcb.BaudRate = CBR_9600; break; ++ case 19200: dcb.BaudRate = CBR_19200; break; ++ case 38400: dcb.BaudRate = CBR_38400; break; ++ case 57600: dcb.BaudRate = CBR_57600; break; ++ case 115200: dcb.BaudRate = CBR_115200; break; ++ default: sp_die("Error: Could not set baud rate"); ++ } ++ dcb.ByteSize=8; ++ dcb.Parity=NOPARITY; ++ dcb.StopBits=ONESTOPBIT; ++ if (!SetCommState(fd, &dcb)) { ++ sp_die("Error: Could not change serial port configuration"); ++ } ++ return fd; ++#else ++ struct termios options; ++ int fd, i; ++ fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY); ++ if (fd < 0) ++ sp_die("Error: cannot open serial port"); ++ fcntl(fd, F_SETFL, 0); ++ tcgetattr(fd, &options); ++ for (i = 0;; i++) { ++ if (sp_baudtable[i].baud == 0) { ++ close(fd); ++ msg_perr( ++ "Error: cannot configure for baudrate %d\n", ++ baud); ++ exit(1); ++ } ++ if (sp_baudtable[i].baud == baud) { ++ cfsetispeed(&options, sp_baudtable[i].flag); ++ cfsetospeed(&options, sp_baudtable[i].flag); ++ break; ++ } ++ } ++ options.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS); ++ options.c_cflag |= (CS8 | CLOCAL | CREAD); ++ options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); ++ options.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL | IGNCR | INLCR); ++ options.c_oflag &= ~OPOST; ++ tcsetattr(fd, TCSANOW, &options); ++ return fd; ++#endif ++} ++ ++void sp_flush_incoming(void) ++{ ++#ifdef _WIN32 ++ PurgeComm(sp_fd, PURGE_RXCLEAR); ++#else ++ tcflush(sp_fd, TCIFLUSH); ++#endif ++ return; ++} ++ ++int serialport_shutdown(void) ++{ ++#ifdef _WIN32 ++ CloseHandle(sp_fd); ++#else ++ close(sp_fd); ++#endif ++ return 0; ++} ++ ++int serialport_write(unsigned char *buf, unsigned int writecnt) ++{ ++ long tmp = 0; ++ ++ while (writecnt > 0) { ++#ifdef _WIN32 ++ WriteFile(sp_fd, buf, writecnt, &tmp, NULL); ++#else ++ tmp = write(sp_fd, buf, writecnt); ++#endif ++ if (tmp == -1) ++ return 1; ++ if (!tmp) ++ msg_pdbg("Empty write\n"); ++ writecnt -= tmp; ++ buf += tmp; ++ } ++ ++ return 0; ++} ++ ++int serialport_read(unsigned char *buf, unsigned int readcnt) ++{ ++ long tmp = 0; ++ ++ while (readcnt > 0) { ++#ifdef _WIN32 ++ ReadFile(sp_fd, buf, readcnt, &tmp, NULL); ++#else ++ tmp = read(sp_fd, buf, readcnt); ++#endif ++ if (tmp == -1) ++ return 1; ++ if (!tmp) ++ msg_pdbg("Empty read\n"); ++ readcnt -= tmp; ++ buf += tmp; ++ } ++ ++ return 0; ++} +diff --git a/serprog.c b/serprog.c +index 23e1a0c..0035e9a 100644 +--- a/serprog.c ++++ b/serprog.c +@@ -61,8 +61,6 @@ + #define S_CMD_Q_RDNMAXLEN 0x11 /* Query read-n maximum length */ + #define S_CMD_S_BUSTYPE 0x12 /* Set used bustype(s). */ + +-static int sp_fd; +- + static uint16_t sp_device_serbuf_size = 16; + static uint16_t sp_device_opbuf_size = 300; + /* Bitmap of supported commands */ +@@ -95,19 +93,13 @@ static int sp_opbuf_usage = 0; + whether the command is supported before doing it */ + static int sp_check_avail_automatic = 0; + +-static void sp_die(char *msg) +-{ +- perror(msg); +- exit(1); +-} +- + static int sp_opensocket(char *ip, unsigned int port) + { + int flag = 1; + struct hostent *hostPtr = NULL; +- struct sockaddr_in sp; ++ union { struct sockaddr_in si; struct sockaddr s; } sp = {}; + int sock; +- printf_debug(MSGHEADER "IP %s port %d\n", ip, port); ++ msg_pdbg(MSGHEADER "IP %s port %d\n", ip, port); + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock < 0) + sp_die("Error: serprog cannot open socket"); +@@ -117,11 +109,10 @@ static int sp_opensocket(char *ip, unsigned int port) + if (NULL == hostPtr) + sp_die("Error: cannot resolve"); + } +- memset(&sp, 0, sizeof(sp)); +- sp.sin_family = AF_INET; +- sp.sin_port = htons(port); +- (void)memcpy(&sp.sin_addr, hostPtr->h_addr, hostPtr->h_length); +- if (connect(sock, (struct sockaddr *)&sp, sizeof(sp)) < 0) { ++ sp.si.sin_family = AF_INET; ++ sp.si.sin_port = htons(port); ++ (void)memcpy(&sp.si.sin_addr, hostPtr->h_addr, hostPtr->h_length); ++ if (connect(sock, &sp.s, sizeof(sp.si)) < 0) { + close(sock); + sp_die("Error: serprog cannot connect"); + } +@@ -131,112 +122,6 @@ static int sp_opensocket(char *ip, unsigned int port) + return sock; + } + +-struct baudentry { +- int flag; +- unsigned int baud; +-}; +- +-/* I'd like if the C preprocessor could have directives in macros */ +-#define BAUDENTRY(baud) { B##baud, baud }, +-static const struct baudentry sp_baudtable[] = { +- BAUDENTRY(9600) +- BAUDENTRY(19200) +- BAUDENTRY(38400) +- BAUDENTRY(57600) +- BAUDENTRY(115200) +-#ifdef B230400 +- BAUDENTRY(230400) +-#endif +-#ifdef B460800 +- BAUDENTRY(460800) +-#endif +-#ifdef B500000 +- BAUDENTRY(500000) +-#endif +-#ifdef B576000 +- BAUDENTRY(576000) +-#endif +-#ifdef B921600 +- BAUDENTRY(921600) +-#endif +-#ifdef B1000000 +- BAUDENTRY(1000000) +-#endif +-#ifdef B1152000 +- BAUDENTRY(1152000) +-#endif +-#ifdef B1500000 +- BAUDENTRY(1500000) +-#endif +-#ifdef B2000000 +- BAUDENTRY(2000000) +-#endif +-#ifdef B2500000 +- BAUDENTRY(2500000) +-#endif +-#ifdef B3000000 +- BAUDENTRY(3000000) +-#endif +-#ifdef B3500000 +- BAUDENTRY(3500000) +-#endif +-#ifdef B4000000 +- BAUDENTRY(4000000) +-#endif +- {0, 0} /* Terminator */ +-}; +- +-static int sp_openserport(char *dev, unsigned int baud) +-{ +- struct termios options; +- int fd, i; +- fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY); +- if (fd < 0) +- sp_die("Error: cannot open serial port"); +- fcntl(fd, F_SETFL, 0); +- tcgetattr(fd, &options); +- for (i = 0;; i++) { +- if (sp_baudtable[i].baud == 0) { +- close(fd); +- fprintf(stderr, +- "Error: cannot configure for baudrate %d\n", +- baud); +- exit(1); +- } +- if (sp_baudtable[i].baud == baud) { +- cfsetispeed(&options, sp_baudtable[i].flag); +- cfsetospeed(&options, sp_baudtable[i].flag); +- break; +- } +- } +- options.c_cflag &= ~PARENB; +- options.c_cflag &= ~CSTOPB; +- options.c_cflag &= ~CSIZE; +- options.c_cflag |= CS8; +- options.c_cflag &= ~CRTSCTS; +- options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); +- options.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL | IGNCR | INLCR); +- options.c_oflag &= ~OPOST; +- options.c_cflag |= (CLOCAL | CREAD); +- tcsetattr(fd, TCSANOW, &options); +- return fd; +-} +- +-static void sp_flush_incoming(void) +-{ +- int i; +- for (i=0;i<100;i++) { /* In case the device doesnt do EAGAIN, just read 0 */ +- unsigned char flush[16]; +- ssize_t rv; +- rv = read(sp_fd, flush, sizeof(flush)); +- if ((rv == -1) && (errno == EAGAIN)) +- break; +- if (rv == -1) +- sp_die("flush read"); +- } +- return; +-} +- + static int sp_sync_read_timeout(int loops) + { + int i; +@@ -285,7 +170,7 @@ static void sp_synchronize(void) + unsigned char c = S_CMD_SYNCNOP; + if (write(sp_fd, &c, 1) != 1) + sp_die("sync write"); +- printf_debug("."); ++ msg_pdbg("."); + fflush(stdout); + for (n = 0; n < 10; n++) { + c = sp_sync_read_timeout(5); /* wait upto 50ms */ +@@ -306,12 +191,11 @@ static void sp_synchronize(void) + /* Ok, synchronized; back to blocking reads and return. */ + flags &= ~O_NONBLOCK; + fcntl(sp_fd, F_SETFL, flags); +- printf_debug("\n"); ++ msg_pdbg("\n"); + return; + } + } +- fprintf(stderr, +- "Error: cannot synchronize protocol\n" ++ msg_perr("Error: cannot synchronize protocol\n" + "- check communications and reset device?\n"); + exit(1); + } +@@ -327,7 +211,7 @@ static int sp_check_commandavail(uint8_t command) + static int sp_automatic_cmdcheck(uint8_t cmd) + { + if ((sp_check_avail_automatic) && (sp_check_commandavail(cmd) == 0)) { +- printf_debug ("Warning: Automatic command availability check" ++ msg_pdbg("Warning: Automatic command availability check" + " failed for cmd %d - wont execute cmd\n",cmd); + return 1; + } +@@ -354,8 +238,7 @@ static int sp_docommand(uint8_t command, uint32_t parmlen, + sp_die("Error: cannot read from device"); + if (c == S_NAK) return 1; + if (c != S_ACK) { +- fprintf(stderr, +- "Error: invalid response 0x%02X from device\n",c); ++ msg_perr("Error: invalid response 0x%02X from device\n",c); + exit(1); + } + if (retlen) { +@@ -378,18 +261,14 @@ static void sp_flush_stream(void) + do { + unsigned char c; + if (read(sp_fd, &c, 1) != 1) { +- sp_die +- ("Error: cannot read from device (flushing stream)"); ++ sp_die("Error: cannot read from device (flushing stream)"); + } + if (c == S_NAK) { +- fprintf(stderr, +- "Error: NAK to a stream buffer operation\n"); ++ msg_perr("Error: NAK to a stream buffer operation\n"); + exit(1); + } + if (c != S_ACK) { +- fprintf(stderr, +- "Error: Invalid reply 0x%02X from device\n", +- c); ++ msg_perr("Error: Invalid reply 0x%02X from device\n", c); + exit(1); + } + } while (--sp_streamed_transmit_ops); +@@ -425,12 +304,11 @@ int serprog_init(void) + unsigned char c; + char *num; + char *dev; +- printf_debug("%s\n", __func__); ++ msg_pspew("%s\n", __func__); + /* the parameter is either of format "/dev/device:baud" or "ip:port" */ + if ((!programmer_param) || (!strlen(programmer_param))) { + nodevice: +- fprintf(stderr, +- "Error: No device/host given for the serial programmer driver.\n" ++ msg_perr("Error: No device/host given for the serial programmer driver.\n" + "Use flashrom -p serprog=/dev/device:baud or flashrom -p serprog=ip:port\n"); + exit(1); + } +@@ -438,8 +316,7 @@ int serprog_init(void) + len = num - programmer_param; + if (!len) goto nodevice; + if (!num) { +- fprintf(stderr, +- "Error: No port or baudrate specified to serial programmer driver.\n" ++ msg_perr("Error: No port or baudrate specified to serial programmer driver.\n" + "Use flashrom -p serprog=/dev/device:baud or flashrom -p serprog=ip:port\n"); + exit(1); + } +@@ -459,28 +336,28 @@ int serprog_init(void) + free(dev); dev = NULL; + free(num); num = NULL; + +- printf_debug(MSGHEADER "connected - attempting to synchronize\n"); ++ msg_pdbg(MSGHEADER "connected - attempting to synchronize\n"); + + sp_check_avail_automatic = 0; + + sp_synchronize(); + +- printf_debug(MSGHEADER "Synchronized\n"); ++ msg_pdbg(MSGHEADER "Synchronized\n"); + + if (sp_docommand(S_CMD_Q_IFACE, 0, NULL, 2, &iface)) { +- fprintf(stderr, "Error: NAK to Query Interface version\n"); ++ msg_perr("Error: NAK to Query Interface version\n"); + exit(1); + } + + if (iface != 1) { +- fprintf(stderr, "Error: Unknown interface version %d\n", iface); ++ msg_perr("Error: Unknown interface version %d\n", iface); + exit(1); + } + +- printf_debug(MSGHEADER "Interface version ok.\n"); ++ msg_pdbg(MSGHEADER "Interface version ok.\n"); + + if (sp_docommand(S_CMD_Q_CMDMAP, 0, NULL, 32, sp_cmdmap)) { +- fprintf(stderr, "Error: query command map not supported\n"); ++ msg_perr("Error: query command map not supported\n"); + exit(1); + } + +@@ -488,81 +365,77 @@ int serprog_init(void) + + /* Check for the minimum operational set of commands */ + if (sp_check_commandavail(S_CMD_R_BYTE) == 0) { +- fprintf(stderr, "Error: Single byte read not supported\n"); ++ msg_perr("Error: Single byte read not supported\n"); + exit(1); + } + /* This could be translated to single byte reads (if missing), * + * but now we dont support that. */ + if (sp_check_commandavail(S_CMD_R_NBYTES) == 0) { +- fprintf(stderr, "Error: Read n bytes not supported\n"); ++ msg_perr("Error: Read n bytes not supported\n"); + exit(1); + } + /* In the future one could switch to read-only mode if these * + * are not available. */ + if (sp_check_commandavail(S_CMD_O_INIT) == 0) { +- fprintf(stderr, +- "Error: Initialize operation buffer not supported\n"); ++ msg_perr("Error: Initialize operation buffer not supported\n"); + exit(1); + } + if (sp_check_commandavail(S_CMD_O_WRITEB) == 0) { +- fprintf(stderr, +- "Error: Write to opbuf: write byte not supported\n"); ++ msg_perr("Error: Write to opbuf: write byte not supported\n"); + exit(1); + } + if (sp_check_commandavail(S_CMD_O_DELAY) == 0) { +- fprintf(stderr, "Error: Write to opbuf: delay not supported\n"); ++ msg_perr("Error: Write to opbuf: delay not supported\n"); + exit(1); + } + if (sp_check_commandavail(S_CMD_O_EXEC) == 0) { +- fprintf(stderr, ++ msg_perr( + "Error: Execute operation buffer not supported\n"); + exit(1); + } + + if (sp_docommand(S_CMD_Q_PGMNAME, 0, NULL, 16, pgmname)) { +- fprintf(stderr, "Warning: NAK to query programmer name\n"); ++ msg_perr("Warning: NAK to query programmer name\n"); + strcpy((char *)pgmname, "(unknown)"); + } + pgmname[16] = 0; +- printf(MSGHEADER "Programmer name \"%s\"\n", pgmname); ++ msg_pinfo(MSGHEADER "Programmer name \"%s\"\n", pgmname); + + if (sp_docommand(S_CMD_Q_SERBUF, 0, NULL, 2, &sp_device_serbuf_size)) { +- fprintf(stderr, "Warning: NAK to query serial buffer size\n"); ++ msg_perr("Warning: NAK to query serial buffer size\n"); + } +- printf_debug(MSGHEADER "serial buffer size %d\n", ++ msg_pdbg(MSGHEADER "serial buffer size %d\n", + sp_device_serbuf_size); + + if (sp_docommand(S_CMD_Q_OPBUF, 0, NULL, 2, &sp_device_opbuf_size)) { +- fprintf(stderr, +- "Warning: NAK to query operation buffer size\n"); ++ msg_perr("Warning: NAK to query operation buffer size\n"); + } +- printf_debug(MSGHEADER "operation buffer size %d\n", ++ msg_pdbg(MSGHEADER "operation buffer size %d\n", + sp_device_opbuf_size); + + if (sp_docommand(S_CMD_Q_BUSTYPE, 0, NULL, 1, &c)) { +- fprintf(stderr, "Warning: NAK to query supported buses\n"); ++ msg_perr("Warning: NAK to query supported buses\n"); + c = CHIP_BUSTYPE_NONSPI; /* A reasonable default for now. */ + } + buses_supported = c; + + if (sp_docommand(S_CMD_O_INIT, 0, NULL, 0, NULL)) { +- fprintf(stderr, "Error: NAK to initialize operation buffer\n"); ++ msg_perr("Error: NAK to initialize operation buffer\n"); + exit(1); + } + + if (sp_docommand(S_CMD_Q_WRNMAXLEN, 0, NULL, 3, rbuf)) { +- printf_debug(MSGHEADER "Write-n not supported"); ++ msg_pdbg(MSGHEADER "Write-n not supported"); + sp_max_write_n = 0; + } else { + sp_max_write_n = ((unsigned int)(rbuf[0]) << 0); + sp_max_write_n |= ((unsigned int)(rbuf[1]) << 8); + sp_max_write_n |= ((unsigned int)(rbuf[2]) << 16); +- printf_debug(MSGHEADER "Maximum write-n length %d\n", ++ msg_pdbg(MSGHEADER "Maximum write-n length %d\n", + sp_max_write_n); + sp_write_n_buf = malloc(sp_max_write_n); + if (!sp_write_n_buf) { +- fprintf(stderr, +- "Error: cannot allocate memory for Write-n buffer\n"); ++ msg_perr("Error: cannot allocate memory for Write-n buffer\n"); + exit(1); + } + sp_write_n_bytes = 0; +@@ -573,10 +446,10 @@ int serprog_init(void) + sp_max_read_n = ((unsigned int)(rbuf[0]) << 0); + sp_max_read_n |= ((unsigned int)(rbuf[1]) << 8); + sp_max_read_n |= ((unsigned int)(rbuf[2]) << 16); +- printf_debug(MSGHEADER "Maximum read-n length %d\n", ++ msg_pdbg(MSGHEADER "Maximum read-n length %d\n", + sp_max_read_n ? sp_max_read_n : (1<<24)); + } else { +- printf_debug(MSGHEADER "Maximum read-n length not reported\n"); ++ msg_pdbg(MSGHEADER "Maximum read-n length not reported\n"); + sp_max_read_n = 0; + } + +@@ -592,7 +465,7 @@ int serprog_init(void) + static void sp_pass_writen(void) + { + unsigned char header[7]; +- printf_debug(MSGHEADER "Passing write-n bytes=%d addr=0x%x\n", ++ msg_pspew(MSGHEADER "Passing write-n bytes=%d addr=0x%x\n", + sp_write_n_bytes, sp_write_n_addr); + if (sp_streamed_transmit_bytes >= + (7 + sp_write_n_bytes + sp_device_serbuf_size)) +@@ -632,7 +505,7 @@ static void sp_execute_opbuf_noflush(void) + if ((sp_max_write_n) && (sp_write_n_bytes)) + sp_pass_writen(); + sp_stream_buffer_op(S_CMD_O_EXEC, 0, 0); +- printf_debug(MSGHEADER "Executed operation buffer of %d bytes\n", ++ msg_pspew(MSGHEADER "Executed operation buffer of %d bytes\n", + sp_opbuf_usage); + sp_opbuf_usage = 0; + sp_prev_was_write = 0; +@@ -647,7 +520,7 @@ static void sp_execute_opbuf(void) + + int serprog_shutdown(void) + { +- printf_debug("%s\n", __func__); ++ msg_pspew("%s\n", __func__); + if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes)) + sp_execute_opbuf(); + close(sp_fd); +@@ -662,14 +535,13 @@ static void sp_check_opbuf_usage(int bytes_to_be_added) + sp_execute_opbuf(); + /* If this happens in the mid of an page load the page load * + * will propably fail. */ +- printf_debug(MSGHEADER +- "Warning: executed operation buffer due to size reasons\n"); ++ msg_pdbg(MSGHEADER "Warning: executed operation buffer due to size reasons\n"); + } + } + + void serprog_chip_writeb(uint8_t val, chipaddr addr) + { +- printf_debug("%s\n", __func__); ++ msg_pspew("%s\n", __func__); + if (sp_max_write_n) { + if ((sp_prev_was_write) + && (addr == (sp_write_n_addr + sp_write_n_bytes))) { +@@ -713,7 +585,7 @@ uint8_t serprog_chip_readb(const chipaddr addr) + sp_flush_stream(); + if (read(sp_fd, &c, 1) != 1) + sp_die("readb byteread"); +- printf_debug("%s addr=0x%lx returning 0x%02X\n", __func__, addr, c); ++ msg_pspew("%s addr=0x%lx returning 0x%02X\n", __func__, addr, c); + return c; + } + +@@ -722,7 +594,7 @@ static void sp_do_read_n(uint8_t * buf, const chipaddr addr, size_t len) + { + int rd_bytes = 0; + unsigned char sbuf[6]; +- printf_debug("%s: addr=0x%lx len=%lu\n", __func__, addr, (unsigned long)len); ++ msg_pspew("%s: addr=0x%lx len=%lu\n", __func__, addr, (unsigned long)len); + /* Stream the read-n -- as above. */ + if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes)) + sp_execute_opbuf_noflush(); +@@ -759,7 +631,7 @@ void serprog_chip_readn(uint8_t * buf, const chipaddr addr, size_t len) + void serprog_delay(int delay) + { + unsigned char buf[4]; +- printf_debug("%s\n", __func__); ++ msg_pspew("%s\n", __func__); + if ((sp_max_write_n) && (sp_write_n_bytes)) + sp_pass_writen(); + sp_check_opbuf_usage(5); +diff --git a/sharplhf00l04.c b/sharplhf00l04.c +index 53b9931..9669e20 100644 +--- a/sharplhf00l04.c ++++ b/sharplhf00l04.c +@@ -33,81 +33,20 @@ void print_lhf00l04_status(uint8_t status) + printf("%s", status & 0x2 ? "WP|TBL#|WP#,ABORT:" : "UNLOCK:"); + } + +-int probe_lhf00l04(struct flashchip *flash) +-{ +- chipaddr bios = flash->virtual_memory; +- uint8_t id1, id2; +- +-#if 0 +- /* Enter ID mode */ +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0x90, bios + 0x5555); +-#endif +- +- chip_writeb(0xff, bios); +- programmer_delay(10); +- chip_writeb(0x90, bios); +- programmer_delay(10); +- +- id1 = chip_readb(bios); +- id2 = chip_readb(bios + 0x01); +- +- /* Leave ID mode */ +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0xF0, bios + 0x5555); +- +- programmer_delay(10); +- +- printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __FUNCTION__, id1, id2); +- +- if (id1 != flash->manufacture_id || id2 != flash->model_id) +- return 0; +- +- map_flash_registers(flash); +- +- return 1; +-} +- +-uint8_t wait_lhf00l04(chipaddr bios) +-{ +- uint8_t status; +- uint8_t id1, id2; +- +- chip_writeb(0x70, bios); +- if ((chip_readb(bios) & 0x80) == 0) { // it's busy +- while ((chip_readb(bios) & 0x80) == 0) ; +- } +- +- status = chip_readb(bios); +- +- // put another command to get out of status register mode +- +- chip_writeb(0x90, bios); +- programmer_delay(10); +- +- id1 = chip_readb(bios); +- id2 = chip_readb(bios + 0x01); +- +- // this is needed to jam it out of "read id" mode +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0xF0, bios + 0x5555); +- +- return status; +-} ++/* FIXME: The datasheet is unclear whether we should use toggle_ready_jedec ++ * or wait_82802ab. ++ */ + +-int erase_lhf00l04_block(struct flashchip *flash, int offset) ++int erase_lhf00l04_block(struct flashchip *flash, unsigned int blockaddr, unsigned int blocklen) + { +- chipaddr bios = flash->virtual_memory + offset; +- chipaddr wrprotect = flash->virtual_registers + offset + 2; ++ chipaddr bios = flash->virtual_memory + blockaddr; ++ chipaddr wrprotect = flash->virtual_registers + blockaddr + 2; + uint8_t status; + + // clear status register + chip_writeb(0x50, bios); + printf("Erase at 0x%lx\n", bios); +- status = wait_lhf00l04(flash->virtual_memory); ++ status = wait_82802ab(flash->virtual_memory); + print_lhf00l04_status(status); + // clear write protect + printf("write protect is at 0x%lx\n", (wrprotect)); +@@ -120,34 +59,17 @@ int erase_lhf00l04_block(struct flashchip *flash, int offset) + chip_writeb(0xd0, bios); + programmer_delay(10); + // now let's see what the register is +- status = wait_lhf00l04(flash->virtual_memory); ++ status = wait_82802ab(flash->virtual_memory); + print_lhf00l04_status(status); +- printf("DONE BLOCK 0x%x\n", offset); ++ printf("DONE BLOCK 0x%x\n", blockaddr); + +- if (check_erased_range(flash, offset, flash->page_size)) { ++ if (check_erased_range(flash, blockaddr, blocklen)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } + return 0; + } + +-int erase_lhf00l04(struct flashchip *flash) +-{ +- int i; +- unsigned int total_size = flash->total_size * 1024; +- +- printf("total_size is %d; flash->page_size is %d\n", +- total_size, flash->page_size); +- for (i = 0; i < total_size; i += flash->page_size) +- if (erase_lhf00l04_block(flash, i)) { +- fprintf(stderr, "ERASE FAILED!\n"); +- return -1; +- } +- printf("DONE ERASE\n"); +- +- return 0; +-} +- + void write_page_lhf00l04(chipaddr bios, uint8_t *src, + chipaddr dst, int page_size) + { +@@ -157,7 +79,7 @@ void write_page_lhf00l04(chipaddr bios, uint8_t *src, + /* transfer data from source to destination */ + chip_writeb(0x40, dst); + chip_writeb(*src++, dst++); +- wait_lhf00l04(bios); ++ wait_82802ab(bios); + } + } + +@@ -168,7 +90,7 @@ int write_lhf00l04(struct flashchip *flash, uint8_t *buf) + int page_size = flash->page_size; + chipaddr bios = flash->virtual_memory; + +- if (erase_lhf00l04(flash)) { ++ if (erase_flash(flash)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } +@@ -180,7 +102,6 @@ int write_lhf00l04(struct flashchip *flash, uint8_t *buf) + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + } + printf("\n"); +- protect_jedec(bios); + + return 0; + } +diff --git a/spi.c b/spi.c +index 7f678a1..e1ab2ea 100644 +--- a/spi.c ++++ b/spi.c +@@ -40,6 +40,7 @@ const struct spi_programmer spi_programmer[] = { + .write_256 = NULL, + }, + ++#if INTERNAL_SUPPORT == 1 + { /* SPI_CONTROLLER_ICH7 */ + .command = ich_spi_send_command, + .multicommand = ich_spi_send_multicommand, +@@ -81,6 +82,7 @@ const struct spi_programmer spi_programmer[] = { + .read = wbsio_spi_read, + .write_256 = wbsio_spi_write_1, + }, ++#endif + + #if FT2232_SPI_SUPPORT == 1 + { /* SPI_CONTROLLER_FT2232 */ +@@ -91,12 +93,32 @@ const struct spi_programmer spi_programmer[] = { + }, + #endif + ++#if DUMMY_SUPPORT == 1 + { /* SPI_CONTROLLER_DUMMY */ + .command = dummy_spi_send_command, + .multicommand = default_spi_send_multicommand, + .read = NULL, + .write_256 = NULL, + }, ++#endif ++ ++#if BUSPIRATE_SPI_SUPPORT == 1 ++ { /* SPI_CONTROLLER_BUSPIRATE */ ++ .command = buspirate_spi_send_command, ++ .multicommand = default_spi_send_multicommand, ++ .read = buspirate_spi_read, ++ .write_256 = spi_chip_write_1, ++ }, ++#endif ++ ++#if DEDIPROG_SUPPORT == 1 ++ { /* SPI_CONTROLLER_DEDIPROG */ ++ .command = dediprog_spi_send_command, ++ .multicommand = default_spi_send_multicommand, ++ .read = dediprog_spi_read, ++ .write_256 = spi_chip_write_1, ++ }, ++#endif + + {}, /* This entry corresponds to SPI_CONTROLLER_INVALID. */ + }; +@@ -116,7 +138,7 @@ int spi_send_command(unsigned int writecnt, unsigned int readcnt, + writearr, readarr); + } + +-int spi_send_multicommand(struct spi_command *spicommands) ++int spi_send_multicommand(struct spi_command *cmds) + { + if (!spi_programmer[spi_controller].multicommand) { + fprintf(stderr, "%s called, but SPI is unsupported on this " +@@ -124,7 +146,7 @@ int spi_send_multicommand(struct spi_command *spicommands) + return 1; + } + +- return spi_programmer[spi_controller].multicommand(spicommands); ++ return spi_programmer[spi_controller].multicommand(cmds); + } + + int default_spi_send_command(unsigned int writecnt, unsigned int readcnt, +@@ -146,13 +168,12 @@ int default_spi_send_command(unsigned int writecnt, unsigned int readcnt, + return spi_send_multicommand(cmd); + } + +-int default_spi_send_multicommand(struct spi_command *spicommands) ++int default_spi_send_multicommand(struct spi_command *cmds) + { + int result = 0; +- while ((spicommands->writecnt || spicommands->readcnt) && !result) { +- result = spi_send_command(spicommands->writecnt, spicommands->readcnt, +- spicommands->writearr, spicommands->readarr); +- spicommands++; ++ for (; (cmds->writecnt || cmds->readcnt) && !result; cmds++) { ++ result = spi_send_command(cmds->writecnt, cmds->readcnt, ++ cmds->writearr, cmds->readarr); + } + return result; + } +@@ -264,7 +285,7 @@ static int probe_spi_rdid_generic(struct flashchip *flash, int bytes) + id2 = (readarr[1] << 8) | readarr[2]; + } + +- printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __FUNCTION__, id1, id2); ++ printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2); + + if (id1 == flash->manufacture_id && id2 == flash->model_id) { + /* Print the status register to tell the +@@ -280,6 +301,11 @@ static int probe_spi_rdid_generic(struct flashchip *flash, int bytes) + GENERIC_DEVICE_ID == flash->model_id) + return 1; + ++ /* Test if there is any vendor ID. */ ++ if (GENERIC_MANUF_ID == flash->manufacture_id && ++ id1 != 0xff) ++ return 1; ++ + return 0; + } + +@@ -293,15 +319,25 @@ int probe_spi_rdid4(struct flashchip *flash) + { + /* only some SPI chipsets support 4 bytes commands */ + switch (spi_controller) { ++#if INTERNAL_SUPPORT == 1 + case SPI_CONTROLLER_ICH7: + case SPI_CONTROLLER_ICH9: + case SPI_CONTROLLER_VIA: + case SPI_CONTROLLER_SB600: + case SPI_CONTROLLER_WBSIO: ++#endif + #if FT2232_SPI_SUPPORT == 1 + case SPI_CONTROLLER_FT2232: + #endif ++#if DUMMY_SUPPORT == 1 + case SPI_CONTROLLER_DUMMY: ++#endif ++#if BUSPIRATE_SPI_SUPPORT == 1 ++ case SPI_CONTROLLER_BUSPIRATE: ++#endif ++#if DEDIPROG_SUPPORT == 1 ++ case SPI_CONTROLLER_DEDIPROG: ++#endif + return probe_spi_rdid_generic(flash, 4); + default: + printf_debug("4b ID not supported on this SPI controller\n"); +@@ -321,7 +357,7 @@ int probe_spi_rems(struct flashchip *flash) + id1 = readarr[0]; + id2 = readarr[1]; + +- printf_debug("%s: id1 0x%x, id2 0x%x\n", __FUNCTION__, id1, id2); ++ printf_debug("%s: id1 0x%x, id2 0x%x\n", __func__, id1, id2); + + if (id1 == flash->manufacture_id && id2 == flash->model_id) { + /* Print the status register to tell the +@@ -337,6 +373,11 @@ int probe_spi_rems(struct flashchip *flash) + GENERIC_DEVICE_ID == flash->model_id) + return 1; + ++ /* Test if there is any vendor ID. */ ++ if (GENERIC_MANUF_ID == flash->manufacture_id && ++ id1 != 0xff) ++ return 1; ++ + return 0; + } + +@@ -356,7 +397,7 @@ int probe_spi_res(struct flashchip *flash) + return 0; + + id2 = readarr[0]; +- printf_debug("%s: id 0x%x\n", __FUNCTION__, id2); ++ printf_debug("%s: id 0x%x\n", __func__, id2); + if (id2 != flash->model_id) + return 0; + +@@ -490,7 +531,7 @@ void spi_prettyprint_status_register(struct flashchip *flash) + int spi_chip_erase_60(struct flashchip *flash) + { + int result; +- struct spi_command spicommands[] = { ++ struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, +@@ -514,7 +555,7 @@ int spi_chip_erase_60(struct flashchip *flash) + return result; + } + +- result = spi_send_multicommand(spicommands); ++ result = spi_send_multicommand(cmds); + if (result) { + fprintf(stderr, "%s failed during command execution\n", + __func__); +@@ -536,7 +577,7 @@ int spi_chip_erase_60(struct flashchip *flash) + int spi_chip_erase_c7(struct flashchip *flash) + { + int result; +- struct spi_command spicommands[] = { ++ struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, +@@ -560,7 +601,7 @@ int spi_chip_erase_c7(struct flashchip *flash) + return result; + } + +- result = spi_send_multicommand(spicommands); ++ result = spi_send_multicommand(cmds); + if (result) { + fprintf(stderr, "%s failed during command execution\n", __func__); + return result; +@@ -592,7 +633,7 @@ int spi_chip_erase_60_c7(struct flashchip *flash) + int spi_block_erase_52(struct flashchip *flash, unsigned int addr, unsigned int blocklen) + { + int result; +- struct spi_command spicommands[] = { ++ struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, +@@ -610,10 +651,10 @@ int spi_block_erase_52(struct flashchip *flash, unsigned int addr, unsigned int + .readarr = NULL, + }}; + +- result = spi_send_multicommand(spicommands); ++ result = spi_send_multicommand(cmds); + if (result) { +- fprintf(stderr, "%s failed during command execution\n", +- __func__); ++ fprintf(stderr, "%s failed during command execution at address 0x%x\n", ++ __func__, addr); + return result; + } + /* Wait until the Write-In-Progress bit is cleared. +@@ -636,7 +677,7 @@ int spi_block_erase_52(struct flashchip *flash, unsigned int addr, unsigned int + int spi_block_erase_d8(struct flashchip *flash, unsigned int addr, unsigned int blocklen) + { + int result; +- struct spi_command spicommands[] = { ++ struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, +@@ -654,9 +695,52 @@ int spi_block_erase_d8(struct flashchip *flash, unsigned int addr, unsigned int + .readarr = NULL, + }}; + +- result = spi_send_multicommand(spicommands); ++ result = spi_send_multicommand(cmds); + if (result) { +- fprintf(stderr, "%s failed during command execution\n", __func__); ++ fprintf(stderr, "%s failed during command execution at address 0x%x\n", ++ __func__, addr); ++ return result; ++ } ++ /* Wait until the Write-In-Progress bit is cleared. ++ * This usually takes 100-4000 ms, so wait in 100 ms steps. ++ */ ++ while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP) ++ programmer_delay(100 * 1000); ++ if (check_erased_range(flash, addr, blocklen)) { ++ fprintf(stderr, "ERASE FAILED!\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++/* Block size is usually ++ * 4k for PMC ++ */ ++int spi_block_erase_d7(struct flashchip *flash, unsigned int addr, unsigned int blocklen) ++{ ++ int result; ++ struct spi_command cmds[] = { ++ { ++ .writecnt = JEDEC_WREN_OUTSIZE, ++ .writearr = (const unsigned char[]){ JEDEC_WREN }, ++ .readcnt = 0, ++ .readarr = NULL, ++ }, { ++ .writecnt = JEDEC_BE_D7_OUTSIZE, ++ .writearr = (const unsigned char[]){ JEDEC_BE_D7, (addr >> 16) & 0xff, (addr >> 8) & 0xff, (addr & 0xff) }, ++ .readcnt = 0, ++ .readarr = NULL, ++ }, { ++ .writecnt = 0, ++ .writearr = NULL, ++ .readcnt = 0, ++ .readarr = NULL, ++ }}; ++ ++ result = spi_send_multicommand(cmds); ++ if (result) { ++ fprintf(stderr, "%s failed during command execution at address 0x%x\n", ++ __func__, addr); + return result; + } + /* Wait until the Write-In-Progress bit is cleared. +@@ -698,7 +782,7 @@ int spi_chip_erase_d8(struct flashchip *flash) + int spi_block_erase_20(struct flashchip *flash, unsigned int addr, unsigned int blocklen) + { + int result; +- struct spi_command spicommands[] = { ++ struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, +@@ -716,10 +800,10 @@ int spi_block_erase_20(struct flashchip *flash, unsigned int addr, unsigned int + .readarr = NULL, + }}; + +- result = spi_send_multicommand(spicommands); ++ result = spi_send_multicommand(cmds); + if (result) { +- fprintf(stderr, "%s failed during command execution\n", +- __func__); ++ fprintf(stderr, "%s failed during command execution at address 0x%x\n", ++ __func__, addr); + return result; + } + /* Wait until the Write-In-Progress bit is cleared. +@@ -775,8 +859,9 @@ int spi_write_status_enable(void) + int spi_write_status_register(int status) + { + int result; +- struct spi_command spicommands[] = { ++ struct spi_command cmds[] = { + { ++ /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */ + .writecnt = JEDEC_EWSR_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_EWSR }, + .readcnt = 0, +@@ -793,7 +878,7 @@ int spi_write_status_register(int status) + .readarr = NULL, + }}; + +- result = spi_send_multicommand(spicommands); ++ result = spi_send_multicommand(cmds); + if (result) { + fprintf(stderr, "%s failed during command execution\n", + __func__); +@@ -801,10 +886,10 @@ int spi_write_status_register(int status) + return result; + } + +-int spi_byte_program(int addr, uint8_t byte) ++int spi_byte_program(int addr, uint8_t databyte) + { + int result; +- struct spi_command spicommands[] = { ++ struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, +@@ -812,7 +897,7 @@ int spi_byte_program(int addr, uint8_t byte) + .readarr = NULL, + }, { + .writecnt = JEDEC_BYTE_PROGRAM_OUTSIZE, +- .writearr = (const unsigned char[]){ JEDEC_BYTE_PROGRAM, (addr >> 16) & 0xff, (addr >> 8) & 0xff, (addr & 0xff), byte }, ++ .writearr = (const unsigned char[]){ JEDEC_BYTE_PROGRAM, (addr >> 16) & 0xff, (addr >> 8) & 0xff, (addr & 0xff), databyte }, + .readcnt = 0, + .readarr = NULL, + }, { +@@ -822,25 +907,25 @@ int spi_byte_program(int addr, uint8_t byte) + .readarr = NULL, + }}; + +- result = spi_send_multicommand(spicommands); ++ result = spi_send_multicommand(cmds); + if (result) { +- fprintf(stderr, "%s failed during command execution\n", +- __func__); ++ fprintf(stderr, "%s failed during command execution at address 0x%x\n", ++ __func__, addr); + } + return result; + } + +-int spi_nbyte_program(int address, uint8_t *bytes, int len) ++int spi_nbyte_program(int addr, uint8_t *bytes, int len) + { + int result; + /* FIXME: Switch to malloc based on len unless that kills speed. */ + unsigned char cmd[JEDEC_BYTE_PROGRAM_OUTSIZE - 1 + 256] = { + JEDEC_BYTE_PROGRAM, +- (address >> 16) & 0xff, +- (address >> 8) & 0xff, +- (address >> 0) & 0xff, ++ (addr >> 16) & 0xff, ++ (addr >> 8) & 0xff, ++ (addr >> 0) & 0xff, + }; +- struct spi_command spicommands[] = { ++ struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, +@@ -869,10 +954,10 @@ int spi_nbyte_program(int address, uint8_t *bytes, int len) + + memcpy(&cmd[4], bytes, len); + +- result = spi_send_multicommand(spicommands); ++ result = spi_send_multicommand(cmds); + if (result) { +- fprintf(stderr, "%s failed during command execution\n", +- __func__); ++ fprintf(stderr, "%s failed during command execution at address 0x%x\n", ++ __func__, addr); + } + return result; + } +@@ -967,18 +1052,20 @@ int spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len) + int spi_chip_write_1(struct flashchip *flash, uint8_t *buf) + { + int total_size = 1024 * flash->total_size; +- int i; ++ int i, result = 0; + + spi_disable_blockprotect(); + /* Erase first */ + printf("Erasing flash before programming... "); +- if (flash->erase(flash)) { ++ if (erase_flash(flash)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } + printf("done.\n"); + for (i = 0; i < total_size; i++) { +- spi_byte_program(i, buf[i]); ++ result = spi_byte_program(i, buf[i]); ++ if (result) ++ return 1; + while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP) + programmer_delay(10); + } +@@ -1014,17 +1101,20 @@ int spi_aai_write(struct flashchip *flash, uint8_t *buf) + int result; + + switch (spi_controller) { ++#if INTERNAL_SUPPORT == 1 + case SPI_CONTROLLER_WBSIO: + fprintf(stderr, "%s: impossible with Winbond SPI masters," + " degrading to byte program\n", __func__); + return spi_chip_write_1(flash, buf); ++#endif + default: + break; + } +- if (flash->erase(flash)) { ++ if (erase_flash(flash)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } ++ /* FIXME: This will fail on ICH/VIA SPI. */ + result = spi_write_enable(); + if (result) + return result; +diff --git a/spi.h b/spi.h +index 3050ccf..1b49d59 100644 +--- a/spi.h ++++ b/spi.h +@@ -74,6 +74,11 @@ + #define JEDEC_BE_D8_OUTSIZE 0x04 + #define JEDEC_BE_D8_INSIZE 0x00 + ++/* Block Erase 0xd7 is supported by PMC chips. */ ++#define JEDEC_BE_D7 0xd7 ++#define JEDEC_BE_D7_OUTSIZE 0x04 ++#define JEDEC_BE_D7_INSIZE 0x00 ++ + /* Sector Erase 0x20 is supported by Macronix/SST chips. */ + #define JEDEC_SE 0x20 + #define JEDEC_SE_OUTSIZE 0x04 +@@ -106,6 +111,7 @@ + #define JEDEC_BYTE_PROGRAM_INSIZE 0x00 + + /* Error codes */ ++#define SPI_GENERIC_ERROR -1 + #define SPI_INVALID_OPCODE -2 + #define SPI_INVALID_ADDRESS -3 + #define SPI_INVALID_LENGTH -4 +diff --git a/sst28sf040.c b/sst28sf040.c +index 35f8c27..f253e39 100644 +--- a/sst28sf040.c ++++ b/sst28sf040.c +@@ -3,6 +3,7 @@ + * + * Copyright (C) 2000 Silicon Integrated System Corporation + * Copyright (C) 2005 coresystems GmbH ++ * Copyright (C) 2009 Sean Nelson + * + * 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 +@@ -30,31 +31,27 @@ + + static void protect_28sf040(chipaddr bios) + { +- uint8_t tmp; +- +- tmp = chip_readb(bios + 0x1823); +- tmp = chip_readb(bios + 0x1820); +- tmp = chip_readb(bios + 0x1822); +- tmp = chip_readb(bios + 0x0418); +- tmp = chip_readb(bios + 0x041B); +- tmp = chip_readb(bios + 0x0419); +- tmp = chip_readb(bios + 0x040A); ++ chip_readb(bios + 0x1823); ++ chip_readb(bios + 0x1820); ++ chip_readb(bios + 0x1822); ++ chip_readb(bios + 0x0418); ++ chip_readb(bios + 0x041B); ++ chip_readb(bios + 0x0419); ++ chip_readb(bios + 0x040A); + } + + static void unprotect_28sf040(chipaddr bios) + { +- uint8_t tmp; +- +- tmp = chip_readb(bios + 0x1823); +- tmp = chip_readb(bios + 0x1820); +- tmp = chip_readb(bios + 0x1822); +- tmp = chip_readb(bios + 0x0418); +- tmp = chip_readb(bios + 0x041B); +- tmp = chip_readb(bios + 0x0419); +- tmp = chip_readb(bios + 0x041A); ++ chip_readb(bios + 0x1823); ++ chip_readb(bios + 0x1820); ++ chip_readb(bios + 0x1822); ++ chip_readb(bios + 0x0418); ++ chip_readb(bios + 0x041B); ++ chip_readb(bios + 0x0419); ++ chip_readb(bios + 0x041A); + } + +-static int erase_sector_28sf040(struct flashchip *flash, unsigned long address, int sector_size) ++int erase_sector_28sf040(struct flashchip *flash, unsigned int address, unsigned int sector_size) + { + chipaddr bios = flash->virtual_memory; + +@@ -71,7 +68,7 @@ static int erase_sector_28sf040(struct flashchip *flash, unsigned long address, + return 0; + } + +-static int write_sector_28sf040(chipaddr bios, uint8_t *src, chipaddr dst, ++int write_sector_28sf040(chipaddr bios, uint8_t *src, chipaddr dst, + unsigned int page_size) + { + int i; +@@ -111,7 +108,7 @@ int probe_28sf040(struct flashchip *flash) + chip_writeb(RESET, bios); + programmer_delay(10); + +- printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __FUNCTION__, id1, id2); ++ printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2); + if (id1 == flash->manufacture_id && id2 == flash->model_id) + return 1; + +@@ -166,3 +163,13 @@ int write_28sf040(struct flashchip *flash, uint8_t *buf) + + return 0; + } ++ ++int erase_chip_28sf040(struct flashchip *flash, unsigned int addr, unsigned int blocklen) ++{ ++ if ((addr != 0) || (blocklen != flash->total_size * 1024)) { ++ fprintf(stderr, "%s called with incorrect arguments\n", ++ __func__); ++ return -1; ++ } ++ return erase_28sf040(flash); ++} +diff --git a/sst49lf040.c b/sst49lf040.c +index ab1c918..c91a139 100644 +--- a/sst49lf040.c ++++ b/sst49lf040.c +@@ -59,8 +59,8 @@ int write_49lf040(struct flashchip *flash, uint8_t *buf) + if (i % 10 == 0) + printf("%04d at address: 0x%08x ", i, i * page_size); + +- write_sector_jedec(bios, buf + i * page_size, +- bios + i * page_size, page_size); ++ write_sector_jedec_common(flash, buf + i * page_size, ++ bios + i * page_size, page_size, 0xffff); + + if (i % 10 == 0) + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); +diff --git a/sst49lfxxxc.c b/sst49lfxxxc.c +index 848ccde..80e5520 100644 +--- a/sst49lfxxxc.c ++++ b/sst49lfxxxc.c +@@ -3,6 +3,7 @@ + * + * Copyright (C) 2000 Silicon Integrated System Corporation + * Copyright (C) 2005-2007 coresystems GmbH ++ * Copyright (C) 2009 Sean Nelson + * + * 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 +@@ -35,6 +36,15 @@ + #define STATUS_ESS (1 << 6) + #define STATUS_WSMS (1 << 7) + ++int unlock_block_49lfxxxc(struct flashchip *flash, unsigned long address, unsigned char bits) ++{ ++ unsigned long lock = flash->virtual_registers + address + 2; ++ printf_debug("lockbits at address=0x%08lx is 0x%01x\n", lock, chip_readb(lock)); ++ chip_writeb(bits, lock); ++ ++ return 0; ++} ++ + static int write_lockbits_49lfxxxc(struct flashchip *flash, unsigned char bits) + { + chipaddr registers = flash->virtual_registers; +@@ -72,7 +82,7 @@ static int write_lockbits_49lfxxxc(struct flashchip *flash, unsigned char bits) + return 0; + } + +-static int erase_sector_49lfxxxc(struct flashchip *flash, unsigned long address, int sector_size) ++int erase_sector_49lfxxxc(struct flashchip *flash, unsigned int address, unsigned int sector_size) + { + unsigned char status; + chipaddr bios = flash->virtual_memory; +@@ -97,6 +107,31 @@ static int erase_sector_49lfxxxc(struct flashchip *flash, unsigned long address, + return 0; + } + ++int erase_block_49lfxxxc(struct flashchip *flash, unsigned int address, unsigned int block_size) ++{ ++ unsigned char status; ++ chipaddr bios = flash->virtual_memory; ++ ++ chip_writeb(BLOCK_ERASE, bios); ++ chip_writeb(ERASE, bios + address); ++ ++ do { ++ status = chip_readb(bios); ++ if (status & (STATUS_ESS | STATUS_BPS)) { ++ printf("block erase FAILED at address=0x%08lx status=0x%01x\n", bios + address, status); ++ chip_writeb(CLEAR_STATUS, bios); ++ return (-1); ++ } ++ } while (!(status & STATUS_WSMS)); ++ chip_writeb(RESET, bios); ++ ++ if (check_erased_range(flash, address, block_size)) { ++ fprintf(stderr, "ERASE FAILED!\n"); ++ return -1; ++ } ++ return 0; ++} ++ + static int write_sector_49lfxxxc(chipaddr bios, uint8_t *src, chipaddr dst, + unsigned int page_size) + { +@@ -141,7 +176,7 @@ int probe_49lfxxxc(struct flashchip *flash) + + chip_writeb(RESET, bios); + +- printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __FUNCTION__, id1, id2); ++ printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2); + + if (!(id1 == flash->manufacture_id && id2 == flash->model_id)) + return 0; +diff --git a/sst_fwhub.c b/sst_fwhub.c +index e7ae9e9..75d9e05 100644 +--- a/sst_fwhub.c ++++ b/sst_fwhub.c +@@ -2,6 +2,8 @@ + * This file is part of the flashrom project. + * + * Copyright (C) 2000 Silicon Integrated System Corporation ++ * Copyright (C) 2009 Kontron Modular Computers ++ * Copyright (C) 2009 Sean Nelson + * + * 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 +@@ -20,6 +22,8 @@ + + /* Adapted from the Intel FW hub stuff for 82802ax parts. */ + ++#include ++#include + #include "flash.h" + + // I need that Berkeley bit-map printer +@@ -86,15 +90,13 @@ int probe_sst_fwhub(struct flashchip *flash) + if (probe_jedec(flash) == 0) + return 0; + +- map_flash_registers(flash); +- + for (i = 0; i < flash->total_size * 1024; i += flash->page_size) + check_sst_fwhub_block_lock(flash, i); + + return 1; + } + +-int erase_sst_fwhub_block(struct flashchip *flash, int offset, int page_size) ++int erase_sst_fwhub_block(struct flashchip *flash, unsigned int offset, unsigned int page_size) + { + uint8_t blockstatus = clear_sst_fwhub_block_lock(flash, offset); + +@@ -113,6 +115,25 @@ int erase_sst_fwhub_block(struct flashchip *flash, int offset, int page_size) + return 0; + } + ++int erase_sst_fwhub_sector(struct flashchip *flash, unsigned int offset, unsigned int page_size) ++{ ++ uint8_t blockstatus = clear_sst_fwhub_block_lock(flash, offset); ++ ++ if (blockstatus) { ++ printf("Sector lock clearing failed, not erasing sector " ++ "at 0x%06x\n", offset); ++ return 1; ++ } ++ ++ if (erase_sector_jedec(flash, offset, page_size)) { ++ fprintf(stderr, "ERASE FAILED!\n"); ++ return -1; ++ } ++ toggle_ready_jedec(flash->virtual_memory); ++ ++ return 0; ++} ++ + int erase_sst_fwhub(struct flashchip *flash) + { + int i; +@@ -130,28 +151,33 @@ int erase_sst_fwhub(struct flashchip *flash) + + int write_sst_fwhub(struct flashchip *flash, uint8_t *buf) + { +- int i; ++ int i, rc; + int total_size = flash->total_size * 1024; + int page_size = flash->page_size; + chipaddr bios = flash->virtual_memory; +- uint8_t blockstatus; +- +- // FIXME: We want block wide erase instead of ironing the whole chip +- if (erase_sst_fwhub(flash)) { +- fprintf(stderr, "ERASE FAILED!\n"); +- return -1; +- } +- ++ uint8_t *readbuf = malloc(page_size); ++ + printf("Programming page: "); + for (i = 0; i < total_size / page_size; i++) { + printf("%04d at address: 0x%08x", i, i * page_size); +- blockstatus = clear_sst_fwhub_block_lock(flash, i * page_size); +- if (blockstatus) { +- printf(" is locked down permanently, aborting\n"); +- return 1; ++ ++ /* Auto Skip Blocks, which already contain the desired data: ++ * Faster, because we only write, what has changed ++ * More secure, because blocks, which are excluded ++ * (with the exclude or layout feature) ++ * are not erased and rewritten; data is retained also ++ * in sudden power off situations ++ */ ++ flash->read(flash, readbuf, i * page_size, page_size); ++ if (memcmp((void *)(buf + i * page_size), ++ (void *)(readbuf), page_size)) { ++ rc = erase_sst_fwhub_block(flash, i * page_size, ++ page_size); ++ if (rc) ++ return 1; ++ write_sector_jedec_common(flash, buf + i * page_size, ++ bios + i * page_size, page_size, 0xffff); + } +- write_sector_jedec(bios, buf + i * page_size, +- bios + i * page_size, page_size); + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + } + printf("\n"); +diff --git a/stm50flw0x0x.c b/stm50flw0x0x.c +index 30b7d50..3b56436 100644 +--- a/stm50flw0x0x.c ++++ b/stm50flw0x0x.c +@@ -2,6 +2,7 @@ + * This file is part of the flashrom project. + * + * Copyright (C) 2008 Claus Gindhart ++ * Copyright (C) 2009 Sean Nelson + * + * 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 +@@ -31,81 +32,19 @@ + #include "flash.h" + #include "flashchips.h" + +-void protect_stm50flw0x0x(chipaddr bios) +-{ +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0xA0, bios + 0x5555); +- +- programmer_delay(200); +-} +- +-int probe_stm50flw0x0x(struct flashchip *flash) +-{ +- chipaddr bios = flash->virtual_memory; +- uint8_t id1, id2; +- uint32_t largeid1, largeid2; +- +- /* Issue JEDEC Product ID Entry command */ +- chip_writeb(0xAA, bios + 0x5555); +- programmer_delay(10); +- chip_writeb(0x55, bios + 0x2AAA); +- programmer_delay(10); +- chip_writeb(0x90, bios + 0x5555); +- programmer_delay(40); +- +- /* Read product ID */ +- id1 = chip_readb(bios); +- id2 = chip_readb(bios + 0x01); +- largeid1 = id1; +- largeid2 = id2; +- +- /* Check if it is a continuation ID, this should be a while loop. */ +- if (id1 == 0x7F) { +- largeid1 <<= 8; +- id1 = chip_readb(bios + 0x100); +- largeid1 |= id1; +- } +- if (id2 == 0x7F) { +- largeid2 <<= 8; +- id2 = chip_readb(bios + 0x101); +- largeid2 |= id2; +- } +- +- /* Issue JEDEC Product ID Exit command */ +- chip_writeb(0xAA, bios + 0x5555); +- programmer_delay(10); +- chip_writeb(0x55, bios + 0x2AAA); +- programmer_delay(10); +- chip_writeb(0xF0, bios + 0x5555); +- programmer_delay(40); +- +- printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __FUNCTION__, largeid1, +- largeid2); +- +- if (largeid1 != flash->manufacture_id || largeid2 != flash->model_id) +- return 0; +- +- map_flash_registers(flash); +- +- return 1; +-} +- + static void wait_stm50flw0x0x(chipaddr bios) + { +- uint8_t id1; +- // id2; +- + chip_writeb(0x70, bios); + if ((chip_readb(bios) & 0x80) == 0) { // it's busy + while ((chip_readb(bios) & 0x80) == 0) ; + } ++ + // put another command to get out of status register mode + + chip_writeb(0x90, bios); + programmer_delay(10); + +- id1 = chip_readb(bios); ++ chip_readb(bios); // Read device ID (to make sure?) + + // this is needed to jam it out of "read id" mode + chip_writeb(0xAA, bios + 0x5555); +@@ -161,9 +100,9 @@ int unlock_block_stm50flw0x0x(struct flashchip *flash, int offset) + return 0; + } + +-int erase_block_stm50flw0x0x(struct flashchip *flash, int offset) ++int erase_block_stm50flw0x0x(struct flashchip *flash, unsigned int block, unsigned int blocksize) + { +- chipaddr bios = flash->virtual_memory + offset; ++ chipaddr bios = flash->virtual_memory + block; + + // clear status register + chip_writeb(0x50, bios); +@@ -175,11 +114,34 @@ int erase_block_stm50flw0x0x(struct flashchip *flash, int offset) + + wait_stm50flw0x0x(flash->virtual_memory); + +- if (check_erased_range(flash, offset, flash->page_size)) { ++ if (check_erased_range(flash, block, blocksize)) { ++ fprintf(stderr, "ERASE FAILED!\n"); ++ return -1; ++ } ++ printf("DONE BLOCK 0x%x\n", block); ++ ++ return 0; ++} ++ ++int erase_sector_stm50flw0x0x(struct flashchip *flash, unsigned int sector, unsigned int sectorsize) ++{ ++ chipaddr bios = flash->virtual_memory + sector; ++ ++ // clear status register ++ chip_writeb(0x50, bios); ++ printf_debug("Erase at 0x%lx\n", bios); ++ // now start it ++ chip_writeb(0x32, bios); ++ chip_writeb(0xd0, bios); ++ programmer_delay(10); ++ ++ wait_stm50flw0x0x(flash->virtual_memory); ++ ++ if (check_erased_range(flash, sector, sectorsize)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } +- printf("DONE BLOCK 0x%x\n", offset); ++ printf("DONE BLOCK 0x%x\n", sector); + + return 0; + } +@@ -230,7 +192,6 @@ int erase_stm50flw0x0x(struct flashchip *flash) + int i; + int total_size = flash->total_size * 1024; + int page_size = flash->page_size; +- chipaddr bios = flash->virtual_memory; + + printf("Erasing page:\n"); + for (i = 0; i < total_size / page_size; i++) { +@@ -241,17 +202,26 @@ int erase_stm50flw0x0x(struct flashchip *flash) + fprintf(stderr, "UNLOCK FAILED!\n"); + return -1; + } +- if (erase_block_stm50flw0x0x(flash, i * page_size)) { ++ if (erase_block_stm50flw0x0x(flash, i * page_size, page_size)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } + } + printf("\n"); +- protect_stm50flw0x0x(bios); + + return 0; + } + ++int erase_chip_stm50flw0x0x(struct flashchip *flash, unsigned int addr, unsigned int blocklen) ++{ ++ if ((addr != 0) || (blocklen != flash->total_size * 1024)) { ++ msg_cerr("%s called with incorrect arguments\n", ++ __func__); ++ return -1; ++ } ++ return erase_stm50flw0x0x(flash); ++} ++ + int write_stm50flw0x0x(struct flashchip *flash, uint8_t * buf) + { + int i, rc = 0; +@@ -285,13 +255,12 @@ int write_stm50flw0x0x(struct flashchip *flash, uint8_t * buf) + + rc = unlock_block_stm50flw0x0x(flash, i * page_size); + if (!rc) +- rc = erase_block_stm50flw0x0x(flash, i * page_size); ++ rc = erase_block_stm50flw0x0x(flash, i * page_size, page_size); + if (!rc) + write_page_stm50flw0x0x(bios, buf + i * page_size, + bios + i * page_size, page_size); + } + printf("\n"); +- protect_stm50flw0x0x(bios); + free(tmpbuf); + + return rc; +diff --git a/udelay.c b/udelay.c +index 9a0ab36..f61d793 100644 +--- a/udelay.c ++++ b/udelay.c +@@ -63,3 +63,16 @@ void myusec_calibrate_delay(void) + (unsigned long)micro, timeusec); + printf("OK.\n"); + } ++ ++void internal_delay(int usecs) ++{ ++ /* If the delay is >1 s, use usleep because timing does not need to ++ * be so precise. ++ */ ++ if (usecs > 1000000) { ++ usleep(usecs); ++ } else { ++ myusec_delay(usecs); ++ } ++} ++ +diff --git a/w29ee011.c b/w29ee011.c +index a26cd80..de9a547 100644 +--- a/w29ee011.c ++++ b/w29ee011.c +@@ -61,7 +61,7 @@ int probe_w29ee011(struct flashchip *flash) + chip_writeb(0xF0, bios + 0x5555); + programmer_delay(10); + +- printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __FUNCTION__, id1, id2); ++ printf_debug("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2); + + if (id1 == flash->manufacture_id && id2 == flash->model_id) + return 1; +diff --git a/w39v040c.c b/w39v040c.c +index 7fccd53..66ab115 100644 +--- a/w39v040c.c ++++ b/w39v040c.c +@@ -23,7 +23,11 @@ + int probe_w39v040c(struct flashchip *flash) + { + chipaddr bios = flash->virtual_memory; +- uint8_t id1, id2, lock; ++ int result = probe_jedec(flash); ++ uint8_t lock; ++ ++ if (!result) ++ return result; + + chip_writeb(0xAA, bios + 0x5555); + programmer_delay(10); +@@ -32,8 +36,6 @@ int probe_w39v040c(struct flashchip *flash) + chip_writeb(0x90, bios + 0x5555); + programmer_delay(10); + +- id1 = chip_readb(bios); +- id2 = chip_readb(bios + 1); + lock = chip_readb(bios + 0xfff2); + + chip_writeb(0xAA, bios + 0x5555); +@@ -43,17 +45,9 @@ int probe_w39v040c(struct flashchip *flash) + chip_writeb(0xF0, bios + 0x5555); + programmer_delay(40); + +- printf_debug("%s: id1 0x%02x, id2 0x%02x", __func__, id1, id2); +- if (!oddparity(id1)) +- printf_debug(", id1 parity violation"); +- printf_debug("\n"); +- if (flash->manufacture_id == id1 && flash->model_id == id2) { +- printf("%s: Boot block #TBL is %slocked, rest of chip #WP is %slocked.\n", +- __func__, lock & 0x4 ? "" : "un", lock & 0x8 ? "" : "un"); +- return 1; +- } +- +- return 0; ++ printf("%s: Boot block #TBL is %slocked, rest of chip #WP is %slocked.\n", ++ __func__, lock & 0x4 ? "" : "un", lock & 0x8 ? "" : "un"); ++ return 1; + } + + int erase_w39v040c(struct flashchip *flash) +@@ -78,7 +72,7 @@ int write_w39v040c(struct flashchip *flash, uint8_t *buf) + int page_size = flash->page_size; + chipaddr bios = flash->virtual_memory; + +- if (flash->erase(flash)) { ++ if (erase_flash(flash)) { + fprintf(stderr, "ERASE FAILED!\n"); + return -1; + } +@@ -86,8 +80,8 @@ int write_w39v040c(struct flashchip *flash, uint8_t *buf) + printf("Programming page: "); + for (i = 0; i < total_size / page_size; i++) { + printf("%04d at address: 0x%08x", i, i * page_size); +- write_sector_jedec(bios, buf + i * page_size, +- bios + i * page_size, page_size); ++ write_sector_jedec_common(flash, buf + i * page_size, ++ bios + i * page_size, page_size, 0xffff); + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + } + printf("\n"); +diff --git a/w39v080fa.c b/w39v080fa.c +index 31ef15f..8e846dc 100644 +--- a/w39v080fa.c ++++ b/w39v080fa.c +@@ -20,37 +20,6 @@ + + #include "flash.h" + +-int probe_winbond_fwhub(struct flashchip *flash) +-{ +- chipaddr bios = flash->virtual_memory; +- uint8_t id1, id2; +- +- /* Product Identification Entry */ +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0x90, bios + 0x5555); +- programmer_delay(10); +- +- /* Read product ID */ +- id1 = chip_readb(bios); +- id2 = chip_readb(bios + 0x01); +- +- /* Product Identifixation Exit */ +- chip_writeb(0xAA, bios + 0x5555); +- chip_writeb(0x55, bios + 0x2AAA); +- chip_writeb(0xF0, bios + 0x5555); +- programmer_delay(10); +- +- printf_debug("%s: id1 0x%x, id2 0x%x\n", __FUNCTION__, id1, id2); +- +- if (id1 != flash->manufacture_id || id2 != flash->model_id) +- return 0; +- +- map_flash_registers(flash); +- +- return 1; +-} +- + static int unlock_block_winbond_fwhub(struct flashchip *flash, int offset) + { + chipaddr wrprotect = flash->virtual_registers + offset + 2; +@@ -201,7 +170,7 @@ int write_winbond_fwhub(struct flashchip *flash, uint8_t *buf) + printf("Programming: "); + for (i = 0; i < total_size; i += flash->page_size) { + printf("0x%08x\b\b\b\b\b\b\b\b\b\b", i); +- write_sector_jedec(bios, buf + i, bios + i, flash->page_size); ++ write_sector_jedec_common(flash, buf + i, bios + i, flash->page_size, 0xffff); + } + printf("\n"); + +diff --git a/w49f002u.c b/w49f002u.c +index d12bc72..87ce000 100644 +--- a/w49f002u.c ++++ b/w49f002u.c +@@ -36,8 +36,8 @@ int write_49f002(struct flashchip *flash, uint8_t *buf) + for (i = 0; i < total_size / page_size; i++) { + printf("%04d at address: 0x%08x ", i, i * page_size); + /* Byte-wise writing of 'page_size' bytes. */ +- write_sector_jedec(bios, buf + i * page_size, +- bios + i * page_size, page_size); ++ write_sector_jedec_common(flash, buf + i * page_size, ++ bios + i * page_size, page_size, 0xffff); + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + fflush(stdout); + } +diff --git a/wbsio_spi.c b/wbsio_spi.c +index 6b9425f..922aff1 100644 +--- a/wbsio_spi.c ++++ b/wbsio_spi.c +@@ -2,6 +2,7 @@ + * This file is part of the flashrom project. + * + * Copyright (C) 2008 Peter Stuge ++ * Copyright (C) 2009,2010 Carl-Daniel Hailfinger + * + * 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 +@@ -34,18 +35,18 @@ static uint16_t wbsio_get_spibase(uint16_t port) + w836xx_ext_enter(port); + id = sio_read(port, 0x20); + if (id != 0xa0) { +- fprintf(stderr, "\nW83627 not found at 0x%x, id=0x%02x want=0xa0.\n", port, id); ++ msg_perr("\nW83627 not found at 0x%x, id=0x%02x want=0xa0.\n", port, id); + goto done; + } + + if (0 == (sio_read(port, 0x24) & 2)) { +- fprintf(stderr, "\nW83627 found at 0x%x, but SPI pins are not enabled. (CR[0x24] bit 1=0)\n", port); ++ msg_perr("\nW83627 found at 0x%x, but SPI pins are not enabled. (CR[0x24] bit 1=0)\n", port); + goto done; + } + + sio_write(port, 0x07, 0x06); + if (0 == (sio_read(port, 0x30) & 1)) { +- fprintf(stderr, "\nW83627 found at 0x%x, but SPI is not enabled. (LDN6[0x30] bit 0=0)\n", port); ++ msg_perr("\nW83627 found at 0x%x, but SPI is not enabled. (LDN6[0x30] bit 0=0)\n", port); + goto done; + } + +@@ -62,7 +63,7 @@ int wbsio_check_for_spi(const char *name) + if (0 == (wbsio_spibase = wbsio_get_spibase(WBSIO_PORT2))) + return 1; + +- printf_debug("\nwbsio_spibase = 0x%x\n", wbsio_spibase); ++ msg_pspew("\nwbsio_spibase = 0x%x\n", wbsio_spibase); + + buses_supported |= CHIP_BUSTYPE_SPI; + spi_controller = SPI_CONTROLLER_WBSIO; +@@ -96,42 +97,42 @@ int wbsio_spi_send_command(unsigned int writecnt, unsigned int readcnt, + int i; + uint8_t mode = 0; + +- printf_debug("%s:", __func__); ++ msg_pspew("%s:", __func__); + + if (1 == writecnt && 0 == readcnt) { + mode = 0x10; + } else if (2 == writecnt && 0 == readcnt) { + OUTB(writearr[1], wbsio_spibase + 4); +- printf_debug(" data=0x%02x", writearr[1]); ++ msg_pspew(" data=0x%02x", writearr[1]); + mode = 0x20; + } else if (1 == writecnt && 2 == readcnt) { + mode = 0x30; + } else if (4 == writecnt && 0 == readcnt) { +- printf_debug(" addr=0x%02x", (writearr[1] & 0x0f)); ++ msg_pspew(" addr=0x%02x", (writearr[1] & 0x0f)); + for (i = 2; i < writecnt; i++) { + OUTB(writearr[i], wbsio_spibase + i); +- printf_debug("%02x", writearr[i]); ++ msg_pspew("%02x", writearr[i]); + } + mode = 0x40 | (writearr[1] & 0x0f); + } else if (5 == writecnt && 0 == readcnt) { +- printf_debug(" addr=0x%02x", (writearr[1] & 0x0f)); ++ msg_pspew(" addr=0x%02x", (writearr[1] & 0x0f)); + for (i = 2; i < 4; i++) { + OUTB(writearr[i], wbsio_spibase + i); +- printf_debug("%02x", writearr[i]); ++ msg_pspew("%02x", writearr[i]); + } + OUTB(writearr[i], wbsio_spibase + i); +- printf_debug(" data=0x%02x", writearr[i]); ++ msg_pspew(" data=0x%02x", writearr[i]); + mode = 0x50 | (writearr[1] & 0x0f); + } else if (8 == writecnt && 0 == readcnt) { +- printf_debug(" addr=0x%02x", (writearr[1] & 0x0f)); ++ msg_pspew(" addr=0x%02x", (writearr[1] & 0x0f)); + for (i = 2; i < 4; i++) { + OUTB(writearr[i], wbsio_spibase + i); +- printf_debug("%02x", writearr[i]); ++ msg_pspew("%02x", writearr[i]); + } +- printf_debug(" data=0x"); ++ msg_pspew(" data=0x"); + for (; i < writecnt; i++) { + OUTB(writearr[i], wbsio_spibase + i); +- printf_debug("%02x", writearr[i]); ++ msg_pspew("%02x", writearr[i]); + } + mode = 0x60 | (writearr[1] & 0x0f); + } else if (5 == writecnt && 4 == readcnt) { +@@ -142,17 +143,17 @@ int wbsio_spi_send_command(unsigned int writecnt, unsigned int readcnt, + */ + ; + } else if (4 == writecnt && readcnt >= 1 && readcnt <= 4) { +- printf_debug(" addr=0x%02x", (writearr[1] & 0x0f)); ++ msg_pspew(" addr=0x%02x", (writearr[1] & 0x0f)); + for (i = 2; i < writecnt; i++) { + OUTB(writearr[i], wbsio_spibase + i); +- printf_debug("%02x", writearr[i]); ++ msg_pspew("%02x", writearr[i]); + } + mode = ((7 + readcnt) << 4) | (writearr[1] & 0x0f); + } +- printf_debug(" cmd=%02x mode=%02x\n", writearr[0], mode); ++ msg_pspew(" cmd=%02x mode=%02x\n", writearr[0], mode); + + if (!mode) { +- fprintf(stderr, "%s: unsupported command type wr=%d rd=%d\n", ++ msg_perr("%s: unsupported command type wr=%d rd=%d\n", + __func__, writecnt, readcnt); + /* Command type refers to the number of bytes read/written. */ + return SPI_INVALID_LENGTH; +@@ -165,12 +166,12 @@ int wbsio_spi_send_command(unsigned int writecnt, unsigned int readcnt, + if (!readcnt) + return 0; + +- printf_debug("%s: returning data =", __func__); ++ msg_pspew("%s: returning data =", __func__); + for (i = 0; i < readcnt; i++) { + readarr[i] = INB(wbsio_spibase + 4 + i); +- printf_debug(" 0x%02x", readarr[i]); ++ msg_pspew(" 0x%02x", readarr[i]); + } +- printf_debug("\n"); ++ msg_pspew("\n"); + return 0; + } + +@@ -179,7 +180,7 @@ int wbsio_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len) + int size = flash->total_size * 1024; + + if (size > 1024 * 1024) { +- fprintf(stderr, "%s: Winbond saved on 4 register bits so max chip size is 1024 KB!\n", __func__); ++ msg_perr("%s: Winbond saved on 4 register bits so max chip size is 1024 KB!\n", __func__); + return 1; + } + +@@ -191,7 +192,7 @@ int wbsio_spi_write_1(struct flashchip *flash, uint8_t *buf) + int size = flash->total_size * 1024; + + if (size > 1024 * 1024) { +- fprintf(stderr, "%s: Winbond saved on 4 register bits so max chip size is 1024 KB!\n", __func__); ++ msg_perr("%s: Winbond saved on 4 register bits so max chip size is 1024 KB!\n", __func__); + return 1; + } + diff --git a/flashrom.spec b/flashrom.spec index 7af7c18..aae2edd 100644 --- a/flashrom.spec +++ b/flashrom.spec @@ -1,12 +1,15 @@ +%global svnrev 893 + Summary: Simple program for reading/writing BIOS chips content Name: flashrom Version: 0.9.1 -Release: 1%{?dist} +Release: 2.svn%{svnrev}%{?dist} License: GPLv2 Group: Applications/System URL: http://flashrom.org Source0: http://qa.coreboot.org/releases/%{name}-%{version}.tar.bz2 Source1: http://qa.coreboot.org/releases/%{name}-%{version}.tar.bz2.asc +Patch0: flashrom-r710-r893.diff BuildRequires: pciutils-devel BuildRequires: zlib-devel BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) @@ -20,6 +23,7 @@ and write new contents on the chips ("flash the chip"). %prep %setup -q +%patch0 -p1 %build CFLAGS="%{optflags}" %{__make} %{?_smp_mflags} @@ -38,6 +42,41 @@ CFLAGS="%{optflags}" %{__make} %{?_smp_mflags} %{_mandir}/man8/%{name}.* %changelog +* Mon Feb 8 2010 Peter Lemenkov 0.9.1-2.svn893 +- Updated to latest svn ver. 893 +- ST M29W512B chip +- Tekram P6Pro-A5 board +- Fixed GIGABYTE GA-7ZM board +- SST39SF512 chip +- Fixed SyncMOS S29C51004T chip +- Intel NM10 chipset +- Fixed A25L40PU and A2540PT chip +- Spansion S25FL008A chip +- MSI 651M-L board +- Several Eon EN25Bxx{T,B} chips +- Fixed Sharp LHF00L04 chip +- VIA VT8233A chipset +- MSI K8N Neo4-F board +- Intel Poulsbo chipset +- ECS K7S6A board +- ASRock M3A790GXH/128M board +- Asus M2V-MX board +- Shuttle AK31 board +- Fixed MSI KT4V board +- Asus P4B266LM board +- Asrock P4i65GV board +- Intel 3400 series / 5 series chipset +- W25x32 and W25x64 chips +- Sanyo LF25FW203A chip (sometimes labeled as 25FW203T) +- Shuttle FN25 (SN25P) board +- EPoX EP-8RDA3+ board +- ASUS P5ND2-SLI Deluxe board +- nVidia nForce 4 chipset +- VIA VT82C596 chipset +- Wyse Winterm S50 board +- Dell S1850 board +- Dr. Kaiser PC-Waechter PCI devices + * Fri Sep 4 2009 Peter Lemenkov 0.9.1-1 - Ver. 0.9.1 - See release notes at http://www.coreboot.org/Flashrom/0.9.1 diff --git a/import.log b/import.log index a7b2a81..812debe 100644 --- a/import.log +++ b/import.log @@ -6,3 +6,4 @@ flashrom-0-0_17_20090311svn3984_fc10:HEAD:flashrom-0-0.17.20090311svn3984.fc10.s flashrom-0-0_18_20090414svn4107_fc10:HEAD:flashrom-0-0.18.20090414svn4107.fc10.src.rpm:1239696789 flashrom-0_9_0-1_fc10:HEAD:flashrom-0.9.0-1.fc10.src.rpm:1241503535 flashrom-0_9_1-1_fc11:HEAD:flashrom-0.9.1-1.fc11.src.rpm:1252055642 +flashrom-0_9_1-2_svn893_fc12:HEAD:flashrom-0.9.1-2.svn893.fc12.src.rpm:1265623249