[flashrom] svn ver. 1158

Peter Lemenkov peter at fedoraproject.org
Sun Sep 12 15:21:21 UTC 2010


commit b85974cf4b0db3d16de9a51b0b896263128c8d4b
Author: Peter Lemenkov <lemenkov at gmail.com>
Date:   Sun Sep 12 19:21:05 2010 +0400

    svn ver. 1158
    
    Signed-off-by: Peter Lemenkov <lemenkov at gmail.com>

 .gitignore                                         |    1 +
 ...01-Define-dmidecode-path-at-a-build-stage.patch |   60 +
 flashrom-r1002-r1158.diff                          |16898 ++++++++++++++++++++
 flashrom-svn_ver.diff                              |   11 +
 flashrom.spec                                      |   26 +-
 5 files changed, 16988 insertions(+), 8 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 700b2c8..5cac6b0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 flashrom-0.9.2.tar.bz2
+/flashrom-0.9.2.tar.bz2
diff --git a/flashrom-0001-Define-dmidecode-path-at-a-build-stage.patch b/flashrom-0001-Define-dmidecode-path-at-a-build-stage.patch
new file mode 100644
index 0000000..c6bb3e5
--- /dev/null
+++ b/flashrom-0001-Define-dmidecode-path-at-a-build-stage.patch
@@ -0,0 +1,60 @@
+From 6023a9545efc976e37cc3e2444cdc7c18d987f8d Mon Sep 17 00:00:00 2001
+From: Peter Lemenkov <lemenkov at gmail.com>
+Date: Sat, 12 Jun 2010 22:13:53 +0400
+Subject: [PATCH 1/1] Define dmidecode path at a build stage
+
+This helps in case then user doesn't have dmidecode in his PATH.
+For example, if user elevates his privileges with sudo, then he
+may not inherit some superuser's environmental variables ($PATH,
+contaning additional directories - /sbin, /usr/sbin and similar).
+
+In case of failure to determine full path to dmidecode it just
+fallbacks to the default value.
+
+Tested only on Linux.
+
+Signed-off-by: Peter Lemenkov <lemenkov at gmail.com>
+---
+ Makefile |    5 ++++-
+ dmi.c    |    2 +-
+ 2 files changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index e83fc0b..7919e10 100644
+--- a/Makefile
++++ b/Makefile
+@@ -169,6 +169,9 @@ CONFIG_DEDIPROG ?= no
+ # Disable wiki printing by default. It is only useful if you have wiki access.
+ CONFIG_PRINT_WIKI ?= no
+ 
++# Try to detect full path for dmidecode
++CONFIG_DMIDECODE_PATH ?= $(shell LC_ALL=C type -p dmidecode 2>/dev/null || echo "dmidecode")
++
+ ifeq ($(CONFIG_INTERNAL), yes)
+ FEATURE_CFLAGS += -D'CONFIG_INTERNAL=1'
+ PROGRAMMER_OBJS += processor_enable.o chipset_enable.o board_enable.o cbtable.o dmi.o internal.o
+@@ -323,7 +326,7 @@ $(PROGRAM)$(EXEC_SUFFIX): $(OBJS)
+ TAROPTIONS = $(shell LC_ALL=C tar --version|grep -q GNU && echo "--owner=root --group=root")
+ 
+ %.o: %.c .features
+-	$(CC) -MMD $(CFLAGS) $(CPPFLAGS) $(FEATURE_CFLAGS) $(SVNDEF) -o $@ -c $<
++	$(CC) -MMD $(CFLAGS) $(CPPFLAGS) $(FEATURE_CFLAGS) $(SVNDEF) -DCONFIG_DMIDECODE_PATH=\"$(CONFIG_DMIDECODE_PATH)\" -o $@ -c $<
+ 
+ # Make sure to add all names of generated binaries here.
+ # This includes all frontends and libflashrom.
+diff --git a/dmi.c b/dmi.c
+index f18907f..d483acb 100644
+--- a/dmi.c
++++ b/dmi.c
+@@ -55,7 +55,7 @@ static const char *dmidecode_names[] = {
+ };
+ 
+ #define DMI_COMMAND_LEN_MAX 260
+-static const char *dmidecode_command = "dmidecode";
++static const char *dmidecode_command = CONFIG_DMIDECODE_PATH;
+ 
+ static char *dmistrings[ARRAY_SIZE(dmidecode_names)];
+ 
+-- 
+1.7.2.3
+
diff --git a/flashrom-r1002-r1158.diff b/flashrom-r1002-r1158.diff
new file mode 100644
index 0000000..07d2bd7
--- /dev/null
+++ b/flashrom-r1002-r1158.diff
@@ -0,0 +1,16898 @@
+diff --git a/82802ab.c b/82802ab.c
+index c9d1a75..3935a7e 100644
+--- a/82802ab.c
++++ b/82802ab.c
+@@ -26,8 +26,6 @@
+  *  - Order number: 290658-004
+  */
+ 
+-#include <string.h>
+-#include <stdlib.h>
+ #include "flash.h"
+ #include "chipdrivers.h"
+ 
+diff --git a/Makefile b/Makefile
+index 6a9beb5..e83fc0b 100644
+--- a/Makefile
++++ b/Makefile
+@@ -48,11 +48,37 @@ ifeq ($(OS_ARCH), FreeBSD)
+ CPPFLAGS += -I/usr/local/include
+ LDFLAGS += -L/usr/local/lib
+ endif
++ifeq ($(OS_ARCH), OpenBSD)
++CPPFLAGS += -I/usr/local/include
++LDFLAGS += -L/usr/local/lib
++endif
+ ifeq ($(OS_ARCH), DOS)
++EXEC_SUFFIX := .exe
+ CPPFLAGS += -I../libgetopt -I../libpci/include
+-# Bus Pirate and Serprog are not supported under DOS.
+-CONFIG_BUSPIRATESPI = no
+-CONFIG_SERPROG = no
++# FIXME Check if we can achieve the same effect with -L../libgetopt -lgetopt
++LIBS += ../libgetopt/libgetopt.a
++# Bus Pirate and Serprog are not supported under DOS (missing serial support).
++ifeq ($(CONFIG_BUSPIRATE_SPI), yes)
++UNSUPPORTED_FEATURES += CONFIG_BUSPIRATE_SPI=yes
++else
++override CONFIG_BUSPIRATE_SPI = no
++endif
++ifeq ($(CONFIG_SERPROG), yes)
++UNSUPPORTED_FEATURES += CONFIG_SERPROG=yes
++else
++override CONFIG_SERPROG = no
++endif
++# Dediprog and FT2232 are not supported under DOS (missing USB support).
++ifeq ($(CONFIG_DEDIPROG), yes)
++UNSUPPORTED_FEATURES += CONFIG_DEDIPROG=yes
++else
++override CONFIG_DEDIPROG = no
++endif
++ifeq ($(CONFIG_FT2232_SPI), yes)
++UNSUPPORTED_FEATURES += CONFIG_FT2232_SPI=yes
++else
++override CONFIG_FT2232_SPI = no
++endif
+ endif
+ 
+ CHIP_OBJS = jedec.o stm50flw0x0x.o w39v040c.o w39v080fa.o w29ee011.o \
+@@ -65,7 +91,7 @@ CLI_OBJS = flashrom.o cli_classic.o cli_output.o print.o
+ 
+ PROGRAMMER_OBJS = udelay.o programmer.o
+ 
+-all: pciutils features dep $(PROGRAM)
++all: pciutils features $(PROGRAM)$(EXEC_SUFFIX)
+ 
+ # Set the flashrom version string from the highest revision number
+ # of the checked out flashrom files.
+@@ -85,14 +111,29 @@ CONFIG_INTERNAL ?= yes
+ # Always enable serprog for now. Needs to be disabled on Windows.
+ CONFIG_SERPROG ?= yes
+ 
+-# Bitbanging SPI infrastructure is not used yet.
++# RayeR SPIPGM hardware support
++CONFIG_RAYER_SPI ?= yes
++
++# Bitbanging SPI infrastructure, default off unless needed.
++ifeq ($(CONFIG_RAYER_SPI), yes)
++override CONFIG_BITBANG_SPI = yes
++else
++ifeq ($(CONFIG_INTERNAL), yes)
++override CONFIG_BITBANG_SPI = yes
++else
++ifeq ($(CONFIG_NICINTEL_SPI), yes)
++override CONFIG_BITBANG_SPI = yes
++else
+ CONFIG_BITBANG_SPI ?= no
++endif
++endif
++endif
+ 
+ # Always enable 3Com NICs for now.
+ CONFIG_NIC3COM ?= yes
+ 
+-# Disable NVIDIA graphics cards for now, write/erase don't work properly.
+-CONFIG_GFXNVIDIA ?= no
++# Enable NVIDIA graphics cards. Note: write and erase do not work properly.
++CONFIG_GFXNVIDIA ?= yes
+ 
+ # Always enable SiI SATA controllers for now.
+ CONFIG_SATASII ?= yes
+@@ -102,7 +143,7 @@ CONFIG_SATASII ?= yes
+ CONFIG_ATAHPT ?= no
+ 
+ # Always enable FT2232 SPI dongles for now.
+-CONFIG_FT2232SPI ?= yes
++CONFIG_FT2232_SPI ?= yes
+ 
+ # Always enable dummy tracing for now.
+ CONFIG_DUMMY ?= yes
+@@ -110,8 +151,17 @@ CONFIG_DUMMY ?= yes
+ # Always enable Dr. Kaiser for now.
+ CONFIG_DRKAISER ?= yes
+ 
++# Always enable Realtek NICs for now.
++CONFIG_NICREALTEK ?= yes
++
++# Disable National Semiconductor NICs until support is complete and tested.
++CONFIG_NICNATSEMI ?= no
++
++# Always enable SPI on Intel NICs for now.
++CONFIG_NICINTEL_SPI ?= yes
++
+ # Always enable Bus Pirate SPI for now.
+-CONFIG_BUSPIRATESPI ?= yes
++CONFIG_BUSPIRATE_SPI ?= yes
+ 
+ # Disable Dediprog SF100 until support is complete and tested.
+ CONFIG_DEDIPROG ?= no
+@@ -120,109 +170,140 @@ CONFIG_DEDIPROG ?= no
+ 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 internal.o
++FEATURE_CFLAGS += -D'CONFIG_INTERNAL=1'
++PROGRAMMER_OBJS += processor_enable.o chipset_enable.o board_enable.o cbtable.o dmi.o internal.o
++# FIXME: The PROGRAMMER_OBJS below should only be included on x86.
++PROGRAMMER_OBJS += it87spi.o ichspi.o sb600spi.o wbsio_spi.o mcp6x_spi.o
+ NEED_PCI := yes
+ endif
+ 
+ ifeq ($(CONFIG_SERPROG), yes)
+-FEATURE_CFLAGS += -D'SERPROG_SUPPORT=1'
++FEATURE_CFLAGS += -D'CONFIG_SERPROG=1'
+ PROGRAMMER_OBJS += serprog.o
+-ifeq ($(OS_ARCH), SunOS)
+-LIBS += -lsocket
++NEED_SERIAL := yes
++NEED_NET := yes
+ endif
++
++ifeq ($(CONFIG_RAYER_SPI), yes)
++FEATURE_CFLAGS += -D'CONFIG_RAYER_SPI=1'
++PROGRAMMER_OBJS += rayer_spi.o
++# Actually, NEED_PCI is wrong. NEED_IOPORT_ACCESS would be more correct.
++NEED_PCI := yes
+ endif
+ 
+ ifeq ($(CONFIG_BITBANG_SPI), yes)
+-FEATURE_CFLAGS += -D'BITBANG_SPI_SUPPORT=1'
++FEATURE_CFLAGS += -D'CONFIG_BITBANG_SPI=1'
+ PROGRAMMER_OBJS += bitbang_spi.o
+ endif
+ 
+ ifeq ($(CONFIG_NIC3COM), yes)
+-FEATURE_CFLAGS += -D'NIC3COM_SUPPORT=1'
++FEATURE_CFLAGS += -D'CONFIG_NIC3COM=1'
+ PROGRAMMER_OBJS += nic3com.o
+ NEED_PCI := yes
+ endif
+ 
+ ifeq ($(CONFIG_GFXNVIDIA), yes)
+-FEATURE_CFLAGS += -D'GFXNVIDIA_SUPPORT=1'
++FEATURE_CFLAGS += -D'CONFIG_GFXNVIDIA=1'
+ PROGRAMMER_OBJS += gfxnvidia.o
+ NEED_PCI := yes
+ endif
+ 
+ ifeq ($(CONFIG_SATASII), yes)
+-FEATURE_CFLAGS += -D'SATASII_SUPPORT=1'
++FEATURE_CFLAGS += -D'CONFIG_SATASII=1'
+ PROGRAMMER_OBJS += satasii.o
+ NEED_PCI := yes
+ endif
+ 
+ ifeq ($(CONFIG_ATAHPT), yes)
+-FEATURE_CFLAGS += -D'ATAHPT_SUPPORT=1'
++FEATURE_CFLAGS += -D'CONFIG_ATAHPT=1'
+ PROGRAMMER_OBJS += atahpt.o
+ NEED_PCI := yes
+ endif
+ 
+-ifeq ($(CONFIG_FT2232SPI), yes)
++ifeq ($(CONFIG_FT2232_SPI), 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_CFLAGS += $(shell LC_ALL=C grep -q "FTDISUPPORT := yes" .features && printf "%s" "-D'CONFIG_FT2232_SPI=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'
++FEATURE_CFLAGS += -D'CONFIG_DUMMY=1'
+ PROGRAMMER_OBJS += dummyflasher.o
+ endif
+ 
+ ifeq ($(CONFIG_DRKAISER), yes)
+-FEATURE_CFLAGS += -D'DRKAISER_SUPPORT=1'
++FEATURE_CFLAGS += -D'CONFIG_DRKAISER=1'
+ PROGRAMMER_OBJS += drkaiser.o
+ NEED_PCI := yes
+ endif
+ 
+-ifeq ($(CONFIG_BUSPIRATESPI), yes)
+-FEATURE_CFLAGS += -D'BUSPIRATE_SPI_SUPPORT=1'
++ifeq ($(CONFIG_NICREALTEK), yes)
++FEATURE_CFLAGS += -D'CONFIG_NICREALTEK=1'
++PROGRAMMER_OBJS += nicrealtek.o
++NEED_PCI := yes
++endif
++
++ifeq ($(CONFIG_NICNATSEMI), yes)
++FEATURE_CFLAGS += -D'CONFIG_NICNATSEMI=1'
++PROGRAMMER_OBJS += nicnatsemi.o
++NEED_PCI := yes
++endif
++
++ifeq ($(CONFIG_NICINTEL_SPI), yes)
++FEATURE_CFLAGS += -D'CONFIG_NICINTEL_SPI=1'
++PROGRAMMER_OBJS += nicintel_spi.o
++NEED_PCI := yes
++endif
++
++ifeq ($(CONFIG_BUSPIRATE_SPI), yes)
++FEATURE_CFLAGS += -D'CONFIG_BUSPIRATE_SPI=1'
+ PROGRAMMER_OBJS += buspirate_spi.o
++NEED_SERIAL := yes
+ endif
+ 
+ ifeq ($(CONFIG_DEDIPROG), yes)
+-FEATURE_CFLAGS += -D'DEDIPROG_SUPPORT=1'
++FEATURE_CFLAGS += -D'CONFIG_DEDIPROG=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)
++ifeq ($(NEED_SERIAL), yes)
+ LIB_OBJS += serial.o
+ endif
+-endif
+ 
+-ifeq ($(NEED_PCI), yes)
+-CHECK_LIBPCI = yes
++ifeq ($(NEED_NET), yes)
++ifeq ($(OS_ARCH), SunOS)
++LIBS += -lsocket
++endif
+ endif
+ 
+ ifeq ($(NEED_PCI), yes)
++CHECK_LIBPCI = yes
+ FEATURE_CFLAGS += -D'NEED_PCI=1'
+ PROGRAMMER_OBJS += pcidev.o physmap.o hwaccess.o
+ ifeq ($(OS_ARCH), NetBSD)
+-LIBS += -lpciutils #		The libpci we want.
+-LIBS += -l$(shell uname -p) #	For (i386|x86_64)_iopl(2).
++# The libpci we want is called libpciutils on NetBSD and needs NetBSD libpci.
++LIBS += -lpciutils -lpci
++# For (i386|x86_64)_iopl(2).
++LIBS += -l$(shell uname -p)
+ else
+ ifeq ($(OS_ARCH), DOS)
+ # FIXME There needs to be a better way to do this
+-LIBS += ../libpci/lib/libpci.a ../libgetopt/libgetopt.a
++LIBS += ../libpci/lib/libpci.a
+ else
+ LIBS += -lpci
++ifeq ($(OS_ARCH), OpenBSD)
++# For (i386|amd64)_iopl(2).
++LIBS += -l$(shell uname -m)
++endif
+ endif
+ endif
+ endif
+ 
+ ifeq ($(CONFIG_PRINT_WIKI), yes)
+-FEATURE_CFLAGS += -D'PRINT_WIKI_SUPPORT=1'
++FEATURE_CFLAGS += -D'CONFIG_PRINT_WIKI=1'
+ CLI_OBJS += print_wiki.o
+ endif
+ 
+@@ -233,8 +314,8 @@ FEATURE_LIBS += $(shell LC_ALL=C grep -q "NEEDLIBZ := yes" .libdeps && printf "%
+ 
+ OBJS = $(CHIP_OBJS) $(CLI_OBJS) $(PROGRAMMER_OBJS) $(LIB_OBJS)
+ 
+-$(PROGRAM): $(OBJS)
+-	$(CC) $(LDFLAGS) -o $(PROGRAM) $(OBJS) $(FEATURE_LIBS) $(LIBS)
++$(PROGRAM)$(EXEC_SUFFIX): $(OBJS)
++	$(CC) $(LDFLAGS) -o $(PROGRAM)$(EXEC_SUFFIX) $(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
+@@ -242,33 +323,35 @@ $(PROGRAM): $(OBJS)
+ TAROPTIONS = $(shell LC_ALL=C tar --version|grep -q GNU && echo "--owner=root --group=root")
+ 
+ %.o: %.c .features
+-	$(CC) $(CFLAGS) $(CPPFLAGS) $(FEATURE_CFLAGS) $(SVNDEF) -o $@ -c $<
++	$(CC) -MMD $(CFLAGS) $(CPPFLAGS) $(FEATURE_CFLAGS) $(SVNDEF) -o $@ -c $<
+ 
++# Make sure to add all names of generated binaries here.
++# This includes all frontends and libflashrom.
++# We don't use EXEC_SUFFIX here because we want to clean everything.
+ clean:
+-	rm -f $(PROGRAM) *.o
++	rm -f $(PROGRAM) $(PROGRAM).exe *.o *.d
+ 
+ distclean: clean
+-	rm -f .dependencies .features .libdeps
+-
+-dep:
+-	@$(CC) $(CPPFLAGS) $(SVNDEF) -MM *.c > .dependencies
++	rm -f .features .libdeps
+ 
+-strip: $(PROGRAM)
+-	$(STRIP) $(STRIP_ARGS) $(PROGRAM)
++strip: $(PROGRAM)$(EXEC_SUFFIX)
++	$(STRIP) $(STRIP_ARGS) $(PROGRAM)$(EXEC_SUFFIX)
+ 
+-compiler:
++compiler: featuresavailable
+ 	@printf "Checking for a C compiler... "
+ 	@$(shell ( echo "int main(int argc, char **argv)"; \
+ 		   echo "{ return 0; }"; ) > .test.c )
+-	@$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .test.c -o .test >/dev/null &&	\
++	@$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .test.c -o .test$(EXEC_SUFFIX) >/dev/null &&	\
+ 		echo "found." || ( echo "not found."; \
+-		rm -f .test.c .test; exit 1)
+-	@rm -f .test.c .test
++		rm -f .test.c .test$(EXEC_SUFFIX); exit 1)
++	@rm -f .test.c .test$(EXEC_SUFFIX)
+ 
+ ifeq ($(CHECK_LIBPCI), yes)
+ pciutils: compiler
+ 	@printf "Checking for libpci headers... "
+-	@$(shell ( echo "#include <pci/pci.h>";		   \
++	@# Avoid a failing test due to libpci header symbol shadowing breakage
++	@$(shell ( echo "#define index shadow_workaround_index"; \
++		   echo "#include <pci/pci.h>";		   \
+ 		   echo "struct pci_access *pacc;";	   \
+ 		   echo "int main(int argc, char **argv)"; \
+ 		   echo "{ pacc = pci_alloc(); return 0; }"; ) > .test.c )
+@@ -279,15 +362,15 @@ pciutils: compiler
+ 		rm -f .test.c .test.o; exit 1)
+ 	@printf "Checking if libpci is present and sufficient... "
+ 	@printf "" > .libdeps
+-	@$(CC) $(LDFLAGS) .test.o -o .test $(LIBS) >/dev/null 2>&1 &&				\
++	@$(CC) $(LDFLAGS) .test.o -o .test$(EXEC_SUFFIX) $(LIBS) >/dev/null 2>&1 &&				\
+ 		echo "yes." || ( echo "no.";							\
+ 		printf "Checking if libz+libpci are present and sufficient...";	\
+-		$(CC) $(LDFLAGS) .test.o -o .test $(LIBS) -lz >/dev/null 2>&1 &&		\
++		$(CC) $(LDFLAGS) .test.o -o .test$(EXEC_SUFFIX) $(LIBS) -lz >/dev/null 2>&1 &&		\
+ 		( echo "yes."; echo "NEEDLIBZ := yes" > .libdeps ) || ( echo "no."; echo;	\
+ 		echo "Please install libpci (package pciutils) and/or libz.";			\
+ 		echo "See README for more information."; echo;				\
+-		rm -f .test.c .test.o .test; exit 1) )
+-	@rm -f .test.c .test.o .test
++		rm -f .test.c .test.o .test$(EXEC_SUFFIX); exit 1) )
++	@rm -f .test.c .test.o .test$(EXEC_SUFFIX)
+ else
+ pciutils: compiler
+ 	@printf "" > .libdeps
+@@ -295,7 +378,18 @@ endif
+ 
+ .features: features
+ 
+-ifeq ($(CONFIG_FT2232SPI), yes)
++# If a user does not explicitly request a non-working feature, we should
++# silently disable it. However, if a non-working (does not compile) feature
++# is explicitly requested, we should bail out with a descriptive error message.
++ifeq ($(UNSUPPORTED_FEATURES), )
++featuresavailable:
++else
++featuresavailable:
++	@echo "The following features are unavailable on your machine: $(UNSUPPORTED_FEATURES)"
++	@false
++endif
++
++ifeq ($(CONFIG_FT2232_SPI), yes)
+ features: compiler
+ 	@echo "FEATURES := yes" > .features.tmp
+ 	@printf "Checking for FTDI support... "
+@@ -303,7 +397,7 @@ features: compiler
+ 		   echo "struct ftdi_context *ftdic = NULL;";	   \
+ 		   echo "int main(int argc, char **argv)"; \
+ 		   echo "{ return ftdi_init(ftdic); }"; ) > .featuretest.c )
+-	@$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest $(FTDILIBS) $(LIBS) >/dev/null 2>&1 &&	\
++	@$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX) $(FTDILIBS) $(LIBS) >/dev/null 2>&1 &&	\
+ 		( echo "found."; echo "FTDISUPPORT := yes" >> .features.tmp ) ||	\
+ 		( echo "not found."; echo "FTDISUPPORT := no" >> .features.tmp )
+ 	@printf "Checking for utsname support... "
+@@ -311,11 +405,11 @@ features: compiler
+ 		   echo "struct utsname osinfo;";	   \
+ 		   echo "int main(int argc, char **argv)"; \
+ 		   echo "{ uname (&osinfo); return 0; }"; ) > .featuretest.c )
+-	@$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest >/dev/null 2>&1 &&	\
++	@$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX) >/dev/null 2>&1 &&	\
+ 		( echo "found."; echo "UTSNAME := yes" >> .features.tmp ) ||	\
+ 		( echo "not found."; echo "UTSNAME := no" >> .features.tmp )
+ 	@$(DIFF) -q .features.tmp .features >/dev/null 2>&1 && rm .features.tmp || mv .features.tmp .features
+-	@rm -f .featuretest.c .featuretest
++	@rm -f .featuretest.c .featuretest$(EXEC_SUFFIX)
+ else
+ features: compiler
+ 	@echo "FEATURES := yes" > .features.tmp
+@@ -324,17 +418,17 @@ features: compiler
+ 		   echo "struct utsname osinfo;";	   \
+ 		   echo "int main(int argc, char **argv)"; \
+ 		   echo "{ uname (&osinfo); return 0; }"; ) > .featuretest.c )
+-	@$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest >/dev/null 2>&1 &&	\
++	@$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX) >/dev/null 2>&1 &&	\
+ 		( echo "found."; echo "UTSNAME := yes" >> .features.tmp ) ||	\
+ 		( echo "not found."; echo "UTSNAME := no" >> .features.tmp )
+ 	@$(DIFF) -q .features.tmp .features >/dev/null 2>&1 && rm .features.tmp || mv .features.tmp .features
+-	@rm -f .featuretest.c .featuretest
++	@rm -f .featuretest.c .featuretest$(EXEC_SUFFIX)
+ endif
+ 
+-install: $(PROGRAM)
++install: $(PROGRAM)$(EXEC_SUFFIX)
+ 	mkdir -p $(DESTDIR)$(PREFIX)/sbin
+ 	mkdir -p $(DESTDIR)$(MANDIR)/man8
+-	$(INSTALL) -m 0755 $(PROGRAM) $(DESTDIR)$(PREFIX)/sbin
++	$(INSTALL) -m 0755 $(PROGRAM)$(EXEC_SUFFIX) $(DESTDIR)$(PREFIX)/sbin
+ 	$(INSTALL) -m 0644 $(PROGRAM).8 $(DESTDIR)$(MANDIR)/man8
+ 
+ export:
+@@ -352,6 +446,6 @@ tarball: export
+ djgpp-dos: clean
+ 	make CC=i586-pc-msdosdjgpp-gcc STRIP=i586-pc-msdosdjgpp-strip WARNERROR=no OS_ARCH=DOS
+ 
+-.PHONY: all clean distclean dep compiler pciutils features export tarball dos
++.PHONY: all clean distclean compiler pciutils features export tarball dos featuresavailable
+ 
+--include .dependencies
++-include $(OBJS:.o=.d)
+diff --git a/README b/README
+index 022c035..9bbecf0 100644
+--- a/README
++++ b/README
+@@ -55,6 +55,11 @@ On FreeBSD, you need the following ports:
+  * devel/gmake
+  * devel/libpci
+ 
++On OpenBSD, you need the following ports:
++
++ * devel/gmake
++ * sysutils/pciutils
++
+ To compile on Linux, use:
+ 
+  make
+@@ -76,6 +81,10 @@ To compile on NetBSD or DragonFly BSD, use:
+  ln -s /usr/pkg/include/pciutils pci
+  gmake CPPFLAGS=-I. LDFLAGS="-L/usr/pkg/lib -Wl,-rpath-link,/usr/pkg/lib"
+ 
++To compile on OpenBSD, use:
++
++ gmake
++
+ To compile and run on Darwin/Mac OS X:
+ 
+  Install DirectIO from coresystems GmbH.
+diff --git a/atahpt.c b/atahpt.c
+index c87bc36..3504407 100644
+--- a/atahpt.c
++++ b/atahpt.c
+@@ -22,6 +22,7 @@
+ #include <string.h>
+ #include <sys/types.h>
+ #include "flash.h"
++#include "programmer.h"
+ 
+ #define BIOS_ROM_ADDR		0x90
+ #define BIOS_ROM_DATA		0x94
+@@ -45,7 +46,7 @@ int atahpt_init(void)
+ 	get_io_perms();
+ 
+ 	io_base_addr = pcidev_init(PCI_VENDOR_ID_HPT, PCI_BASE_ADDRESS_4,
+-				   ata_hpt, programmer_param);
++				   ata_hpt);
+ 
+ 	/* Enable flash access. */
+ 	reg32 = pci_read_long(pcidev_dev, REG_FLASH_ACCESS);
+@@ -66,7 +67,6 @@ int atahpt_shutdown(void)
+ 	reg32 &= ~(1 << 24);
+ 	pci_write_long(pcidev_dev, REG_FLASH_ACCESS, reg32);
+ 
+-	free(programmer_param);
+ 	pci_cleanup(pacc);
+ 	release_io_perms();
+ 	return 0;
+diff --git a/bitbang_spi.c b/bitbang_spi.c
+index 8d6a9a1..f697f5a 100644
+--- a/bitbang_spi.c
++++ b/bitbang_spi.c
+@@ -1,7 +1,7 @@
+ /*
+  * This file is part of the flashrom project.
+  *
+- * Copyright (C) 2009 Carl-Daniel Hailfinger
++ * 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
+@@ -24,48 +24,57 @@
+ #include <ctype.h>
+ #include "flash.h"
+ #include "chipdrivers.h"
++#include "programmer.h"
+ #include "spi.h"
+ 
+-/* Length of half a clock period in usecs */
+-int bitbang_spi_half_period = 0;
++/* Length of half a clock period in usecs. */
++static int bitbang_spi_half_period;
+ 
+-enum bitbang_spi_master bitbang_spi_master = BITBANG_SPI_INVALID;
++static const struct bitbang_spi_master *bitbang_spi_master = NULL;
+ 
+-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)
++/* Note that CS# is active low, so val=0 means the chip is active. */
++static void bitbang_spi_set_cs(int val)
+ {
+-	bitbang_spi_master_table[bitbang_spi_master].set_cs(val);
++	bitbang_spi_master->set_cs(val);
+ }
+ 
+-void bitbang_spi_set_sck(int val)
++static void bitbang_spi_set_sck(int val)
+ {
+-	bitbang_spi_master_table[bitbang_spi_master].set_sck(val);
++	bitbang_spi_master->set_sck(val);
+ }
+ 
+-void bitbang_spi_set_mosi(int val)
++static void bitbang_spi_set_mosi(int val)
+ {
+-	bitbang_spi_master_table[bitbang_spi_master].set_mosi(val);
++	bitbang_spi_master->set_mosi(val);
+ }
+ 
+-int bitbang_spi_get_miso(void)
++static int bitbang_spi_get_miso(void)
+ {
+-	return bitbang_spi_master_table[bitbang_spi_master].get_miso();
++	return bitbang_spi_master->get_miso();
+ }
+ 
+-int bitbang_spi_init(void)
++int bitbang_spi_init(const struct bitbang_spi_master *master, int halfperiod)
+ {
++	/* BITBANG_SPI_INVALID is 0, so if someone forgot to initialize ->type,
++	 * we catch it here. Same goes for missing initialization of bitbanging
++	 * functions.
++	 */
++	if (!master || master->type == BITBANG_SPI_INVALID || !master->set_cs ||
++	    !master->set_sck || !master->set_mosi || !master->get_miso) {
++		msg_perr("Incomplete bitbanging SPI master setting! Please "
++			 "report a bug at flashrom at flashrom.org\n");
++		return 1;
++	}
++	bitbang_spi_master = master;
++	bitbang_spi_half_period = halfperiod;
++
+ 	bitbang_spi_set_cs(1);
+ 	bitbang_spi_set_sck(0);
+-	buses_supported = CHIP_BUSTYPE_SPI;
++	bitbang_spi_set_mosi(0);
+ 	return 0;
+ }
+ 
+-uint8_t bitbang_spi_readwrite_byte(uint8_t val)
++static uint8_t bitbang_spi_readwrite_byte(uint8_t val)
+ {
+ 	uint8_t ret = 0;
+ 	int i;
+@@ -85,50 +94,17 @@ uint8_t bitbang_spi_readwrite_byte(uint8_t val)
+ 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]);
+-	}
++	for (i = 0; i < writecnt; i++)
++		bitbang_spi_readwrite_byte(writearr[i]);
++	for (i = 0; i < readcnt; i++)
++		readarr[i] = bitbang_spi_readwrite_byte(0);
++
+ 	programmer_delay(bitbang_spi_half_period);
+ 	bitbang_spi_set_cs(1);
+ 	programmer_delay(bitbang_spi_half_period);
+-	memcpy(readarr, bufin + writecnt, readcnt);
+ 
+ 	return 0;
+ }
+@@ -139,27 +115,7 @@ int bitbang_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+ 	return spi_read_chunked(flash, buf, start, len, 64 * 1024);
+ }
+ 
+-int bitbang_spi_write_256(struct flashchip *flash, uint8_t *buf)
++int bitbang_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
+ {
+-	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;
++	return spi_write_chunked(flash, buf, start, len, 256);
+ }
+diff --git a/board_enable.c b/board_enable.c
+index 2cf81c2..a073d9c 100644
+--- a/board_enable.c
++++ b/board_enable.c
+@@ -25,9 +25,10 @@
+  */
+ 
+ #include <string.h>
+-#include <fcntl.h>
+ #include "flash.h"
++#include "programmer.h"
+ 
++#if defined(__i386__) || defined(__x86_64__)
+ /*
+  * Helper functions for many Winbond Super I/Os of the W836xx range.
+  */
+@@ -95,90 +96,279 @@ static int enable_flash_decode_superio(void)
+ }
+ #endif
+ 
+-/**
+- * Winbond W83627HF: Raise GPIO24.
+- *
+- * Suited for:
+- *  - Agami Aruma
+- *  - IWILL DK8-HTX
++/*
++ * SMSC FDC37B787: Raise GPIO50
+  */
+-static int w83627hf_gpio24_raise(uint16_t port, const char *name)
++static int fdc37b787_gpio50_raise(uint16_t port)
+ {
+-	w836xx_ext_enter(port);
++	uint8_t id, val;
+ 
+-	/* Is this the W83627HF? */
+-	if (sio_read(port, 0x20) != 0x52) {	/* Super I/O device ID reg. */
+-		msg_perr("\nERROR: %s: W83627HF: Wrong ID: 0x%02X.\n",
+-			name, sio_read(port, 0x20));
+-		w836xx_ext_leave(port);
++	OUTB(0x55, port);	/* enter conf mode */
++	id = sio_read(port, 0x20);
++	if (id != 0x44) {
++		msg_perr("\nERROR: FDC37B787: Wrong ID 0x%02X.\n", id);
++		OUTB(0xAA, port); /* leave conf mode */
+ 		return -1;
+ 	}
+ 
+-	/* PIN89S: WDTO/GP24 multiplex -> GPIO24 */
+-	sio_mask(port, 0x2B, 0x10, 0x10);
+-
+-	/* Select logical device 8: GPIO port 2 */
+-	sio_write(port, 0x07, 0x08);
++	sio_write(port, 0x07, 0x08);	/* Select Aux I/O subdevice */
+ 
+-	sio_mask(port, 0x30, 0x01, 0x01);	/* Activate logical device. */
+-	sio_mask(port, 0xF0, 0x00, 0x10);	/* GPIO24 -> output */
+-	sio_mask(port, 0xF2, 0x00, 0x10);	/* Clear GPIO24 inversion */
+-	sio_mask(port, 0xF1, 0x10, 0x10);	/* Raise GPIO24 */
++	val = sio_read(port, 0xC8);	/* GP50 */
++	if ((val & 0x1B) != 0x10)	/* output, no invert, GPIO */
++	{
++		msg_perr("\nERROR: GPIO50 mode 0x%02X unexpected.\n", val);
++		OUTB(0xAA, port);
++		return -1;
++	}
+ 
+-	w836xx_ext_leave(port);
++	sio_mask(port, 0xF9, 0x01, 0x01);
+ 
++	OUTB(0xAA, port);		/* Leave conf mode */
+ 	return 0;
+ }
+ 
+-static int w83627hf_gpio24_raise_2e(const char *name)
++/*
++ * Suited for:
++ *  - Nokia IP530: Intel 440BX + PIIX4 + FDC37B787
++ */
++static int fdc37b787_gpio50_raise_3f0(void)
+ {
+-	return w83627hf_gpio24_raise(0x2e, name);
++	return fdc37b787_gpio50_raise(0x3f0);
+ }
+ 
+-/**
+- * Winbond W83627THF: GPIO 4, bit 4
+- *
+- * Suited for:
+- *  - MSI K8T Neo2-F
+- *  - MSI K8N-NEO3
++struct winbond_mux {
++	uint8_t reg;		/* 0 if the corresponding pin is not muxed */
++	uint8_t data;		/* reg/data/mask may be directly ... */
++	uint8_t mask;		/* ... passed to sio_mask */
++};
++
++struct winbond_port {
++	const struct winbond_mux *mux; /* NULL or pointer to mux info for the 8 bits */
++	uint8_t ldn;		/* LDN this GPIO register is located in */
++	uint8_t enable_bit;	/* bit in 0x30 of that LDN to enable 
++	                           the GPIO port */
++	uint8_t base;		/* base register in that LDN for the port */
++};
++
++struct winbond_chip {
++	uint8_t device_id;	/* reg 0x20 of the expected w83626x */
++	uint8_t gpio_port_count;
++	const struct winbond_port *port;
++};
++
++
++#define UNIMPLEMENTED_PORT {NULL, 0, 0, 0}
++
++enum winbond_id {
++	WINBOND_W83627HF_ID = 0x52,
++	WINBOND_W83627EHF_ID = 0x88,
++	WINBOND_W83627THF_ID = 0x82,
++};
++
++static const struct winbond_mux w83627hf_port2_mux[8] = {
++	{0x2A, 0x01, 0x01},	/* or MIDI */
++	{0x2B, 0x80, 0x80},	/* or SPI */
++	{0x2B, 0x40, 0x40},	/* or SPI */
++	{0x2B, 0x20, 0x20},	/* or power LED */
++	{0x2B, 0x10, 0x10},	/* or watchdog */
++	{0x2B, 0x08, 0x08},	/* or infra red */
++	{0x2B, 0x04, 0x04},	/* or infra red */
++	{0x2B, 0x03, 0x03}	/* or IRQ1 input */
++};
++
++static const struct winbond_port w83627hf[3] = {
++	UNIMPLEMENTED_PORT,
++	{w83627hf_port2_mux, 0x08, 0, 0xF0},
++	UNIMPLEMENTED_PORT
++};
++
++static const struct winbond_mux w83627ehf_port2_mux[8] = {
++	{0x29, 0x06, 0x02},	/* or MIDI */
++	{0x29, 0x06, 0x02},
++	{0x24, 0x02, 0x00},	/* or SPI ROM interface */
++	{0x24, 0x02, 0x00},
++	{0x2A, 0x01, 0x01},	/* or keyboard/mouse interface */
++	{0x2A, 0x01, 0x01},
++	{0x2A, 0x01, 0x01},
++	{0x2A, 0x01, 0x01}
++};
++
++static const struct winbond_port w83627ehf[6] = {
++	UNIMPLEMENTED_PORT,
++	{w83627ehf_port2_mux, 0x09, 0, 0xE3},
++	UNIMPLEMENTED_PORT,
++	UNIMPLEMENTED_PORT,
++	UNIMPLEMENTED_PORT,
++	UNIMPLEMENTED_PORT
++};
++
++static const struct winbond_mux w83627thf_port4_mux[8] = {
++	{0x2D, 0x01, 0x01},	/* or watchdog or VID level strap */
++	{0x2D, 0x02, 0x02},	/* or resume reset */
++	{0x2D, 0x04, 0x04},	/* or S3 input */
++	{0x2D, 0x08, 0x08},	/* or PSON# */
++	{0x2D, 0x10, 0x10},	/* or PWROK */
++	{0x2D, 0x20, 0x20},	/* or suspend LED */
++	{0x2D, 0x40, 0x40},	/* or panel switch input */
++	{0x2D, 0x80, 0x80}	/* or panel switch output */
++};
++
++static const struct winbond_port w83627thf[5] = {
++	UNIMPLEMENTED_PORT,	/* GPIO1 */
++	UNIMPLEMENTED_PORT,	/* GPIO2 */
++	UNIMPLEMENTED_PORT,	/* GPIO3 */
++	{w83627thf_port4_mux, 0x09, 1, 0xF4},
++	UNIMPLEMENTED_PORT	/* GPIO5 */
++};
++
++static const struct winbond_chip winbond_chips[] = {
++	{WINBOND_W83627HF_ID,  ARRAY_SIZE(w83627hf),  w83627hf },
++	{WINBOND_W83627EHF_ID, ARRAY_SIZE(w83627ehf), w83627ehf},
++	{WINBOND_W83627THF_ID, ARRAY_SIZE(w83627thf), w83627thf},
++};
++
++/*
++ * Detects which Winbond Super I/O is responding at the given base address,
++ * but takes no effort to make sure the chip is really a Winbond Super I/O.
+  */
+-static int w83627thf_gpio4_4_raise(uint16_t port, const char *name)
++static const struct winbond_chip *winbond_superio_detect(uint16_t base)
+ {
+-	w836xx_ext_enter(port);
++	uint8_t chipid;
++	const struct winbond_chip *chip = NULL;
++	int i;
++
++	w836xx_ext_enter(base);
++	chipid = sio_read(base, 0x20);
++
++	for (i = 0; i < ARRAY_SIZE(winbond_chips); i++) {
++		if (winbond_chips[i].device_id == chipid) {
++			chip = &winbond_chips[i];
++			break;
++		}
++	}
++
++	w836xx_ext_leave(base);
++	return chip;
++}
+ 
+-	/* Is this the W83627THF? */
+-	if (sio_read(port, 0x20) != 0x82) {	/* Super I/O device ID reg. */
+-		msg_perr("\nERROR: %s: W83627THF: Wrong ID: 0x%02X.\n",
+-			name, sio_read(port, 0x20));
+-		w836xx_ext_leave(port);
++/*
++ * The chipid parameter goes away as soon as we have Super I/O matching in the
++ * board enable table. The call to winbond_superio_detect() goes away as
++ * soon as we have generic Super I/O detection code.
++ */
++static int winbond_gpio_set(uint16_t base, enum winbond_id chipid,
++                            int pin, int raise)
++{
++	const struct winbond_chip *chip = NULL;
++	const struct winbond_port *gpio;
++	int port = pin / 10;
++	int bit = pin % 10;
++
++	chip = winbond_superio_detect(base);
++	if (!chip) {
++		msg_perr("\nERROR: No supported Winbond Super I/O found\n");
++		return -1;
++	}
++	if (chip->device_id != chipid) {
++		msg_perr("\nERROR: Found Winbond chip with ID 0x%x, "
++		         "expected %x\n", chip->device_id, chipid);
++		return -1;
++	}
++	if (bit >= 8 || port == 0 || port > chip->gpio_port_count) {
++		msg_perr("\nERROR: winbond_gpio_set: Invalid GPIO number %d\n",
++		         pin);
+ 		return -1;
+ 	}
+ 
+-	/* PINxxxxS: GPIO4/bit 4 multiplex -> GPIOXXX */
++	gpio = &chip->port[port - 1];
+ 
+-	sio_write(port, 0x07, 0x09);      /* Select LDN 9: GPIO port 4 */
+-	sio_mask(port, 0x30, 0x02, 0x02); /* Activate logical device. */
+-	sio_mask(port, 0xF4, 0x00, 0x10); /* GPIO4 bit 4 -> output */
+-	sio_mask(port, 0xF6, 0x00, 0x10); /* Clear GPIO4 bit 4 inversion */
+-	sio_mask(port, 0xF5, 0x10, 0x10); /* Raise GPIO4 bit 4 */
++	if (gpio->ldn == 0) {
++		msg_perr("\nERROR: GPIO%d is not supported yet on this"
++		          " winbond chip\n", port);
++		return -1;
++	}
+ 
+-	w836xx_ext_leave(port);
++	w836xx_ext_enter(base);
++
++	/* Select logical device. */
++	sio_write(base, 0x07, gpio->ldn);
++
++	/* Activate logical device. */
++	sio_mask(base, 0x30, 1 << gpio->enable_bit, 1 << gpio->enable_bit);
++
++	/* Select GPIO function of that pin. */
++	if (gpio->mux && gpio->mux[bit].reg)
++		sio_mask(base, gpio->mux[bit].reg,
++		         gpio->mux[bit].data, gpio->mux[bit].mask);
++
++	sio_mask(base, gpio->base + 0, 0, 1 << bit);	/* Make pin output */
++	sio_mask(base, gpio->base + 2, 0, 1 << bit);	/* Clear inversion */
++	sio_mask(base, gpio->base + 1, raise << bit, 1 << bit);
++
++	w836xx_ext_leave(base);
+ 
+ 	return 0;
+ }
+ 
+-static int w83627thf_gpio4_4_raise_2e(const char *name)
++/*
++ * Winbond W83627HF: Raise GPIO24.
++ *
++ * Suited for:
++ *  - Agami Aruma
++ *  - IWILL DK8-HTX
++ */
++static int w83627hf_gpio24_raise_2e(void)
++{
++	return winbond_gpio_set(0x2e, WINBOND_W83627HF_ID, 24, 1);
++}
++
++/*
++ * Winbond W83627HF: Raise GPIO25.
++ *
++ * Suited for:
++ *  - MSI MS-6577
++ */
++static int w83627hf_gpio25_raise_2e(void)
++{
++	return winbond_gpio_set(0x2e, WINBOND_W83627HF_ID, 25, 1);
++}
++
++/*
++ * Winbond W83627EHF: Raise GPIO24.
++ *
++ * Suited for:
++ *  - ASUS A8N-VM CSM: AMD Socket 939 + GeForce 6150 (C51) + MCP51
++ */
++static int w83627ehf_gpio24_raise_2e(void)
+ {
+-	return w83627thf_gpio4_4_raise(0x2e, name);
++	return winbond_gpio_set(0x2e, WINBOND_W83627EHF_ID, 24, 1);
+ }
+ 
+-static int w83627thf_gpio4_4_raise_4e(const char *name)
++/*
++ * Winbond W83627THF: Raise GPIO 44.
++ *
++ * Suited for:
++ *  - MSI K8T Neo2-F
++ */
++static int w83627thf_gpio44_raise_2e(void)
+ {
+-	return w83627thf_gpio4_4_raise(0x4e, name);
++	return winbond_gpio_set(0x2e, WINBOND_W83627THF_ID, 44, 1);
+ }
+ 
+-/**
+- * w83627: Enable MEMW# and set ROM size to max.
++/*
++ * Winbond W83627THF: Raise GPIO 44.
++ *
++ * Suited for:
++ *  - MSI K8N Neo3
++ */
++static int w83627thf_gpio44_raise_4e(void)
++{
++	return winbond_gpio_set(0x4e, WINBOND_W83627THF_ID, 44, 1);
++}
++
++/*
++ * Enable MEMW# and set ROM size to max.
++ * Supported chips: W83L517D, W83697HF/F/HG, W83697SF/UF/UG
+  */
+ static void w836xx_memw_enable(uint16_t port)
+ {
+@@ -190,102 +380,166 @@ static void w836xx_memw_enable(uint16_t port)
+ 	w836xx_ext_leave(port);
+ }
+ 
+-/**
++/*
+  * 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.
++ *  - 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
++ *  - MSI KM4M-V and KM4AM-V: VIA KM400/KM400A + VT8237
++ *  - MSI MS-6561 (745 Ultra): SiS 745 + W83697HF
++ *  - MSI MS-6787 (P4MAM-V/P4MAM-L): VIA P4M266 + VT8235
+  */
+-static int w836xx_memw_enable_2e(const char *name)
++static int w836xx_memw_enable_2e(void)
+ {
+ 	w836xx_memw_enable(0x2E);
+ 
+ 	return 0;
+ }
+ 
+-/**
++/*
+  * Suited for:
+- *   - Termtek TK-3370 (rev. 2.5b)
++ *  - Termtek TK-3370 (rev. 2.5b)
+  */
+-static int w836xx_memw_enable_4e(const char *name)
++static int w836xx_memw_enable_4e(void)
+ {
+ 	w836xx_memw_enable(0x4E);
+ 
+ 	return 0;
+ }
+ 
+-/**
+- *
++/*
++ * Suited for all boards with ITE IT8705F.
++ * The SIS950 Super I/O probably requires a similar flash write enable.
+  */
+-static int it8705f_write_enable(uint8_t port, const char *name)
++int it8705f_write_enable(uint8_t port)
+ {
++	uint8_t tmp;
++	int ret = 0;
++
+ 	enter_conf_mode_ite(port);
+-	sio_mask(port, 0x24, 0x04, 0x04); /* Flash ROM I/F Writes Enable */
++	tmp = sio_read(port, 0x24);
++	/* Check if at least one flash segment is enabled. */
++	if (tmp & 0xf0) {
++		/* The IT8705F will respond to LPC cycles and translate them. */
++		buses_supported = CHIP_BUSTYPE_PARALLEL;
++		/* Flash ROM I/F Writes Enable */
++		tmp |= 0x04;
++		msg_pdbg("Enabling IT8705F flash ROM interface write.\n");
++		if (tmp & 0x02) {
++			/* The data sheet contradicts itself about max size. */
++			max_rom_decode.parallel = 1024 * 1024;
++			msg_pinfo("IT8705F with very unusual settings. Please "
++				  "send the output of \"flashrom -V\" to \n"
++				  "flashrom at flashrom.org to help us finish "
++				  "support for your Super I/O. Thanks.\n");
++			ret = 1;
++		} else if (tmp & 0x08) {
++			max_rom_decode.parallel = 512 * 1024;
++		} else {
++			max_rom_decode.parallel = 256 * 1024;
++		}
++		/* Safety checks. The data sheet is unclear here: Segments 1+3
++		 * overlap, no segment seems to cover top - 1MB to top - 512kB.
++		 * We assume that certain combinations make no sense.
++		 */
++		if (((tmp & 0x02) && !(tmp & 0x08)) || /* 1 MB en, 512 kB dis */
++		    (!(tmp & 0x10)) || /* 128 kB dis */
++		    (!(tmp & 0x40))) { /*  256/512 kB dis */
++			msg_perr("Inconsistent IT8705F decode size!\n");
++			ret = 1;
++		}
++		if (sio_read(port, 0x25) != 0) {
++			msg_perr("IT8705F flash data pins disabled!\n");
++			ret = 1;
++		}
++		if (sio_read(port, 0x26) != 0) {
++			msg_perr("IT8705F flash address pins 0-7 disabled!\n");
++			ret = 1;
++		}
++		if (sio_read(port, 0x27) != 0) {
++			msg_perr("IT8705F flash address pins 8-15 disabled!\n");
++			ret = 1;
++		}
++		if ((sio_read(port, 0x29) & 0x10) != 0) {
++			msg_perr("IT8705F flash write enable pin disabled!\n");
++			ret = 1;
++		}
++		if ((sio_read(port, 0x29) & 0x08) != 0) {
++			msg_perr("IT8705F flash chip select pin disabled!\n");
++			ret = 1;
++		}
++		if ((sio_read(port, 0x29) & 0x04) != 0) {
++			msg_perr("IT8705F flash read strobe pin disabled!\n");
++			ret = 1;
++		}
++		if ((sio_read(port, 0x29) & 0x03) != 0) {
++			msg_perr("IT8705F flash address pins 16-17 disabled!\n");
++			/* Not really an error if you use flash chips smaller
++			 * than 256 kByte, but such a configuration is unlikely.
++			 */
++			ret = 1;
++		}
++		msg_pdbg("Maximum IT8705F parallel flash decode size is %u.\n",
++			max_rom_decode.parallel);
++		if (ret) {
++			msg_pinfo("Not enabling IT8705F flash write.\n");
++		} else {
++			sio_write(port, 0x24, tmp);
++		}
++	} else {
++		msg_pdbg("No IT8705F flash segment enabled.\n");
++		/* Not sure if this is an error or not. */
++		ret = 0;
++	}
+ 	exit_conf_mode_ite(port);
+ 
+-	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
+- *
+- * The SIS950 Super I/O probably requires the same flash write enable.
+- */
+-static int it8705f_write_enable_2e(const char *name)
+-{
+-	return it8705f_write_enable(0x2e, name);
++	return ret;
+ }
+ 
+ static int pc87360_gpio_set(uint8_t gpio, int raise)
+ {
+-        static const int bankbase[] = {0, 4, 8, 10, 12};
+-        int gpio_bank = gpio / 8;
+-        int gpio_pin = gpio % 8;
+-        uint16_t baseport;
+-        uint8_t id, val;
+-
+-        if (gpio_bank > 4) {
+-                msg_perr("PC87360: Invalid GPIO %d\n", gpio);
+-                return -1;
+-        }
++	static const int bankbase[] = {0, 4, 8, 10, 12};
++	int gpio_bank = gpio / 8;
++	int gpio_pin = gpio % 8;
++	uint16_t baseport;
++	uint8_t id, val;
++
++	if (gpio_bank > 4) {
++		msg_perr("PC87360: Invalid GPIO %d\n", gpio);
++		return -1;
++	}
+ 
+-        id = sio_read(0x2E, 0x20);
+-        if (id != 0xE1) {
+-                msg_perr("PC87360: unexpected ID %02x\n", id);
+-                return -1;
+-        }
++	id = sio_read(0x2E, 0x20);
++	if (id != 0xE1) {
++		msg_perr("PC87360: unexpected ID %02x\n", id);
++		return -1;
++	}
+ 
+-        sio_write(0x2E, 0x07, 0x07);		/* Select GPIO device */
+-        baseport = (sio_read(0x2E, 0x60) << 8) | sio_read(0x2E, 0x61);
+-        if ((baseport & 0xFFF0) == 0xFFF0 || baseport == 0) {
+-                msg_perr("PC87360: invalid GPIO base address %04x\n",
+-                         baseport);
+-                return -1;
+-        }
+-        sio_mask (0x2E, 0x30, 0x01, 0x01);	/* Enable logical device */
+-        sio_write(0x2E, 0xF0, gpio_bank * 16 + gpio_pin);
+-        sio_mask (0x2E, 0xF1, 0x01, 0x01);	/* Make pin output */
++	sio_write(0x2E, 0x07, 0x07);		/* Select GPIO device. */
++	baseport = (sio_read(0x2E, 0x60) << 8) | sio_read(0x2E, 0x61);
++	if ((baseport & 0xFFF0) == 0xFFF0 || baseport == 0) {
++		msg_perr("PC87360: invalid GPIO base address %04x\n",
++			 baseport);
++		return -1;
++	}
++	sio_mask (0x2E, 0x30, 0x01, 0x01);	/* Enable logical device. */
++	sio_write(0x2E, 0xF0, gpio_bank * 16 + gpio_pin);
++	sio_mask (0x2E, 0xF1, 0x01, 0x01);	/* Make pin output. */
+ 
+-        val = INB(baseport + bankbase[gpio_bank]);
+-        if (raise)
+-                val |= 1 << gpio_pin;
+-        else
+-                val &= ~(1 << gpio_pin);
+-        OUTB(val, baseport + bankbase[gpio_bank]);
++	val = INB(baseport + bankbase[gpio_bank]);
++	if (raise)
++		val |= 1 << gpio_pin;
++	else
++		val &= ~(1 << gpio_pin);
++	OUTB(val, baseport + bankbase[gpio_bank]);
+ 
+-        return 0;
++	return 0;
+ }
+ 
+-/**
+- * VT823x: Set one of the GPIO pins.
++/*
++ * VIA VT823x: Set one of the GPIO pins.
+  */
+ static int via_vt823x_gpio_set(uint8_t gpio, int raise)
+ {
+@@ -340,42 +594,45 @@ static int via_vt823x_gpio_set(uint8_t gpio, int raise)
+ 	return 0;
+ }
+ 
+-/**
+- * Suited for ASUS M2V-MX: VIA K8M890 + VT8237A + IT8716F
++/*
++ * Suited for:
++ *  - ASUS M2V-MX: VIA K8M890 + VT8237A + IT8716F
+  */
+-static int via_vt823x_gpio5_raise(const char *name)
++static int via_vt823x_gpio5_raise(void)
+ {
+ 	/* On M2V-MX: GPO5 is connected to WP# and TBL#. */
+ 	return via_vt823x_gpio_set(5, 1);
+ }
+ 
+-/**
+- * Suited for VIA EPIA N & NL.
++/*
++ * Suited for:
++ *  - VIA EPIA EK & N & NL
+  */
+-static int via_vt823x_gpio9_raise(const char *name)
++static int via_vt823x_gpio9_raise(void)
+ {
+ 	return via_vt823x_gpio_set(9, 1);
+ }
+ 
+-/**
+- * Suited for VIA EPIA M and MII, and maybe other CLE266 based EPIAs.
++/*
++ * Suited for:
++ *  - VIA 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 via_vt823x_gpio15_raise(const char *name)
++static int via_vt823x_gpio15_raise(void)
+ {
+ 	return via_vt823x_gpio_set(15, 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 KT4V and KT4V-L: AMD K7 + VIA KT400 + VT8235
++ *  - MSI KT4 Ultra: AMD K7 + VIA KT400 + VT8235
+  */
+-static int board_msi_kt4v(const char *name)
++static int board_msi_kt4v(void)
+ {
+ 	int ret;
+ 
+@@ -385,14 +642,15 @@ static int board_msi_kt4v(const char *name)
+ 	return ret;
+ }
+ 
+-/**
+- * Suited for ASUS P5A.
++/*
++ * Suited for:
++ *  - ASUS P5A
+  *
+  * This is rather nasty code, but there's no way to do this cleanly.
+  * We're basically talking to some unknown device on SMBus, my guess
+  * is that it is the Winbond W83781D that lives near the DIP BIOS.
+  */
+-static int board_asus_p5a(const char *name)
++static int board_asus_p5a(void)
+ {
+ 	uint8_t tmp;
+ 	int i;
+@@ -411,7 +669,7 @@ static int board_asus_p5a(const char *name)
+ 	}
+ 
+ 	if (i == ASUSP5A_LOOP) {
+-		msg_perr("%s: Unable to contact device.\n", name);
++		msg_perr("Unable to contact device.\n");
+ 		return -1;
+ 	}
+ 
+@@ -427,7 +685,7 @@ static int board_asus_p5a(const char *name)
+ 	}
+ 
+ 	if ((i == ASUSP5A_LOOP) || !(tmp & 0x10)) {
+-		msg_perr("%s: failed to read device.\n", name);
++		msg_perr("Failed to read device.\n");
+ 		return -1;
+ 	}
+ 
+@@ -454,7 +712,7 @@ static int board_asus_p5a(const char *name)
+ 	}
+ 
+ 	if ((i == ASUSP5A_LOOP) || !(tmp & 0x10)) {
+-		msg_perr("%s: failed to write to device.\n", name);
++		msg_perr("Failed to write to device.\n");
+ 		return -1;
+ 	}
+ 
+@@ -464,9 +722,9 @@ static int board_asus_p5a(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.
++ * 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)
++static int board_hp_dl145_g3_enable(void)
+ {
+ 	/* GPIO 0 reg from PM regs */
+ 	/* Set GPIO 2 and 5 high, connected to flash WP# and TBL# pins. */
+@@ -475,18 +733,33 @@ static int board_hp_dl145_g3_enable(const char *name)
+ 	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.
++ */
++static int board_hp_dl165_g6_enable(void)
++{
++	/* Variant of DL145, with slightly different pin placement. */
++	sio_mask(0xcd6, 0x44, 0x80, 0x80); /* TBL# */
++	sio_mask(0xcd6, 0x46, 0x04, 0x04); /* WP# */
++
++	return 0;
++}
++
++static int board_ibm_x3455(void)
+ {
+-	/* raise gpio13 */
++	/* Raise GPIO13. */
+ 	sio_mask(0xcd6, 0x45, 0x20, 0x20);
+ 
+ 	return 0;
+ }
+ 
+-/**
+- * Suited for Shuttle FN25 (SN25P): AMD S939 + NVIDIA CK804 (nForce4).
++/*
++ * Suited for:
++ *  - Shuttle FN25 (SN25P): AMD S939 + NVIDIA CK804 (nForce4)
+  */
+-static int board_shuttle_fn25(const char *name)
++static int board_shuttle_fn25(void)
+ {
+ 	struct pci_dev *dev;
+ 
+@@ -502,13 +775,14 @@ static int board_shuttle_fn25(const char *name)
+ 	return 0;
+ }
+ 
+-/**
++/*
+  * Very similar to AMD 8111 IO Hub.
+  */
+ static int nvidia_mcp_gpio_set(int gpio, int raise)
+ {
+ 	struct pci_dev *dev;
+ 	uint16_t base;
++	uint16_t devclass;
+ 	uint8_t tmp;
+ 
+ 	if ((gpio < 0) || (gpio >= 0x40)) {
+@@ -522,18 +796,43 @@ static int nvidia_mcp_gpio_set(int gpio, int raise)
+ 	case 0x0030: /* CK804 */
+ 	case 0x0050: /* MCP04 */
+ 	case 0x0060: /* MCP2 */
++	case 0x00E0: /* CK8 */
+ 		break;
+-	default:
+-	    /* Newer MCPs use the SMBus Controller */
+-	    dev = pci_dev_find_vendorclass(0x10DE, 0x0C05);
+-	    switch (dev->device_id) {
+-	    case 0x0264: /* MCP51 */
++	case 0x0260: /* MCP51 */
++	case 0x0364: /* MCP55 */
++		/* find SMBus controller on *this* southbridge */
++		/* The infamous Tyan S2915-E has two south bridges; they are
++		   easily told apart from each other by the class of the 
++		   LPC bridge, but have the same SMBus bridge IDs */
++		if (dev->func != 0) {
++			msg_perr("MCP LPC bridge at unexpected function"
++			         " number %d\n", dev->func);
++			return -1;
++		}
++
++#if PCI_LIB_VERSION >= 0x020200
++		dev = pci_get_dev(pacc, dev->domain, dev->bus, dev->dev, 1);
++#else
++		/* pciutils/libpci before version 2.2 is too old to support
++		 * PCI domains. Such old machines usually don't have domains
++		 * besides domain 0, so this is not a problem.
++		 */
++		dev = pci_get_dev(pacc, dev->bus, dev->dev, 1);
++#endif
++		if (!dev) {
++			msg_perr("MCP SMBus controller could not be found\n");
++			return -1;
++		}
++		devclass = pci_read_word(dev, PCI_CLASS_DEVICE);
++		if (devclass != 0x0C05) {
++			msg_perr("Unexpected device class %04x for SMBus"
++			         " controller\n", devclass);
++			return -1;
++		}
+ 		break;
+-	    default:
++	default:
+ 		msg_perr("\nERROR: no NVIDIA LPC/SMBus controller found.\n");
+ 		return -1;
+-	    }
+-	    break;
+ 	}
+ 
+ 	base = pci_read_long(dev, 0x64) & 0x0000FF00; /* System control area */
+@@ -549,78 +848,130 @@ static int nvidia_mcp_gpio_set(int gpio, int raise)
+ 	return 0;
+ }
+ 
+-/**
+- * Suited for ASUS A8N-LA: nVidia MCP51.
+- * Suited for ASUS M2NBP-VM CSM: NVIDIA MCP51.
++/*
++ * Suited for:
++ *  - ASUS A8N-LA (HP OEM "Nagami-GL8E"): NVIDIA MCP51
++ *  - ASUS M2NBP-VM CSM: NVIDIA MCP51
+  */
+-static int nvidia_mcp_gpio0_raise(const char *name)
++static int nvidia_mcp_gpio0_raise(void)
+ {
+ 	return nvidia_mcp_gpio_set(0x00, 1);
+ }
+ 
+-/**
+- * Suited for Abit KN8 Ultra: nVidia CK804.
++/*
++ * Suited for:
++ *  - abit KN8 Ultra: NVIDIA CK804
+  */
+-static int nvidia_mcp_gpio2_lower(const char *name)
++static int nvidia_mcp_gpio2_lower(void)
+ {
+ 	return nvidia_mcp_gpio_set(0x02, 0);
+ }
+ 
+-/**
+- * Suited for MSI K8N Neo4: NVIDIA CK804.
+- * Suited for MSI K8N GM2-L: NVIDIA MCP51.
++/*
++ * Suited for:
++ *  - MSI K8N Neo4: NVIDIA CK804. TODO: Should probably be K8N Neo4 Platinum, see http://www.coreboot.org/pipermail/flashrom/2010-August/004362.html.
++ *  - MSI K8NGM2-L: NVIDIA MCP51
+  */
+-static int nvidia_mcp_gpio2_raise(const char *name)
++static int nvidia_mcp_gpio2_raise(void)
+ {
+ 	return nvidia_mcp_gpio_set(0x02, 1);
+ }
+ 
+-/**
+- * Suited for Abit NF7-S: NVIDIA CK804.
++/*
++ * Suited for:
++ *  - HP xw9400 (Tyan S2915-E OEM): Dual(!) NVIDIA MCP55
++ *
++ * Notes: a) There are two MCP55 chips, so also two SMBus bridges on that
++ *           board. We can't tell the SMBus logical devices apart, but we
++ *           can tell the LPC bridge functions apart.
++ *           We need to choose the SMBus bridge next to the LPC bridge with
++ *           ID 0x364 and the "LPC bridge" class.
++ *        b) #TBL is hardwired on that board to a pull-down. It can be
++ *           overridden by connecting the two solder points next to F2.
+  */
+-static int nvidia_mcp_gpio8_raise(const char *name)
++static int nvidia_mcp_gpio5_raise(void)
++{
++	return nvidia_mcp_gpio_set(0x05, 1);
++}
++
++/*
++ * Suited for:
++ *  - abit NF7-S: NVIDIA CK804
++ */
++static int nvidia_mcp_gpio8_raise(void)
+ {
+ 	return nvidia_mcp_gpio_set(0x08, 1);
+ }
+ 
+-/**
+- * Suited for ASUS P5ND2-SLI Deluxe: LGA775 + nForce4 SLI + MCP04.
++/*
++ * Suited for:
++ *  - MSI K8N Neo2 Platinum: Socket 939 + nForce3 Ultra + CK8
++ */
++static int nvidia_mcp_gpio0c_raise(void)
++{
++	return nvidia_mcp_gpio_set(0x0c, 1);
++}
++
++/*
++ * Suited for:
++ *  - abit NF-M2 nView: Socket AM2 + NVIDIA MCP51
++ */
++static int nvidia_mcp_gpio4_lower(void)
++{
++	return nvidia_mcp_gpio_set(0x04, 0);
++}
++
++/*
++ * Suited for:
++ *  - ASUS P5ND2-SLI Deluxe: LGA775 + nForce4 SLI + MCP04
+  */
+-static int nvidia_mcp_gpio10_raise(const char *name)
++static int nvidia_mcp_gpio10_raise(void)
+ {
+ 	return nvidia_mcp_gpio_set(0x10, 1);
+ }
+ 
+-/**
+- * Suited for the Gigabyte GA-K8N-SLI: CK804 southbridge.
++/*
++ * Suited for:
++ *  - GIGABYTE GA-K8N-SLI: AMD socket 939 + NVIDIA CK804 + ITE IT8712F
+  */
+-static int nvidia_mcp_gpio21_raise(const char *name)
++static int nvidia_mcp_gpio21_raise(void)
+ {
+ 	return nvidia_mcp_gpio_set(0x21, 0x01);
+ }
+ 
+-/**
+- * Suited for EPoX EP-8RDA3+: Socket A + nForce2 Ultra 400 + MCP2.
++/*
++ * Suited for:
++ *  - EPoX EP-8RDA3+: Socket A + nForce2 Ultra 400 + MCP2
+  */
+-static int nvidia_mcp_gpio31_raise(const char *name)
++static int nvidia_mcp_gpio31_raise(void)
+ {
+ 	return nvidia_mcp_gpio_set(0x31, 0x01);
+ }
+ 
+-/**
+- * Suited for Artec Group DBE61 and DBE62.
++/*
++ * Suited for:
++ *  - GIGABYTE GA-K8N51GMF-9
+  */
+-static int board_artecgroup_dbe6x(const char *name)
++static int nvidia_mcp_gpio3b_raise(void)
++{
++	return nvidia_mcp_gpio_set(0x3b, 1);
++}
++
++/*
++ * Suited for:
++ *  - Artec Group DBE61 and DBE62
++ */
++static int board_artecgroup_dbe6x(void)
+ {
+ #define DBE6x_MSR_DIVIL_BALL_OPTS	0x51400015
+-#define DBE6x_PRI_BOOT_LOC_SHIFT	(2)
+-#define DBE6x_BOOT_OP_LATCHED_SHIFT	(8)
+-#define DBE6x_SEC_BOOT_LOC_SHIFT	(10)
++#define DBE6x_PRI_BOOT_LOC_SHIFT	2
++#define DBE6x_BOOT_OP_LATCHED_SHIFT	8
++#define DBE6x_SEC_BOOT_LOC_SHIFT	10
+ #define DBE6x_PRI_BOOT_LOC		(3 << DBE6x_PRI_BOOT_LOC_SHIFT)
+ #define DBE6x_BOOT_OP_LATCHED		(3 << DBE6x_BOOT_OP_LATCHED_SHIFT)
+ #define DBE6x_SEC_BOOT_LOC		(3 << DBE6x_SEC_BOOT_LOC_SHIFT)
+-#define DBE6x_BOOT_LOC_FLASH		(2)
+-#define DBE6x_BOOT_LOC_FWHUB		(3)
++#define DBE6x_BOOT_LOC_FLASH		2
++#define DBE6x_BOOT_LOC_FWHUB		3
+ 
+ 	msr_t msr;
+ 	unsigned long boot_loc;
+@@ -648,7 +999,7 @@ static int board_artecgroup_dbe6x(const char *name)
+ 	return 0;
+ }
+ 
+-/**
++/*
+  * Helper function to raise/drop a given gpo line on Intel PIIX4{,E,M}.
+  */
+ static int intel_piix4_gpo_set(unsigned int gpo, int raise)
+@@ -657,48 +1008,61 @@ static int intel_piix4_gpo_set(unsigned int gpo, int raise)
+ 	struct pci_dev *dev;
+ 	uint32_t tmp, base;
+ 
++	static const uint32_t nonmuxed_gpos  = 0x58000101; /* GPPO {0,8,27,28,30} are always available */
++
++	static const struct {unsigned int reg, mask, value; } piix4_gpo[] = {
++	  {0},
++	  {0xB0, 0x0001, 0x0000},        /* GPO1... */
++	  {0xB0, 0x0001, 0x0000},
++	  {0xB0, 0x0001, 0x0000},
++	  {0xB0, 0x0001, 0x0000},
++	  {0xB0, 0x0001, 0x0000},
++	  {0xB0, 0x0001, 0x0000},
++	  {0xB0, 0x0001, 0x0000},        /* ...GPO7: GENCFG bit 0 */
++	  {0},
++	  {0xB0, 0x0100, 0x0000},        /* GPO9:  GENCFG bit 8 */
++	  {0xB0, 0x0200, 0x0000},        /* GPO10: GENCFG bit 9 */
++	  {0xB0, 0x0400, 0x0000},        /* GPO11: GENCFG bit 10 */
++	  {0x4E, 0x0100, 0x0000},        /* GPO12... */
++	  {0x4E, 0x0100, 0x0000},
++	  {0x4E, 0x0100, 0x0000},        /* ...GPO14: XBCS bit 8 */
++	  {0xB2, 0x0002, 0x0002},        /* GPO15... */
++	  {0xB2, 0x0002, 0x0002},        /* ...GPO16: GENCFG bit 17 */
++	  {0xB2, 0x0004, 0x0004},        /* GPO17: GENCFG bit 18 */
++	  {0xB2, 0x0008, 0x0008},        /* GPO18: GENCFG bit 19 */
++	  {0xB2, 0x0010, 0x0010},        /* GPO19: GENCFG bit 20 */
++	  {0xB2, 0x0020, 0x0020},        /* GPO20: GENCFG bit 21 */
++	  {0xB2, 0x0040, 0x0040},        /* GPO21: GENCFG bit 22 */
++	  {0xB2, 0x1000, 0x1000},        /* GPO22... */
++	  {0xB2, 0x1000, 0x1000},        /* ...GPO23: GENCFG bit 28 */
++	  {0xB2, 0x2000, 0x2000},        /* GPO24: GENCFG bit 29 */
++	  {0xB2, 0x4000, 0x4000},        /* GPO25: GENCFG bit 30 */
++	  {0xB2, 0x8000, 0x8000},        /* GPO26: GENCFG bit 31 */
++	  {0},
++	  {0},
++	  {0x4E, 0x0100, 0x0000},        /* ...GPO29: XBCS bit 8 */
++	  {0}
++	};
++
++
+ 	dev = pci_dev_find(0x8086, 0x7110);	/* Intel PIIX4 ISA bridge */
+ 	if (!dev) {
+ 		msg_perr("\nERROR: Intel PIIX4 ISA bridge not found.\n");
+ 		return -1;
+ 	}
+ 
+-	/* sanity check */
++	/* Sanity check. */
+ 	if (gpo > 30) {
+ 		msg_perr("\nERROR: Intel PIIX4 has no GPO%d.\n", gpo);
+ 		return -1;
+ 	}
+ 
+-	/* these are dual function pins which are most likely in use already */
+-	if (((gpo >= 1) && (gpo <= 7)) ||
+-	    ((gpo >= 9) && (gpo <= 21)) || (gpo == 29)) {
+-		msg_perr("\nERROR: Unsupported PIIX4 GPO%d.\n", gpo);
+-		return -1;
++	if ( (((1 << gpo) & nonmuxed_gpos) == 0) &&
++	     (pci_read_word(dev, piix4_gpo[gpo].reg) & piix4_gpo[gpo].mask) != piix4_gpo[gpo].value ) {
++	  msg_perr("\nERROR: PIIX4 GPO\%d not programmed for output.\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) {
+ 		msg_perr("\nERROR: Intel PIIX4 PM not found.\n");
+@@ -720,23 +1084,34 @@ static int intel_piix4_gpo_set(unsigned int gpo, int raise)
+ 	return 0;
+ }
+ 
+-/**
+- * Suited for EPoX EP-BX3, and maybe some other Intel 440BX based boards.
++/*
++ * Suited for:
++ *  - ASUS P2B-N
+  */
+-static int board_epox_ep_bx3(const char *name)
++static int intel_piix4_gpo18_lower(void)
++{
++	return intel_piix4_gpo_set(18, 0);
++}
++
++/*
++ * Suited for:
++ *  - EPoX EP-BX3
++ */
++static int intel_piix4_gpo22_raise(void)
+ {
+ 	return intel_piix4_gpo_set(22, 1);
+ }
+ 
+-/**
+- * Suited for Intel SE440BX-2
++/*
++ * Suited for:
++ *  - Intel SE440BX-2
+  */
+-static int intel_piix4_gpo27_lower(const char *name)
++static int intel_piix4_gpo27_lower(void)
+ {
+-        return intel_piix4_gpo_set(27, 0);
++	return intel_piix4_gpo_set(27, 0);
+ }
+ 
+-/**
++/*
+  * Set a GPIO line on a given Intel ICH LPC controller.
+  */
+ static int intel_ich_gpio_set(int gpio, int raise)
+@@ -789,9 +1164,11 @@ static int intel_ich_gpio_set(int gpio, int raise)
+ 
+ 	/* First, look for a known LPC bridge */
+ 	for (dev = pacc->devices; dev; dev = dev->next) {
+-		pci_fill_info(dev, PCI_FILL_CLASS);
++		uint16_t device_class;
++		/* libpci before version 2.2.4 does not store class info. */
++		device_class = pci_read_word(dev, PCI_CLASS_DEVICE);
+ 		if ((dev->vendor_id == 0x8086) &&
+-		    (dev->device_class == 0x0601)) { /* ISA Bridge */
++		    (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)
+@@ -807,12 +1184,14 @@ static int intel_ich_gpio_set(int gpio, int raise)
+ 		return -1;
+ 	}
+ 
+-	/* 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. */
++	/*
++	 * 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;
+ 
+-	/* check whether the line is allowed */
++	/* Check whether the line is allowed. */
+ 	if (gpio < 32)
+ 		allowed = (intel_ich_gpio_table[i].bank0 >> gpio) & 0x01;
+ 	else if (gpio < 64)
+@@ -830,7 +1209,7 @@ static int intel_ich_gpio_set(int gpio, int raise)
+ 	       raise ? "Rais" : "Dropp", gpio);
+ 
+ 	if (gpio < 32) {
+-		/* Set line to GPIO */
++		/* Set line to GPIO. */
+ 		tmp = INL(base);
+ 		/* ICH/ICH0 multiplexes 27/28 on the line set. */
+ 		if ((gpio == 28) &&
+@@ -852,12 +1231,12 @@ static int intel_ich_gpio_set(int gpio, int raise)
+ 			}
+ 		}
+ 
+-		/* Set GPIO to OUTPUT */
++		/* Set GPIO to OUTPUT. */
+ 		tmp = INL(base + 0x04);
+ 		tmp &= ~(1 << gpio);
+ 		OUTL(tmp, base + 0x04);
+ 
+-		/* Raise GPIO line */
++		/* Raise GPIO line. */
+ 		tmp = INL(base + 0x0C);
+ 		if (raise)
+ 			tmp |= 1 << gpio;
+@@ -867,7 +1246,7 @@ static int intel_ich_gpio_set(int gpio, int raise)
+ 	} else if (gpio < 64) {
+ 		gpio -= 32;
+ 
+-		/* Set line to GPIO */
++		/* Set line to GPIO. */
+ 		tmp = INL(base + 0x30);
+ 		tmp |= 1 << gpio;
+ 		OUTL(tmp, base + 0x30);
+@@ -884,12 +1263,12 @@ static int intel_ich_gpio_set(int gpio, int raise)
+ 			}
+ 		}
+ 
+-		/* Set GPIO to OUTPUT */
++		/* Set GPIO to OUTPUT. */
+ 		tmp = INL(base + 0x34);
+ 		tmp &= ~(1 << gpio);
+ 		OUTL(tmp, base + 0x34);
+ 
+-		/* Raise GPIO line */
++		/* Raise GPIO line. */
+ 		tmp = INL(base + 0x38);
+ 		if (raise)
+ 			tmp |= 1 << gpio;
+@@ -899,7 +1278,7 @@ static int intel_ich_gpio_set(int gpio, int raise)
+ 	} else {
+ 		gpio -= 64;
+ 
+-		/* Set line to GPIO */
++		/* Set line to GPIO. */
+ 		tmp = INL(base + 0x40);
+ 		tmp |= 1 << gpio;
+ 		OUTL(tmp, base + 0x40);
+@@ -911,12 +1290,12 @@ static int intel_ich_gpio_set(int gpio, int raise)
+ 			return -1;
+ 		}
+ 
+-		/* Set GPIO to OUTPUT */
++		/* Set GPIO to OUTPUT. */
+ 		tmp = INL(base + 0x44);
+ 		tmp &= ~(1 << gpio);
+ 		OUTL(tmp, base + 0x44);
+ 
+-		/* Raise GPIO line */
++		/* Raise GPIO line. */
+ 		tmp = INL(base + 0x48);
+ 		if (raise)
+ 			tmp |= 1 << gpio;
+@@ -928,90 +1307,128 @@ static int intel_ich_gpio_set(int gpio, int raise)
+ 	return 0;
+ }
+ 
+-/**
+- * Suited for Abit IP35: Intel P35 + ICH9R.
+- * Suited for Abit IP35 Pro: Intel P35 + ICH9R.
++/*
++ * Suited for:
++ *  - abit IP35: Intel P35 + ICH9R
++ *  - abit IP35 Pro: Intel P35 + ICH9R
+  */
+-static int intel_ich_gpio16_raise(const char *name)
++static int intel_ich_gpio16_raise(void)
+ {
+ 	return intel_ich_gpio_set(16, 1);
+ }
+ 
+-/**
+- * Suited for ASUS A8JM: Intel 945 + ICH7
++/*
++ * Suited for:
++ *  - HP Puffer2-UL8E (ASUS PTGD-LA OEM): LGA775 + 915 + ICH6
++ */
++static int intel_ich_gpio18_raise(void)
++{
++	return intel_ich_gpio_set(18, 1);
++}
++
++/*
++ * Suited for:
++ *  - ASUS A8Jm (laptop): Intel 945 + ICH7
+  */
+-static int intel_ich_gpio34_raise(const char *name)
++static int intel_ich_gpio34_raise(void)
+ {
+ 	return intel_ich_gpio_set(34, 1);
+ }
+ 
+-/**
+- * Suited for MSI MS-7046: LGA775 + 915P + ICH6.
++/*
++ * Suited for:
++ *  - MSI MS-7046: LGA775 + 915P + ICH6
+  */
+-static int intel_ich_gpio19_raise(const char *name)
++static int intel_ich_gpio19_raise(void)
+ {
+ 	return intel_ich_gpio_set(19, 1);
+ }
+ 
+-/**
++/*
+  * Suited for:
+- * - ASUS P4B266LM (Sony Vaio PCV-RX650): socket478 + 845D + ICH2.
+- * - ASUS P4C800-E Deluxe: socket478 + 875P + ICH5.
+- * - ASUS P4P800-E Deluxe: Intel socket478 + 865PE + ICH5R.
++ *  - ASUS P4B266LM (Sony Vaio PCV-RX650): socket478 + 845D + ICH2
++ *  - ASUS P4C800-E Deluxe: socket478 + 875P + ICH5
++ *  - ASUS P4P800: Intel socket478 + 865PE + ICH5R
++ *  - ASUS P4P800-E Deluxe: Intel socket478 + 865PE + ICH5R
++ *  - ASUS P5GD1 Pro: Intel LGA 775 + 915P + ICH6R
++ *  - ASUS P5PE-VM: Intel LGA775 + 865G + ICH5
++ *  - Samsung Polaris 32: socket478 + 865P + ICH5
+  */
+-static int intel_ich_gpio21_raise(const char *name)
++static int intel_ich_gpio21_raise(void)
+ {
+ 	return intel_ich_gpio_set(21, 1);
+ }
+ 
+-/**
++/*
+  * Suited for:
+- *  - ASUS P4B266: socket478 + Intel 845D + ICH2.
++ *  - ASUS P4B266: socket478 + Intel 845D + ICH2
+  *  - ASUS P4B533-E: socket478 + 845E + ICH4
+  *  - ASUS P4B-MX variant in HP Vectra VL420 SFF: socket478 + 845D + ICH2
+  */
+-static int intel_ich_gpio22_raise(const char *name)
++static int intel_ich_gpio22_raise(void)
+ {
+ 	return intel_ich_gpio_set(22, 1);
+ }
+ 
+-/**
+- * Suited for HP Vectra VL400: 815 + ICH + PC87360.
++/*
++ * Suited for:
++ *  - HP Vectra VL400: 815 + ICH + PC87360
+  */
+-
+-static int board_hp_vl400(const char *name)
++static int board_hp_vl400(void)
+ {
+-        int ret;
+-        ret = intel_ich_gpio_set(25, 1);	/* Master write enable ? */
+-        if (!ret)
+-                ret = pc87360_gpio_set(0x09, 1);	/* #WP ? */
+-        if (!ret)
+-                ret = pc87360_gpio_set(0x27, 1);	/* #TBL */
+-        return ret;
++	int ret;
++	ret = intel_ich_gpio_set(25, 1);	/* Master write enable ? */
++	if (!ret)
++		ret = pc87360_gpio_set(0x09, 1);	/* #WP ? */
++	if (!ret)
++		ret = pc87360_gpio_set(0x27, 1);	/* #TBL */
++	return ret;
+ }
+ 
+-/**
++/*
+  * Suited for:
+- * - Dell PowerEdge 1850: Intel PPGA604 + E7520 + ICH5R.
+- * - ASRock P4i65GV: Intel Socket478 + 865GV + ICH5R.
++ *  - Dell PowerEdge 1850: Intel PPGA604 + E7520 + ICH5R
++ *  - ASRock P4i65GV: Intel Socket478 + 865GV + ICH5R
++ *  - ASRock 775i65G: Intel LGA 775 + 865G + ICH5
+  */
+-static int intel_ich_gpio23_raise(const char *name)
++static int intel_ich_gpio23_raise(void)
+ {
+ 	return intel_ich_gpio_set(23, 1);
+ }
+ 
+-/**
+- * Suited for IBase MB899: i945GM + ICH7.
++/*
++ * Suited for:
++ *  - GIGABYTE GA-8IRML: Intel Socket478 + i845 + ICH2
+  */
+-static int intel_ich_gpio26_raise(const char *name)
++static int intel_ich_gpio25_raise(void)
++{
++	return intel_ich_gpio_set(25, 1);
++}
++
++/*
++ * Suited for:
++ *  - IBASE MB899: i945GM + ICH7
++ */
++static int intel_ich_gpio26_raise(void)
+ {
+ 	return intel_ich_gpio_set(26, 1);
+ }
+ 
+-/**
+- * Suited for Acorp 6A815EPD: socket 370 + intel 815 + ICH2.
++/*
++ * Suited for:
++ *  - P4SD-LA (HP OEM): i865 + ICH5
++ *  - GIGABYTE GA-8PE667 Ultra 2: socket 478 + i845PE + ICH4
+  */
+-static int board_acorp_6a815epd(const char *name)
++static int intel_ich_gpio32_raise(void)
++{
++	return intel_ich_gpio_set(32, 1);
++}
++
++/*
++ * Suited for:
++ *  - Acorp 6A815EPD: socket 370 + intel 815 + ICH2
++ */
++static int board_acorp_6a815epd(void)
+ {
+ 	int ret;
+ 
+@@ -1023,10 +1440,11 @@ static int board_acorp_6a815epd(const char *name)
+ 	return ret;
+ }
+ 
+-/**
+- * Suited for Kontron 986LCD-M: socket478 + 915GM + ICH7R.
++/*
++ * Suited for:
++ *  - Kontron 986LCD-M: Socket478 + 915GM + ICH7R
+  */
+-static int board_kontron_986lcd_m(const char *name)
++static int board_kontron_986lcd_m(void)
+ {
+ 	int ret;
+ 
+@@ -1037,8 +1455,9 @@ static int board_kontron_986lcd_m(const char *name)
+ 	return ret;
+ }
+ 
+-/**
+- * Suited for Soyo SY-7VCA: Pro133A + VT82C686.
++/*
++ * Suited for:
++ *  - Soyo SY-7VCA: Pro133A + VT82C686
+  */
+ static int via_apollo_gpo_set(int gpio, int raise)
+ {
+@@ -1089,29 +1508,34 @@ static int via_apollo_gpo_set(int gpio, int raise)
+ 	return 0;
+ }
+ 
+-/**
+- * Suited for Abit VT6X4: Pro133x + VT82C686A
++/*
++ * Suited for:
++ *  - abit VT6X4: Pro133x + VT82C686A
++ *  - abit VA6: Pro133x + VT82C686A
+  */
+-static int via_apollo_gpo4_lower(const char *name)
++static int via_apollo_gpo4_lower(void)
+ {
+ 	return via_apollo_gpo_set(4, 0);
+ }
+ 
+-/**
+- * Suited for Soyo SY-7VCA: Pro133A + VT82C686.
++/*
++ * Suited for:
++ *  - Soyo SY-7VCA: Pro133A + VT82C686
+  */
+-static int via_apollo_gpo0_lower(const char *name)
++static int via_apollo_gpo0_lower(void)
+ {
+ 	return via_apollo_gpo_set(0, 0);
+ }
+ 
+-/**
++/*
+  * Enable some GPIO pin on SiS southbridge.
+- * Suited for MSI 651M-L: SiS651 / SiS962
++ *
++ * Suited for:
++ *  - MSI 651M-L: SiS651 / SiS962
+  */
+-static int board_msi_651ml(const char *name)
++static int board_msi_651ml(void)
+ {
+-    	struct pci_dev *dev;
++	struct pci_dev *dev;
+ 	uint16_t base, temp;
+ 
+ 	dev = pci_dev_find(0x1039, 0x0962);
+@@ -1120,7 +1544,7 @@ static int board_msi_651ml(const char *name)
+ 		return 1;
+ 	}
+ 
+-	/* Registers 68 and 64 seem like bitmaps */
++	/* Registers 68 and 64 seem like bitmaps. */
+ 	base = pci_read_word(dev, 0x74);
+ 	temp = INW(base + 0x68);
+ 	temp &= ~(1 << 0);		/* Make pin output? */
+@@ -1135,7 +1559,7 @@ static int board_msi_651ml(const char *name)
+ 	return 0;
+ }
+ 
+-/**
++/*
+  * Find the runtime registers of an SMSC Super I/O, after verifying its
+  * chip ID.
+  *
+@@ -1169,11 +1593,11 @@ out:
+ 	return rt_port;
+ }
+ 
+-/**
+- * Disable write protection on the Mitac 6513WU.  WP# on the FWH is
++/*
++ * Disable write protection on the Mitac 6513WU. WP# on the FWH is
+  * connected to GP30 on the Super I/O, and TBL# is always high.
+  */
+-static int board_mitac_6513wu(const char *name)
++static int board_mitac_6513wu(void)
+ {
+ 	struct pci_dev *dev;
+ 	uint16_t rt_port;
+@@ -1202,15 +1626,16 @@ static int board_mitac_6513wu(const char *name)
+ 	return 0;
+ }
+ 
+-/**
+- * Suited for ASUS A7V8X: VIA KT400 + VT8235 + IT8703F-A
++/*
++ * Suited for:
++ *  - ASUS A7V8X: VIA KT400 + VT8235 + IT8703F
+  */
+-static int board_asus_a7v8x(const char *name)
++static int board_asus_a7v8x(void)
+ {
+ 	uint16_t id, base;
+ 	uint8_t tmp;
+ 
+-	/* find the IT8703F */
++	/* Find the IT8703F. */
+ 	w836xx_ext_enter(0x2E);
+ 	id = (sio_read(0x2E, 0x20) << 8) | sio_read(0x2E, 0x21);
+ 	w836xx_ext_leave(0x2E);
+@@ -1220,7 +1645,7 @@ static int board_asus_a7v8x(const char *name)
+ 		return -1;
+ 	}
+ 
+-	/* Get the GP567 IO base */
++	/* Get the GP567 I/O base. */
+ 	w836xx_ext_enter(0x2E);
+ 	sio_write(0x2E, 0x07, 0x0C);
+ 	base = (sio_read(0x2E, 0x60) << 8) | sio_read(0x2E, 0x61);
+@@ -1257,11 +1682,11 @@ static int it8712f_gpio_set(unsigned int line, int raise)
+ 	/* Check line */
+ 	if ((port > 4) || /* also catches unsigned -1 */
+ 	    ((port < 4) && (line > 7)) || ((port == 4) && (line > 5))) {
+-	    msg_perr("\nERROR: Unsupported IT8712F GPIO Line %02d.\n", line);
++	    msg_perr("\nERROR: Unsupported IT8712F GPIO line %02d.\n", line);
+ 	    return -1;
+ 	}
+ 
+-	/* find the IT8712F */
++	/* Find the IT8712F. */
+ 	enter_conf_mode_ite(0x2E);
+ 	id = (sio_read(0x2E, 0x20) << 8) | sio_read(0x2E, 0x21);
+ 	exit_conf_mode_ite(0x2E);
+@@ -1294,17 +1719,19 @@ static int it8712f_gpio_set(unsigned int line, int raise)
+ 	return 0;
+ }
+ 
+-/**
++/*
+  * Suited for:
+  * - ASUS A7V600-X: VIA KT600 + VT8237 + IT8712F
+  * - ASUS A7V8X-X: VIA KT400 + VT8235 + IT8712F
+  */
+-static int it8712f_gpio3_1_raise(const char *name)
++static int it8712f_gpio3_1_raise(void)
+ {
+ 	return it8712f_gpio_set(32, 1);
+ }
+ 
+-/**
++#endif
++
++/*
+  * Below is the list of boards which need a special "board enable" code in
+  * flashrom before their ROM chip can be accessed/written to.
+  *
+@@ -1344,88 +1771,110 @@ static int it8712f_gpio3_1_raise(const char *name)
+  */
+ 
+ /* Please keep this list alphabetically ordered by vendor/board name. */
+-struct board_pciid_enable board_pciid_enables[] = {
++const struct board_pciid_enable board_pciid_enables[] = {
+ 
+ 	/* first pci-id set [4],          second pci-id set [4],          dmi identifier coreboot id [2],             vendor name    board name       max_rom_...  OK? flash enable */
+-	{0x10DE, 0x0547, 0x147B, 0x1C2F,  0x10DE, 0x0548, 0x147B, 0x1C2F, NULL,          NULL,         NULL,          "Abit",        "AN-M2",                 0,   NT, nvidia_mcp_gpio2_raise},
+-	{0x8086, 0x2926, 0x147b, 0x1084,  0x11ab, 0x4364, 0x147b, 0x1084, NULL,          NULL,         NULL,          "Abit",        "IP35",                  0,   OK, intel_ich_gpio16_raise},
+-	{0x8086, 0x2930, 0x147b, 0x1083,  0x10ec, 0x8167, 0x147b, 0x1083, NULL,          NULL,         NULL,          "Abit",        "IP35 Pro",              0,   OK, intel_ich_gpio16_raise},
+-	{0x10de, 0x0050, 0x147b, 0x1c1a,       0,      0,      0,      0, NULL,          NULL,         NULL,          "Abit",        "KN8 Ultra",             0,   NT, nvidia_mcp_gpio2_lower},
+-	{0x10de, 0x01e0, 0x147b, 0x1c00,  0x10de, 0x0060, 0x147B, 0x1c00, NULL,          NULL,         NULL,          "Abit",        "NF7-S",                 0,   OK, nvidia_mcp_gpio8_raise},
+-	{0x1106, 0x0691,      0,      0,  0x1106, 0x3057,      0,      0, NULL,          "abit",       "vt6x4",       "Abit",        "VT6X4",                 0,   OK, via_apollo_gpo4_lower},
++#if defined(__i386__) || defined(__x86_64__)
++	{0x10DE, 0x0547, 0x147B, 0x1C2F,  0x10DE, 0x0548, 0x147B, 0x1C2F, NULL,          NULL,         NULL,          "abit",        "AN-M2",                 0,   NT, nvidia_mcp_gpio2_raise},
++	{0x8086, 0x24d3, 0x147b, 0x1014,  0x8086, 0x2578, 0x147b, 0x1014, NULL,          NULL,         NULL,          "abit",        "IC7",                   0,   NT, intel_ich_gpio23_raise},
++	{0x8086, 0x2930, 0x147b, 0x1084,  0x11ab, 0x4364, 0x147b, 0x1084, NULL,          NULL,         NULL,          "abit",        "IP35",                  0,   OK, intel_ich_gpio16_raise},
++	{0x8086, 0x2930, 0x147b, 0x1083,  0x10ec, 0x8167, 0x147b, 0x1083, NULL,          NULL,         NULL,          "abit",        "IP35 Pro",              0,   OK, intel_ich_gpio16_raise},
++	{0x10de, 0x0050, 0x147b, 0x1c1a,       0,      0,      0,      0, NULL,          NULL,         NULL,          "abit",        "KN8 Ultra",             0,   NT, nvidia_mcp_gpio2_lower},
++	{0x10de, 0x01e0, 0x147b, 0x1c00,  0x10de, 0x0060, 0x147B, 0x1c00, NULL,          NULL,         NULL,          "abit",        "NF7-S",                 0,   OK, nvidia_mcp_gpio8_raise},
++	{0x10de, 0x02f0, 0x147b, 0x1c26,  0x10de, 0x0240, 0x10de, 0x0222, NULL,          NULL,         NULL,          "abit",        "NF-M2 nView",           0,   NT, nvidia_mcp_gpio4_lower},
++	{0x1106, 0x0691,      0,      0,  0x1106, 0x3057,      0,      0, "(VA6)$",      NULL,         NULL,          "abit",        "VA6",                   0,   OK, via_apollo_gpo4_lower},
++	{0x1106, 0x0691,      0,      0,  0x1106, 0x3057,      0,      0, NULL,          "abit",       "vt6x4",       "abit",        "VT6X4",                 0,   OK, via_apollo_gpo4_lower},
+ 	{0x105a, 0x0d30, 0x105a, 0x4d33,  0x8086, 0x1130, 0x8086,      0, NULL,          NULL,         NULL,          "Acorp",       "6A815EPD",              0,   OK, board_acorp_6a815epd},
+-	{0x8086, 0x24D4, 0x1849, 0x24D0,  0x8086, 0x24D5, 0x1849, 0x9739, NULL,          NULL,         NULL,          "ASRock",      "P4i65GV",               0,   OK, intel_ich_gpio23_raise},
+ 	{0x1022, 0x746B,      0,      0,       0,      0,      0,      0, NULL,          "AGAMI",      "ARUMA",       "agami",       "Aruma",                 0,   OK, w83627hf_gpio24_raise_2e},
+-	{0x1106, 0x3177, 0x17F2, 0x3177,  0x1106, 0x3148, 0x17F2, 0x3148, NULL,          NULL,         NULL,          "Albatron",    "PM266A",                0,   OK, w836xx_memw_enable_2e},
+-	{0x1106, 0x3205, 0x1106, 0x3205,  0x10EC, 0x8139, 0xA0A0, 0x0477, NULL,          NULL,         NULL,          "AOpen",       "vKM400Am-S",            0,   OK, it8705f_write_enable_2e},
++	{0x1106, 0x3177, 0x17F2, 0x3177,  0x1106, 0x3148, 0x17F2, 0x3148, NULL,          NULL,         NULL,          "Albatron",    "PM266A Pro",            0,   OK, w836xx_memw_enable_2e},
+ 	{0x1022, 0x2090,      0,      0,  0x1022, 0x2080,      0,      0, NULL,          "artecgroup", "dbe61",       "Artec Group", "DBE61",                 0,   OK, board_artecgroup_dbe6x},
+ 	{0x1022, 0x2090,      0,      0,  0x1022, 0x2080,      0,      0, NULL,          "artecgroup", "dbe62",       "Artec Group", "DBE62",                 0,   OK, board_artecgroup_dbe6x},
++	{0x8086, 0x24D4, 0x1849, 0x24D0,  0x8086, 0x24D5, 0x1849, 0x9739, NULL,          NULL,         NULL,          "ASRock",      "P4i65GV",               0,   OK, intel_ich_gpio23_raise},
++	{0x8086, 0x2570, 0x1849, 0x2570,  0x8086, 0x24d3, 0x1849, 0x24d0, NULL,          NULL,         NULL,          "ASRock",      "775i65G",               0,   OK, intel_ich_gpio23_raise},
+ 	{0x1106, 0x3189, 0x1043, 0x807F,  0x1106, 0x3065, 0x1043, 0x80ED, NULL,          NULL,         NULL,          "ASUS",        "A7V600-X",              0,   OK, it8712f_gpio3_1_raise},
++	{0x1106, 0x3177, 0x1043, 0x80A1,  0x1106, 0x3205, 0x1043, 0x8118, NULL,          NULL,         NULL,          "ASUS",        "A7V8X-MX SE",           0,   OK, w836xx_memw_enable_2e},
+ 	{0x1106, 0x3189, 0x1043, 0x807F,  0x1106, 0x3177, 0x1043, 0x808C, NULL,          NULL,         NULL,          "ASUS",        "A7V8X",                 0,   OK, board_asus_a7v8x},
+ 	{0x1106, 0x3189, 0x1043, 0x807F,  0x1106, 0x3177, 0x1043, 0x80A1, NULL,          NULL,         NULL,          "ASUS",        "A7V8X-X",               0,   OK, it8712f_gpio3_1_raise},
+-	{0x1106, 0x3177, 0x1043, 0x80A1,  0x1106, 0x3205, 0x1043, 0x8118, NULL,          NULL,         NULL,          "ASUS",        "A7V8X-MX SE",           0,   OK, w836xx_memw_enable_2e},
+-	{0x8086, 0x27A0, 0x1043, 0x1287,  0x8086, 0x27DF, 0x1043, 0x1287, "^A8J",        NULL,         NULL,          "ASUS",        "A8JM",                  0,   NT, intel_ich_gpio34_raise},
+-	{0x10DE, 0x005E, 0x1043, 0x815A,  0x10DE, 0x0054, 0x1043, 0x815A, NULL,          NULL,         NULL,          "ASUS",        "A8N",                   0,   NT, board_shuttle_fn25},
+-	{0x10DE, 0x0260, 0x103c, 0x2a3e,  0x10DE, 0x0264, 0x103c, 0x2a3e, "NAGAMI",      NULL,         NULL,          "ASUS",        "A8N-LA",                0,   NT, nvidia_mcp_gpio0_raise},
++	{0x8086, 0x27A0, 0x1043, 0x1287,  0x8086, 0x27DF, 0x1043, 0x1287, "^A8J",        NULL,         NULL,          "ASUS",        "A8Jm",                  0,   NT, intel_ich_gpio34_raise},
++	{0x10DE, 0x0260, 0x103c, 0x2a3e,  0x10DE, 0x0264, 0x103c, 0x2a3e, "NAGAMI2L",    NULL,         NULL,          "ASUS",        "A8N-LA (Nagami-GL8E)",  0,   OK, nvidia_mcp_gpio0_raise},
++	{0x10DE, 0x005E, 0x1043, 0x815A,  0x10DE, 0x0054, 0x1043, 0x815A, NULL,          NULL,         NULL,          "ASUS",        "A8N",                   0,   NT, board_shuttle_fn25}, /* TODO: This should probably be A8N-SLI Deluxe, see http://www.coreboot.org/pipermail/flashrom/2009-November/000878.html. */
++	{0x10de, 0x0264, 0x1043, 0x81bc,  0x10de, 0x02f0, 0x1043, 0x81cd, NULL,          NULL,         NULL,          "ASUS",        "A8N-VM CSM",            0,   NT, w83627ehf_gpio24_raise_2e},
+ 	{0x10DE, 0x0264, 0x1043, 0x81C0,  0x10DE, 0x0260, 0x1043, 0x81C0, NULL,          NULL,         NULL,          "ASUS",        "M2NBP-VM CSM",          0,   OK, nvidia_mcp_gpio0_raise},
+ 	{0x1106, 0x1336, 0x1043, 0x80ed,  0x1106, 0x3288, 0x1043, 0x8249, NULL,          NULL,         NULL,          "ASUS",        "M2V-MX",                0,   OK, via_vt823x_gpio5_raise},
+-	{0x8086, 0x1a30, 0x1043, 0x8070,  0x8086, 0x244b, 0x1043, 0x8028, NULL,          NULL,         NULL,          "ASUS",        "P4B266",                0,   OK, intel_ich_gpio22_raise},
++	{0x8086, 0x7190,      0,      0,  0x8086, 0x7110,      0,      0, "^P2B-N$",     NULL,         NULL,          "ASUS",        "P2B-N",                 0,   OK, intel_piix4_gpo18_lower},
+ 	{0x8086, 0x1A30, 0x1043, 0x8025,  0x8086, 0x244B, 0x104D, 0x80F0, NULL,          NULL,         NULL,          "ASUS",        "P4B266-LM",             0,   OK, intel_ich_gpio21_raise},
++	{0x8086, 0x1a30, 0x1043, 0x8070,  0x8086, 0x244b, 0x1043, 0x8028, NULL,          NULL,         NULL,          "ASUS",        "P4B266",                0,   OK, intel_ich_gpio22_raise},
+ 	{0x8086, 0x1A30, 0x1043, 0x8088,  0x8086, 0x24C3, 0x1043, 0x8089, NULL,          NULL,         NULL,          "ASUS",        "P4B533-E",              0,   NT, intel_ich_gpio22_raise},
+ 	{0x8086, 0x24D3, 0x1043, 0x80A6,  0x8086, 0x2578, 0x1043, 0x80F6, NULL,          NULL,         NULL,          "ASUS",        "P4C800-E Deluxe",       0,   OK, intel_ich_gpio21_raise},
++	{0x8086, 0x2570, 0x1043, 0x80F2,  0x8086, 0x24D5, 0x1043, 0x80F3, NULL,          NULL,         NULL,          "ASUS",        "P4P800",                0,   NT, intel_ich_gpio21_raise},
+ 	{0x8086, 0x2570, 0x1043, 0x80F2,  0x105A, 0x3373, 0x1043, 0x80F5, NULL,          NULL,         NULL,          "ASUS",        "P4P800-E Deluxe",       0,   OK, intel_ich_gpio21_raise},
++	{0x8086, 0x2570, 0x1043, 0x80A5,  0x105A, 0x24D3, 0x1043, 0x80A6, NULL,          NULL,         NULL,          "ASUS",        "P4SD-LA",               0,   NT, intel_ich_gpio32_raise},
++	{0x1039, 0x0661, 0x1043, 0x8113,  0x1039, 0x5513, 0x1043, 0x8087, NULL,          NULL,         NULL,          "ASUS",        "P4S800-MX",             512, OK, w836xx_memw_enable_2e},
+ 	{0x10B9, 0x1541,      0,      0,  0x10B9, 0x1533,      0,      0, "^P5A$",       "asus",       "p5a",         "ASUS",        "P5A",                   0,   OK, board_asus_p5a},
++	{0x8086, 0x266a, 0x1043, 0x80a6,  0x8086, 0x2668, 0x1043, 0x814e, NULL,          NULL,         NULL,          "ASUS",        "P5GD1 Pro",             0,   OK, intel_ich_gpio21_raise},
+ 	{0x10DE, 0x0030, 0x1043, 0x818a,  0x8086, 0x100E, 0x1043, 0x80EE, NULL,          NULL,         NULL,          "ASUS",        "P5ND2-SLI Deluxe",      0,   OK, nvidia_mcp_gpio10_raise},
+-	{0x1106, 0x3149, 0x1565, 0x3206,  0x1106, 0x3344, 0x1565, 0x1202, NULL,          NULL,         NULL,          "Biostar",     "P4M80-M4",              0,   OK, it8705f_write_enable_2e},
++	{0x8086, 0x24dd, 0x1043, 0x80a6,  0x8086, 0x2570, 0x1043, 0x8157, NULL,          NULL,         NULL,          "ASUS",        "P5PE-VM",               0,   OK, intel_ich_gpio21_raise},
+ 	{0x8086, 0x3590, 0x1028, 0x016c,  0x1000, 0x0030, 0x1028, 0x016c, NULL,          NULL,         NULL,          "Dell",        "PowerEdge 1850",        0,   OK, intel_ich_gpio23_raise},
+-	{0x1039, 0x5513, 0x1019, 0x0A41,  0x1039, 0x0018,      0,      0, NULL,          NULL,         NULL,          "Elitegroup",  "K7S6A",                 0,   OK, it8705f_write_enable_2e},
+-	{0x1106, 0x3038, 0x1019, 0x0996,  0x1106, 0x3177, 0x1019, 0x0996, NULL,          NULL,         NULL,          "Elitegroup",  "K7VTA3",                256, OK, it8705f_write_enable_2e},
++	{0x1106, 0x3038, 0x1019, 0x0996,  0x1106, 0x3177, 0x1019, 0x0996, NULL,          NULL,         NULL,          "Elitegroup",  "K7VTA3",                256, OK, NULL},
+ 	{0x1106, 0x3177, 0x1106, 0x3177,  0x1106, 0x3059, 0x1695, 0x3005, NULL,          NULL,         NULL,          "EPoX",        "EP-8K5A2",              0,   OK, w836xx_memw_enable_2e},
+ 	{0x10EC, 0x8139, 0x1695, 0x9001,  0x11C1, 0x5811, 0x1695, 0x9015, NULL,          NULL,         NULL,          "EPoX",        "EP-8RDA3+",             0,   OK, nvidia_mcp_gpio31_raise},
+-	{0x8086, 0x7110,      0,      0,  0x8086, 0x7190,      0,      0, NULL,          "epox",       "ep-bx3",      "EPoX",        "EP-BX3",                0,   OK, board_epox_ep_bx3},
++	{0x8086, 0x7110,      0,      0,  0x8086, 0x7190,      0,      0, NULL,          "epox",       "ep-bx3",      "EPoX",        "EP-BX3",                0,   NT, intel_piix4_gpo22_raise},
+ 	{0x1106, 0x0686, 0x1106, 0x0686,  0x1106, 0x3058, 0x1458, 0xa000, NULL,          NULL,         NULL,          "GIGABYTE",    "GA-7ZM",                512, OK, NULL},
+-	{0x1106, 0x3227, 0x1458, 0x5001,  0x10ec, 0x8139, 0x1458, 0xe000, NULL,          NULL,         NULL,          "GIGABYTE",    "GA-7VT600",             0,   OK, it8705f_write_enable_2e},
++	{0x8086, 0x244b, 0x8086, 0x2442,  0x8086, 0x2445, 0x1458, 0xa002, NULL,          NULL,         NULL,          "GIGABYTE",    "GA-8IRML",              0,   OK, intel_ich_gpio25_raise},
++	{0x8086, 0x24c3, 0x1458, 0x24c2,  0x8086, 0x24cd, 0x1458, 0x5004, NULL,          NULL,         NULL,          "GIGABYTE",    "GA-8PE667 Ultra 2",     0,   OK, intel_ich_gpio32_raise},
++	{0x10DE, 0x026C, 0x1458, 0xA102,  0x10DE, 0x0260, 0x1458, 0x5001, NULL,          NULL,         NULL,          "GIGABYTE",    "GA-K8N51GMF-9",         0,   OK, nvidia_mcp_gpio3b_raise},
+ 	{0x10DE, 0x0050, 0x1458, 0x0C11,  0x10DE, 0x005e, 0x1458, 0x5000, NULL,          NULL,         NULL,          "GIGABYTE",    "GA-K8N-SLI",            0,   OK, nvidia_mcp_gpio21_raise},
+-	{0x1166, 0x0223, 0x103c, 0x320d,  0x102b, 0x0522, 0x103c, 0x31fa, NULL,          "hp",         "dl145_g3",    "HP",          "DL145 G3",              0,   OK, board_hp_dl145_g3_enable},
++	{0x1166, 0x0223, 0x103c, 0x320d,  0x14e4, 0x1678, 0x103c, 0x703e, NULL,          "hp",         "dl145_g3",    "HP",          "ProLiant DL145 G3",     0,   OK, board_hp_dl145_g3_enable},
++	{0x1166, 0x0223, 0x103c, 0x320d,  0x14e4, 0x1648, 0x103c, 0x310f, NULL,          "hp",         "dl165_g6",    "HP",          "ProLiant DL165 G6",     0,   OK, board_hp_dl165_g6_enable},
++	{0x8086, 0x2580, 0x103c, 0x2a08,  0x8086, 0x2640, 0x103c, 0x2a0a, NULL,          NULL,         NULL,          "HP",          "Puffer2-UL8E",          0,   OK, intel_ich_gpio18_raise},
+ 	{0x8086, 0x2415, 0x103c, 0x1249,  0x10b7, 0x9200, 0x103c, 0x1246, NULL,          NULL,         NULL,          "HP",          "Vectra VL400",          0,   OK, board_hp_vl400}, 
+-	{0x8086, 0x1a30, 0x103c, 0x1a30,  0x8086, 0x2443, 0x103c, 0x2440, "^VL420$",     NULL,         NULL,          "HP",          "VL420 SFF",             0,   OK, intel_ich_gpio22_raise},
+-	{0x8086, 0x27A0,      0,      0,  0x8086, 0x27B9,      0,      0, NULL,          "ibase",      "mb899",       "iBASE",       "MB899",                 0,   NT, intel_ich_gpio26_raise},
++	{0x8086, 0x1a30, 0x103c, 0x1a30,  0x8086, 0x2443, 0x103c, 0x2440, "^VL420$",     NULL,         NULL,          "HP",          "Vectra VL420 SFF",      0,   OK, intel_ich_gpio22_raise},
++	{0x10de, 0x0369, 0x103c, 0x12fe,  0x10de, 0x0364, 0x103c, 0x12fe, NULL,          NULL,         NULL,          "HP",          "xw9400",                0,   OK, nvidia_mcp_gpio5_raise},
++	{0x8086, 0x27A0,      0,      0,  0x8086, 0x27B9,      0,      0, NULL,          "ibase",      "mb899",       "IBASE",       "MB899",                 0,   OK, intel_ich_gpio26_raise},
+ 	{0x1166, 0x0205, 0x1014, 0x0347,  0x1002, 0x515E, 0x1014, 0x0325, NULL,          NULL,         NULL,          "IBM",         "x3455",                 0,   OK, board_ibm_x3455},
+ 	{0x1039, 0x5513, 0x8086, 0xd61f,  0x1039, 0x6330, 0x8086, 0xd61f, NULL,          NULL,         NULL,          "Intel",       "D201GLY",               0,   OK, wbsio_check_for_spi},
+ 	{0x8086, 0x7190,      0,      0,  0x8086, 0x7110,      0,      0, "^SE440BX-2$", NULL,         NULL,          "Intel",       "SE440BX-2",             0,   NT, intel_piix4_gpo27_lower},
+ 	{0x1022, 0x7468,      0,      0,       0,      0,      0,      0, NULL,          "iwill",      "dk8_htx",     "IWILL",       "DK8-HTX",               0,   OK, w83627hf_gpio24_raise_2e},
+ 	{0x8086, 0x27A0, 0x8086, 0x27a0,  0x8086, 0x27b8, 0x8086, 0x27b8, NULL,          "kontron",    "986lcd-m",    "Kontron",     "986LCD-M",              0,   OK, board_kontron_986lcd_m},
+ 	{0x8086, 0x2411, 0x8086, 0x2411,  0x8086, 0x7125, 0x0e11, 0xb165, NULL,          NULL,         NULL,          "Mitac",       "6513WU",                0,   OK, board_mitac_6513wu},
++	{0x10DE, 0x005E, 0x1462, 0x7125,  0x10DE, 0x0052, 0x1462, 0x7125, NULL,          NULL,         NULL,          "MSI",         "K8N Neo4-F",            0,   OK, nvidia_mcp_gpio2_raise}, /* TODO: Should probably be K8N Neo4 Platinum, see http://www.coreboot.org/pipermail/flashrom/2010-August/004362.html. */
++	{0x1039, 0x0745,      0,      0,  0x1039, 0x0018,      0,      0, "^MS-6561",    NULL,         NULL,          "MSI",         "MS-6561 (745 Ultra)",   0,   OK, w836xx_memw_enable_2e},
++	{0x8086, 0x2560, 0x1462, 0x5770,  0x8086, 0x2562, 0x1462, 0x5778, NULL,          NULL,         NULL,          "MSI",         "MS-6577 (Xenon)",       0,   OK, w83627hf_gpio25_raise_2e},
+ 	{0x13f6, 0x0111, 0x1462, 0x5900,  0x1106, 0x3177, 0x1106,      0, NULL,          NULL,         NULL,          "MSI",         "MS-6590 (KT4 Ultra)",   0,   OK, board_msi_kt4v},
+-	{0x1106, 0x3149, 0x1462, 0x7094,  0x10ec, 0x8167, 0x1462, 0x094c, NULL,          NULL,         NULL,          "MSI",         "MS-6702E (K8T Neo2-F)", 0,   OK, w83627thf_gpio4_4_raise_2e},
++	{0x1106, 0x3149, 0x1462, 0x7094,  0x10ec, 0x8167, 0x1462, 0x094c, NULL,          NULL,         NULL,          "MSI",         "MS-6702E (K8T Neo2-F)", 0,   OK, w83627thf_gpio44_raise_2e},
+ 	{0x1106, 0x0571, 0x1462, 0x7120,  0x1106, 0x3065, 0x1462, 0x7120, NULL,          NULL,         NULL,          "MSI",         "MS-6712 (KT4V)",        0,   OK, board_msi_kt4v},
++	{0x1106, 0x3148, 0     , 0     ,  0x1106, 0x3177, 0     , 0     , NULL,          "msi",        "ms6787",      "MSI",         "MS-6787 (P4MAM-V/P4MAM-L)", 0,   NT, w836xx_memw_enable_2e},
+ 	{0x1039, 0x7012, 0x1462, 0x0050,  0x1039, 0x6325, 0x1462, 0x0058, NULL,          NULL,         NULL,          "MSI",         "MS-7005 (651M-L)",      0,   OK, board_msi_651ml},
++	{0x10DE, 0x00E0, 0x1462, 0x0250,  0x10DE, 0x00E1, 0x1462, 0x0250, NULL,          NULL,         NULL,          "MSI",         "MS-7025 (K8N Neo2 Platinum)", 0,   OK, nvidia_mcp_gpio0c_raise},
+ 	{0x8086, 0x2658, 0x1462, 0x7046,  0x1106, 0x3044, 0x1462, 0x046d, NULL,          NULL,         NULL,          "MSI",         "MS-7046",               0,   OK, intel_ich_gpio19_raise},
+-	{0x10DE, 0x005E, 0x1462, 0x7135,  0x10DE, 0x0050, 0x1462, 0x7135, NULL,          "msi",        "k8n-neo3",    "MSI",         "MS-7135 (K8N Neo3)",    0,   OK, w83627thf_gpio4_4_raise_4e},
+-	{0x10DE, 0x0270, 0x1462, 0x7207,  0x10DE, 0x0264, 0x1462, 0x7207, NULL,          NULL,         NULL,          "MSI",         "MS-7207 (K8N GM2-L)",   0,   NT, nvidia_mcp_gpio2_raise},
+-	{0x10DE, 0x005E, 0x1462, 0x7125,  0x10DE, 0x0052, 0x1462, 0x7125, NULL,          NULL,         NULL,          "MSI",         "K8N Neo4-F",            0,   OK, nvidia_mcp_gpio2_raise},
++	{0x1106, 0x3149, 0x1462, 0x7061,  0x1106, 0x3227,      0,      0, NULL,          NULL,         NULL,          "MSI",         "MS-7061 (KM4M-V/KM4AM-V)", 0,   OK, w836xx_memw_enable_2e},
++	{0x10DE, 0x005E, 0x1462, 0x7135,  0x10DE, 0x0050, 0x1462, 0x7135, NULL,          "msi",        "k8n-neo3",    "MSI",         "MS-7135 (K8N Neo3)",    0,   OK, w83627thf_gpio44_raise_4e},
++	{0x10DE, 0x0270, 0x1462, 0x7207,  0x10DE, 0x0264, 0x1462, 0x7207, NULL,          NULL,         NULL,          "MSI",         "MS-7207 (K8NGM2-L)",    0,   NT, nvidia_mcp_gpio2_raise},
++	{0x1011, 0x0019, 0xaa55, 0xaa55,  0x8086, 0x7190,      0,      0, NULL,          NULL,         NULL,          "Nokia",       "IP530",                 0,   OK, fdc37b787_gpio50_raise_3f0},
++	{0x8086, 0x24d3, 0x144d, 0xb025,  0x8086, 0x1050, 0x144d, 0xb025, NULL,          NULL,         NULL,          "Samsung",     "Polaris 32",            0,   OK, intel_ich_gpio21_raise},
+ 	{0x1106, 0x3099,      0,      0,  0x1106, 0x3074,      0,      0, NULL,          "shuttle",    "ak31",        "Shuttle",     "AK31",                  0,   OK, w836xx_memw_enable_2e},
+-	{0x1106, 0x3104, 0x1297, 0xa238,  0x1106, 0x3059, 0x1297, 0xc063, NULL,          NULL,         NULL,          "Shuttle",     "AK38N",                 256, OK, it8705f_write_enable_2e},
++	{0x1106, 0x3104, 0x1297, 0xa238,  0x1106, 0x3059, 0x1297, 0xc063, NULL,          NULL,         NULL,          "Shuttle",     "AK38N",                 256, OK, NULL},
+ 	{0x10DE, 0x0050, 0x1297, 0x5036,  0x1412, 0x1724, 0x1297, 0x5036, NULL,          NULL,         NULL,          "Shuttle",     "FN25",                  0,   OK, board_shuttle_fn25},
+ 	{0x1106, 0x3038, 0x0925, 0x1234,  0x1106, 0x3058, 0x15DD, 0x7609, NULL,          NULL,         NULL,          "Soyo",        "SY-7VCA",               0,   OK, via_apollo_gpo0_lower},
+-	{0x8086, 0x1076, 0x8086, 0x1176,  0x1106, 0x3059, 0x10f1, 0x2498, NULL,          NULL,         NULL,          "Tyan",        "S2498 (Tomcat K7M)",    0,   OK, w836xx_memw_enable_2e},
+ 	{0x1106, 0x3038, 0x0925, 0x1234,  0x1106, 0x0596, 0x1106,      0, NULL,          NULL,         NULL,          "Tekram",      "P6Pro-A5",              256, OK, NULL},
+ 	{0x1106, 0x3123, 0x1106, 0x3123,  0x1106, 0x3059, 0x1106, 0x4161, NULL,          NULL,         NULL,          "Termtek",     "TK-3370 (Rev:2.5B)",    0,   OK, w836xx_memw_enable_4e},
++	{0x8086, 0x1076, 0x8086, 0x1176,  0x1106, 0x3059, 0x10f1, 0x2498, NULL,          NULL,         NULL,          "Tyan",        "S2498 (Tomcat K7M)",    0,   OK, w836xx_memw_enable_2e},
++	{0x1106, 0x0259, 0x1106, 0xAA07,  0x1106, 0x3227, 0x1106, 0xAA07, NULL,          NULL,         NULL,          "VIA",         "EPIA EK",               0,   NT, via_vt823x_gpio9_raise},
+ 	{0x1106, 0x3177, 0x1106, 0xAA01,  0x1106, 0x3123, 0x1106, 0xAA01, NULL,          NULL,         NULL,          "VIA",         "EPIA M/MII/...",        0,   OK, via_vt823x_gpio15_raise},
+ 	{0x1106, 0x0259, 0x1106, 0x3227,  0x1106, 0x3065, 0x1106, 0x3149, NULL,          NULL,         NULL,          "VIA",         "EPIA-N/NL",             0,   OK, via_vt823x_gpio9_raise},
+-
++#endif
+ 	{     0,      0,      0,      0,       0,      0,      0,      0, NULL,          NULL,         NULL,          NULL,          NULL,                    0,   NT, NULL}, /* end marker */
+ };
+ 
+-/**
++/*
+  * Match boards on coreboot table gathered vendor and part name.
+  * Require main PCI IDs to match too as extra safety.
+  */
+-static struct board_pciid_enable *board_match_coreboot_name(const char *vendor,
++static const struct board_pciid_enable *board_match_coreboot_name(const char *vendor,
+ 							    const char *part)
+ {
+-	struct board_pciid_enable *board = board_pciid_enables;
+-	struct board_pciid_enable *partmatch = NULL;
++	const struct board_pciid_enable *board = board_pciid_enables;
++	const struct board_pciid_enable *partmatch = NULL;
+ 
+ 	for (; board->vendor_name; board++) {
+ 		if (vendor && (!board->lb_vendor
+@@ -1470,13 +1919,13 @@ static struct board_pciid_enable *board_match_coreboot_name(const char *vendor,
+ 	return NULL;
+ }
+ 
+-/**
++/*
+  * Match boards on PCI IDs and subsystem IDs.
+  * Second set of IDs can be main only or missing completely.
+  */
+-static struct board_pciid_enable *board_match_pci_card_ids(void)
++const static struct board_pciid_enable *board_match_pci_card_ids(void)
+ {
+-	struct board_pciid_enable *board = board_pciid_enables;
++	const struct board_pciid_enable *board = board_pciid_enables;
+ 
+ 	for (; board->vendor_name; board++) {
+ 		if ((!board->first_card_vendor || !board->first_card_device) &&
+@@ -1522,7 +1971,7 @@ static struct board_pciid_enable *board_match_pci_card_ids(void)
+ 
+ int board_flash_enable(const char *vendor, const char *part)
+ {
+-	struct board_pciid_enable *board = NULL;
++	const struct board_pciid_enable *board = NULL;
+ 	int ret = 0;
+ 
+ 	if (part)
+@@ -1531,19 +1980,19 @@ int board_flash_enable(const char *vendor, const char *part)
+ 	if (!board)
+ 		board = board_match_pci_card_ids();
+ 
+-        if (board && board->status == NT) {
+-                if (!force_boardenable) {
+-                        msg_pinfo("WARNING: Your mainboard is %s %s, but the mainboard-specific\n"
+-                               "code has not been tested, and thus will not not be executed by default.\n"
+-                               "Depending on your hardware environment, erasing, writing or even probing\n"
+-                               "can fail without running the board specific code.\n\n"
+-                               "Please see the man page (section PROGRAMMER SPECIFIC INFO, subsection\n"
+-                               "\"internal programmer\") for details.\n",
+-                               board->vendor_name, board->board_name);
+-                        board = NULL;
+-                } else {
+-                        msg_pinfo("NOTE: Running an untested board enable procedure.\n"
+-                               "Please report success/failure to flashrom at flashrom.org.\n");
++	if (board && board->status == NT) {
++		if (!force_boardenable) {
++			msg_pinfo("WARNING: Your mainboard is %s %s, but the mainboard-specific\n"
++			       "code has not been tested, and thus will not not be executed by default.\n"
++			       "Depending on your hardware environment, erasing, writing or even probing\n"
++			       "can fail without running the board specific code.\n\n"
++			       "Please see the man page (section PROGRAMMER SPECIFIC INFO, subsection\n"
++			       "\"internal programmer\") for details.\n",
++			       board->vendor_name, board->board_name);
++		board = NULL;
++		} else {
++		        msg_pinfo("NOTE: Running an untested board enable procedure.\n"
++		               "Please report success/failure to flashrom at flashrom.org.\n");
+ 		}
+         }
+ 
+@@ -1554,10 +2003,10 @@ int board_flash_enable(const char *vendor, const char *part)
+ 
+ 		if (board->enable != NULL) {
+ 			msg_pinfo("Disabling flash write protection for "
+-			       "board \"%s %s\"... ", board->vendor_name,
+-			       board->board_name);
++				  "board \"%s %s\"... ", board->vendor_name,
++				  board->board_name);
+ 
+-			ret = board->enable(board->vendor_name);
++			ret = board->enable();
+ 			if (ret)
+ 				msg_pinfo("FAILED!\n");
+ 			else
+diff --git a/buspirate_spi.c b/buspirate_spi.c
+index dc491e2..c56155f 100644
+--- a/buspirate_spi.c
++++ b/buspirate_spi.c
+@@ -1,7 +1,7 @@
+ /*
+  * This file is part of the flashrom project.
+  *
+- * Copyright (C) 2009 Carl-Daniel Hailfinger
++ * 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
+@@ -24,13 +24,14 @@
+ #include <ctype.h>
+ #include "flash.h"
+ #include "chipdrivers.h"
++#include "programmer.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)
++static int buspirate_serialport_setup(char *dev)
+ {
+ 	/* 115200bps, 8 databits, no parity, 1 stopbit */
+ 	sp_fd = sp_openserport(dev, 115200);
+@@ -44,7 +45,7 @@ int buspirate_serialport_setup(char *dev)
+ #define sp_flush_incoming(...) 0
+ #endif
+ 
+-int buspirate_sendrecv(unsigned char *buf, unsigned int writecnt, unsigned int readcnt)
++static int buspirate_sendrecv(unsigned char *buf, unsigned int writecnt, unsigned int readcnt)
+ {
+ 	int i, ret = 0;
+ 
+@@ -101,24 +102,14 @@ int buspirate_spi_init(void)
+ 	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) {
++	dev = extract_programmer_param("dev");
++	if (!dev || !strlen(dev)) {
+ 		msg_perr("No serial device given. Use flashrom -p "
+-			"buspiratespi:dev=/dev/ttyUSB0\n");
++			"buspirate_spi:dev=/dev/ttyUSB0\n");
+ 		return 1;
+ 	}
++
++	speed = extract_programmer_param("spispeed");
+ 	if (speed) {
+ 		for (i = 0; spispeeds[i].name; i++)
+ 			if (!strncasecmp(spispeeds[i].name, speed,
+@@ -129,12 +120,15 @@ int buspirate_spi_init(void)
+ 		if (!spispeeds[i].name)
+ 			msg_perr("Invalid SPI speed, using default.\n");
+ 	}
++	free(speed);
++
+ 	/* 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;
++	free(dev);
+ 
+ 	/* This is the brute force version, but it should work. */
+ 	for (i = 0; i < 19; i++) {
+@@ -266,8 +260,8 @@ int buspirate_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ 	if (writecnt > 16 || readcnt > 16 || (readcnt + writecnt) > 16)
+ 		return SPI_INVALID_LENGTH;
+ 
+-	/* +2 is pretty arbitrary. */
+-	buf = realloc(buf, writecnt + readcnt + 2);
++	/* 3 bytes extra for CS#, len, CS#. */
++	buf = realloc(buf, writecnt + readcnt + 3);
+ 	if (!buf) {
+ 		msg_perr("Out of memory!\n");
+ 		exit(1); // -1
+@@ -275,39 +269,41 @@ int buspirate_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ 
+ 	/* 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)
++
++	i += readcnt;
++	/* De-assert CS# */
++	buf[i++] = 0x03;
++
++	ret = buspirate_sendrecv(buf, i, i);
++
++	if (ret) {
++		msg_perr("Bus Pirate communication error!\n");
+ 		return SPI_GENERIC_ERROR;
++	}
++
+ 	if (buf[0] != 0x01) {
+-		msg_perr("Protocol error while reading/writing SPI!\n");
++		msg_perr("Protocol error while lowering CS#!\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)
++	if (buf[1] != 0x01) {
++		msg_perr("Protocol error while reading/writing SPI!\n");
+ 		return SPI_GENERIC_ERROR;
+-	if (buf[0] != 0x01) {
++	}
++
++	if (buf[i - 1] != 0x01) {
+ 		msg_perr("Protocol error while raising CS#!\n");
+ 		return SPI_GENERIC_ERROR;
+ 	}
+ 
++	/* Skip CS#, length, writearr. */
++	memcpy(readarr, buf + 2 + writecnt, readcnt);
++
+ 	return ret;
+ }
+ 
+@@ -316,39 +312,7 @@ int buspirate_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len
+ 	return spi_read_chunked(flash, buf, start, len, 12);
+ }
+ 
+-int buspirate_spi_write_256(struct flashchip *flash, uint8_t *buf)
++int buspirate_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
+ {
+-	int total_size = 1024 * flash->total_size;
+-	int i;
+-
+-	spi_disable_blockprotect();
+-	/* Erase first. */
+-	msg_pinfo("Erasing flash before programming... ");
+-	if (erase_flash(flash)) {
+-		msg_perr("ERASE FAILED!\n");
+-		return -1;
+-	}
+-	msg_pinfo("done.\n");
+-
+-	/* FIXME: We could do 12 byte writes, but then we'd have to make sure
+-	 * not to cross a 256 byte page boundary. This problem only applies to
+-	 * writes, reads can cross page boundaries just fine.
+-	 */
+-	for (i = 0; i < total_size; i += 8) {
+-		int l, r;
+-		if (i + 8 <= total_size)
+-			l = 8;
+-		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;
++	return spi_write_chunked(flash, buf, start, len, 12);
+ }
+diff --git a/cbtable.c b/cbtable.c
+index 3e97ed7..8f906d4 100644
+--- a/cbtable.c
++++ b/cbtable.c
+@@ -22,10 +22,13 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
++#include <unistd.h>
++#include <stdio.h>
+ #include <stdlib.h>
+ #include <sys/types.h>
+ #include <string.h>
+ #include "flash.h"
++#include "programmer.h"
+ #include "coreboot_tables.h"
+ 
+ char *lb_part = NULL, *lb_vendor = NULL;
+diff --git a/chipdrivers.h b/chipdrivers.h
+index 6d5cef0..119ea70 100644
+--- a/chipdrivers.h
++++ b/chipdrivers.h
+@@ -29,13 +29,12 @@
+ 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 probe_spi_res1(struct flashchip *flash);
++int probe_spi_res2(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);
+@@ -44,14 +43,27 @@ int spi_block_erase_60(struct flashchip *flash, unsigned int addr, unsigned int
+ 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_write_1_new(struct flashchip *flash, uint8_t *buf, int start, int len);
++int spi_chip_write_256_new(struct flashchip *flash, uint8_t *buf, int start, int len);
+ 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_prettyprint_status_register_at25df(struct flashchip *flash);
++int spi_prettyprint_status_register_at25df_sec(struct flashchip *flash);
++int spi_prettyprint_status_register_at25f(struct flashchip *flash);
++int spi_prettyprint_status_register_at25fs010(struct flashchip *flash);
++int spi_prettyprint_status_register_at25fs040(struct flashchip *flash);
++int spi_disable_blockprotect(struct flashchip *flash);
++int spi_disable_blockprotect_at25df(struct flashchip *flash);
++int spi_disable_blockprotect_at25df_sec(struct flashchip *flash);
++int spi_disable_blockprotect_at25f(struct flashchip *flash);
++int spi_disable_blockprotect_at25fs010(struct flashchip *flash);
++int spi_disable_blockprotect_at25fs040(struct flashchip *flash);
+ 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);
++int spi_write_chunked(struct flashchip *flash, uint8_t *buf, int start, int len, int chunksize);
++int spi_aai_write(struct flashchip *flash, uint8_t *buf, int start, int len);
+ 
+ /* 82802ab.c */
+ uint8_t wait_82802ab(chipaddr bios);
+@@ -85,7 +97,6 @@ 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);
+diff --git a/chipset_enable.c b/chipset_enable.c
+index 69f1623..4ac3a55 100644
+--- a/chipset_enable.c
++++ b/chipset_enable.c
+@@ -30,11 +30,13 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/types.h>
+-#include <sys/stat.h>
+-#include <fcntl.h>
++#include <unistd.h>
+ #include "flash.h"
++#include "programmer.h"
+ 
+-extern int ichspi_lock;
++#define NOT_DONE_YET 1
++
++#if defined(__i386__) || defined(__x86_64__)
+ 
+ static int enable_flash_ali_m1533(struct pci_dev *dev, const char *name)
+ {
+@@ -285,6 +287,7 @@ static int enable_flash_ich_4e(struct pci_dev *dev, const char *name)
+ 	 * FWH_DEC_EN1, but they are called FB_SEL1, FB_SEL2, FB_DEC_EN1 and
+ 	 * FB_DEC_EN2.
+ 	 */
++	buses_supported = CHIP_BUSTYPE_FWH;
+ 	return enable_flash_ich(dev, name, 0x4e);
+ }
+ 
+@@ -298,11 +301,8 @@ static int enable_flash_ich_dc(struct pci_dev *dev, const char *name)
+ 	int max_decode_fwh_decode = 0;
+ 	int contiguous = 1;
+ 
+-	if (programmer_param)
+-		idsel = strstr(programmer_param, "fwh_idsel=");
+-
+-	if (idsel) {
+-		idsel += strlen("fwh_idsel=");
++	idsel = extract_programmer_param("fwh_idsel");
++	if (idsel && strlen(idsel)) {
+ 		fwh_conf = (uint32_t)strtoul(idsel, NULL, 0);
+ 
+ 		/* FIXME: Need to undo this on shutdown. */
+@@ -310,7 +310,15 @@ static int enable_flash_ich_dc(struct pci_dev *dev, const char *name)
+ 		pci_write_long(dev, 0xd0, fwh_conf);
+ 		pci_write_word(dev, 0xd4, fwh_conf);
+ 		/* FIXME: Decode settings are not changed. */
++	} else if (idsel) {
++		msg_perr("Error: idsel= specified, but no number given.\n");
++		free(idsel);
++		/* FIXME: Return failure here once internal_init() starts
++		 * to care about the return value of the chipset enable.
++		 */
++		exit(1);
+ 	}
++	free(idsel);
+ 
+ 	/* Ignore all legacy ranges below 1 MB.
+ 	 * We currently only support flashing the chip which responds to
+@@ -398,6 +406,7 @@ static int enable_flash_poulsbo(struct pci_dev *dev, const char *name)
+        if (new != old)
+                pci_write_byte(dev, 0xd9, new);
+ 
++	buses_supported = CHIP_BUSTYPE_FWH;
+        return 0;
+ }
+ 
+@@ -409,30 +418,15 @@ static int enable_flash_poulsbo(struct pci_dev *dev, const char *name)
+ 
+ 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;
+-	msg_pdbg("MMIO base at = 0x%x\n", mmio_base);
+-	spibar = physmap("VT8237S MMIO registers", mmio_base, 0x70);
+-
+-	msg_pdbg("0x6c: 0x%04x     (CLOCK/DEBUG)\n",
+-		     mmio_readw(spibar + 0x6c));
+-
+-	/* Not sure if it speaks all these bus protocols. */
+-	buses_supported = CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI;
+-	spi_controller = SPI_CONTROLLER_VIA;
+-	ich_init_opcodes();
+-
+-	return 0;
++	return via_init_spi(dev);
+ }
+ 
+ static int enable_flash_ich_dc_spi(struct pci_dev *dev, const char *name,
+ 				   int ich_generation)
+ {
+-	int ret, i;
+-	uint8_t old, new, bbs, buc;
+-	uint16_t spibar_offset, tmp2;
++	int ret;
++	uint8_t bbs, buc;
+ 	uint32_t tmp, gcs;
+ 	void *rcrb;
+ 	//TODO: These names are incorrect for EP80579. For that, the solution would look like the commented line
+@@ -465,156 +459,22 @@ static int enable_flash_ich_dc_spi(struct pci_dev *dev, const char *name,
+ 	 * on ICH7 when the southbridge is strapped to LPC
+ 	 */
+ 
+-	if (ich_generation == 7 && bbs == ICH_STRAP_LPC) {
+-		buses_supported = CHIP_BUSTYPE_FWH;
+-		/* No further SPI initialization required */
+-		return ret;
+-	}
+-
+-	switch (ich_generation) {
+-	case 7:
+-		buses_supported = CHIP_BUSTYPE_SPI;
+-		spi_controller = SPI_CONTROLLER_ICH7;
+-		spibar_offset = 0x3020;
+-		break;
+-	case 8:
+-		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 */
+-		buses_supported = CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI;
+-		spi_controller = SPI_CONTROLLER_ICH9;
+-		spibar_offset = 0x3800;
+-		break;
+-	}
+-
+-	/* SPIBAR is at RCRB+0x3020 for ICH[78] and RCRB+0x3800 for ICH9. */
+-	msg_pdbg("SPIBAR = 0x%x + 0x%04x\n", tmp, spibar_offset);
+-
+-	/* Assign Virtual Address */
+-	spibar = rcrb + spibar_offset;
+-
+-	switch (spi_controller) {
+-	case SPI_CONTROLLER_ICH7:
+-		msg_pdbg("0x00: 0x%04x     (SPIS)\n",
+-			     mmio_readw(spibar + 0));
+-		msg_pdbg("0x02: 0x%04x     (SPIC)\n",
+-			     mmio_readw(spibar + 2));
+-		msg_pdbg("0x04: 0x%08x (SPIA)\n",
+-			     mmio_readl(spibar + 4));
+-		for (i = 0; i < 8; i++) {
+-			int offs;
+-			offs = 8 + (i * 8);
+-			msg_pdbg("0x%02x: 0x%08x (SPID%d)\n", offs,
+-				     mmio_readl(spibar + offs), i);
+-			msg_pdbg("0x%02x: 0x%08x (SPID%d+4)\n", offs + 4,
+-				     mmio_readl(spibar + offs + 4), i);
+-		}
+-		msg_pdbg("0x50: 0x%08x (BBAR)\n",
+-			     mmio_readl(spibar + 0x50));
+-		msg_pdbg("0x54: 0x%04x     (PREOP)\n",
+-			     mmio_readw(spibar + 0x54));
+-		msg_pdbg("0x56: 0x%04x     (OPTYPE)\n",
+-			     mmio_readw(spibar + 0x56));
+-		msg_pdbg("0x58: 0x%08x (OPMENU)\n",
+-			     mmio_readl(spibar + 0x58));
+-		msg_pdbg("0x5c: 0x%08x (OPMENU+4)\n",
+-			     mmio_readl(spibar + 0x5c));
+-		for (i = 0; i < 4; i++) {
+-			int offs;
+-			offs = 0x60 + (i * 4);
+-			msg_pdbg("0x%02x: 0x%08x (PBR%d)\n", offs,
+-				     mmio_readl(spibar + offs), i);
+-		}
+-		msg_pdbg("\n");
+-		if (mmio_readw(spibar) & (1 << 15)) {
+-			msg_pinfo("WARNING: SPI Configuration Lockdown activated.\n");
+-			ichspi_lock = 1;
+-		}
+-		ich_init_opcodes();
+-		break;
+-	case SPI_CONTROLLER_ICH9:
+-		tmp2 = mmio_readw(spibar + 4);
+-		msg_pdbg("0x04: 0x%04x (HSFS)\n", tmp2);
+-		msg_pdbg("FLOCKDN %i, ", (tmp2 >> 15 & 1));
+-		msg_pdbg("FDV %i, ", (tmp2 >> 14) & 1);
+-		msg_pdbg("FDOPSS %i, ", (tmp2 >> 13) & 1);
+-		msg_pdbg("SCIP %i, ", (tmp2 >> 5) & 1);
+-		msg_pdbg("BERASE %i, ", (tmp2 >> 3) & 3);
+-		msg_pdbg("AEL %i, ", (tmp2 >> 2) & 1);
+-		msg_pdbg("FCERR %i, ", (tmp2 >> 1) & 1);
+-		msg_pdbg("FDONE %i\n", (tmp2 >> 0) & 1);
+-
+-		tmp = mmio_readl(spibar + 0x50);
+-		msg_pdbg("0x50: 0x%08x (FRAP)\n", tmp);
+-		msg_pdbg("BMWAG %i, ", (tmp >> 24) & 0xff);
+-		msg_pdbg("BMRAG %i, ", (tmp >> 16) & 0xff);
+-		msg_pdbg("BRWA %i, ", (tmp >> 8) & 0xff);
+-		msg_pdbg("BRRA %i\n", (tmp >> 0) & 0xff);
+-
+-		msg_pdbg("0x54: 0x%08x (FREG0)\n",
+-			     mmio_readl(spibar + 0x54));
+-		msg_pdbg("0x58: 0x%08x (FREG1)\n",
+-			     mmio_readl(spibar + 0x58));
+-		msg_pdbg("0x5C: 0x%08x (FREG2)\n",
+-			     mmio_readl(spibar + 0x5C));
+-		msg_pdbg("0x60: 0x%08x (FREG3)\n",
+-			     mmio_readl(spibar + 0x60));
+-		msg_pdbg("0x64: 0x%08x (FREG4)\n",
+-			     mmio_readl(spibar + 0x64));
+-		msg_pdbg("0x74: 0x%08x (PR0)\n",
+-			     mmio_readl(spibar + 0x74));
+-		msg_pdbg("0x78: 0x%08x (PR1)\n",
+-			     mmio_readl(spibar + 0x78));
+-		msg_pdbg("0x7C: 0x%08x (PR2)\n",
+-			     mmio_readl(spibar + 0x7C));
+-		msg_pdbg("0x80: 0x%08x (PR3)\n",
+-			     mmio_readl(spibar + 0x80));
+-		msg_pdbg("0x84: 0x%08x (PR4)\n",
+-			     mmio_readl(spibar + 0x84));
+-		msg_pdbg("0x90: 0x%08x (SSFS, SSFC)\n",
+-			     mmio_readl(spibar + 0x90));
+-		msg_pdbg("0x94: 0x%04x     (PREOP)\n",
+-			     mmio_readw(spibar + 0x94));
+-		msg_pdbg("0x96: 0x%04x     (OPTYPE)\n",
+-			     mmio_readw(spibar + 0x96));
+-		msg_pdbg("0x98: 0x%08x (OPMENU)\n",
+-			     mmio_readl(spibar + 0x98));
+-		msg_pdbg("0x9C: 0x%08x (OPMENU+4)\n",
+-			     mmio_readl(spibar + 0x9C));
+-		msg_pdbg("0xA0: 0x%08x (BBAR)\n",
+-			     mmio_readl(spibar + 0xA0));
+-		msg_pdbg("0xB0: 0x%08x (FDOC)\n",
+-			     mmio_readl(spibar + 0xB0));
+-		if (tmp2 & (1 << 15)) {
+-			msg_pinfo("WARNING: SPI Configuration Lockdown activated.\n");
+-			ichspi_lock = 1;
++	buses_supported = CHIP_BUSTYPE_FWH;
++	if (ich_generation == 7) {
++		if(bbs == ICH_STRAP_LPC) {
++			/* No further SPI initialization required */
++			return ret;
+ 		}
+-		ich_init_opcodes();
+-		break;
+-	default:
+-		/* Nothing */
+-		break;
++		else
++			/* Disable LPC/FWH if strapped to PCI or SPI */
++			buses_supported = 0;
+ 	}
+ 
+-	old = pci_read_byte(dev, 0xdc);
+-	msg_pdbg("SPI Read Configuration: ");
+-	new = (old >> 2) & 0x3;
+-	switch (new) {
+-	case 0:
+-	case 1:
+-	case 2:
+-		msg_pdbg("prefetching %sabled, caching %sabled, ",
+-			     (new & 0x2) ? "en" : "dis",
+-			     (new & 0x1) ? "dis" : "en");
+-		break;
+-	default:
+-		msg_pdbg("invalid prefetching/caching settings, ");
+-		break;
+-	}
++	/* this adds CHIP_BUSTYPE_SPI */
++	if (ich_init_spi(dev, tmp, rcrb, ich_generation) != 0) {
++	        if (!ret)
++		        ret = ERROR_NONFATAL;
++        }
+ 
+ 	return ret;
+ }
+@@ -639,6 +499,32 @@ static int enable_flash_ich10(struct pci_dev *dev, const char *name)
+ 	return enable_flash_ich_dc_spi(dev, name, 10);
+ }
+ 
++static void via_do_byte_merge(void * arg)
++{
++	struct pci_dev * dev = arg;
++	uint8_t val;
++
++	msg_pdbg("Re-enabling byte merging\n");
++	val = pci_read_byte(dev, 0x71);
++	val |= 0x40;
++	pci_write_byte(dev, 0x71, val);
++}
++
++static int via_no_byte_merge(struct pci_dev *dev, const char *name)
++{
++	uint8_t val;
++
++	val = pci_read_byte(dev, 0x71);
++	if (val & 0x40)
++	{
++		msg_pdbg("Disabling byte merging\n");
++		val &= ~0x40;
++		pci_write_byte(dev, 0x71, val);
++		register_shutdown(via_do_byte_merge, dev);
++	}
++	return NOT_DONE_YET;	/* need to find south bridge, too */
++}
++
+ static int enable_flash_vt823x(struct pci_dev *dev, const char *name)
+ {
+ 	uint8_t val;
+@@ -723,7 +609,7 @@ static int enable_flash_cs5530(struct pci_dev *dev, const char *name)
+ 	return 0;
+ }
+ 
+-/**
++/*
+  * Geode systems write protect the BIOS via RCONFs (cache settings similar
+  * to MTRRs). To unlock, change MSR 0x1808 top byte to 0x22. 
+  *
+@@ -808,10 +694,9 @@ static int enable_flash_amd8111(struct pci_dev *dev, const char *name)
+ 
+ static int enable_flash_sb600(struct pci_dev *dev, const char *name)
+ {
+-	uint32_t tmp, prot;
++	uint32_t prot;
+ 	uint8_t reg;
+-	struct pci_dev *smbus_dev;
+-	int has_spi = 1;
++	int ret;
+ 
+ 	/* Clear ROM protect 0-3. */
+ 	for (reg = 0x50; reg < 0x60; reg += 4) {
+@@ -835,80 +720,9 @@ static int enable_flash_sb600(struct pci_dev *dev, const char *name)
+ 				(prot & 0xfffffc00) + ((prot & 0x3ff) << 8));
+ 	}
+ 
+-	/* Read SPI_BaseAddr */
+-	tmp = pci_read_long(dev, 0xa0);
+-	tmp &= 0xffffffe0;	/* remove bits 4-0 (reserved) */
+-	msg_pdbg("SPI base address is at 0x%x\n", tmp);
+-
+-	/* If the BAR has address 0, it is unlikely SPI is used. */
+-	if (!tmp)
+-		has_spi = 0;
+-
+-	if (has_spi) {
+-		/* Physical memory has to be mapped at page (4k) boundaries. */
+-		sb600_spibar = physmap("SB600 SPI registers", tmp & 0xfffff000,
+-				       0x1000);
+-		/* The low bits of the SPI base address are used as offset into
+-		 * the mapped page.
+-		 */
+-		sb600_spibar += tmp & 0xfff;
+-
+-		tmp = pci_read_long(dev, 0xa0);
+-		msg_pdbg("AltSpiCSEnable=%i, SpiRomEnable=%i, "
+-			     "AbortEnable=%i\n", tmp & 0x1, (tmp & 0x2) >> 1,
+-			     (tmp & 0x4) >> 2);
+-		tmp = (pci_read_byte(dev, 0xba) & 0x4) >> 2;
+-		msg_pdbg("PrefetchEnSPIFromIMC=%i, ", tmp);
+-
+-		tmp = pci_read_byte(dev, 0xbb);
+-		msg_pdbg("PrefetchEnSPIFromHost=%i, SpiOpEnInLpcMode=%i\n",
+-			     tmp & 0x1, (tmp & 0x20) >> 5);
+-		tmp = mmio_readl(sb600_spibar);
+-		msg_pdbg("SpiArbEnable=%i, SpiAccessMacRomEn=%i, "
+-			     "SpiHostAccessRomEn=%i, ArbWaitCount=%i, "
+-			     "SpiBridgeDisable=%i, DropOneClkOnRd=%i\n",
+-			     (tmp >> 19) & 0x1, (tmp >> 22) & 0x1,
+-			     (tmp >> 23) & 0x1, (tmp >> 24) & 0x7,
+-			     (tmp >> 27) & 0x1, (tmp >> 28) & 0x1);
+-	}
+-
+-	/* Look for the SMBus device. */
+-	smbus_dev = pci_dev_find(0x1002, 0x4385);
+-
+-	if (has_spi && !smbus_dev) {
+-		msg_perr("ERROR: SMBus device not found. Not enabling SPI.\n");
+-		has_spi = 0;
+-	}
+-	if (has_spi) {
+-		/* Note about the bit tests below: If a bit is zero, the GPIO is SPI. */
+-		/* GPIO11/SPI_DO and GPIO12/SPI_DI status */
+-		reg = pci_read_byte(smbus_dev, 0xAB);
+-		reg &= 0xC0;
+-		msg_pdbg("GPIO11 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_DO");
+-		msg_pdbg("GPIO12 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_DI");
+-		if (reg != 0x00)
+-			has_spi = 0;
+-		/* GPIO31/SPI_HOLD and GPIO32/SPI_CS status */
+-		reg = pci_read_byte(smbus_dev, 0x83);
+-		reg &= 0xC0;
+-		msg_pdbg("GPIO31 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_HOLD");
+-		msg_pdbg("GPIO32 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_CS");
+-		/* SPI_HOLD is not used on all boards, filter it out. */
+-		if ((reg & 0x80) != 0x00)
+-			has_spi = 0;
+-		/* GPIO47/SPI_CLK status */
+-		reg = pci_read_byte(smbus_dev, 0xA7);
+-		reg &= 0x40;
+-		msg_pdbg("GPIO47 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_CLK");
+-		if (reg != 0x00)
+-			has_spi = 0;
+-	}
+-
+ 	buses_supported = CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH;
+-	if (has_spi) {
+-		buses_supported |= CHIP_BUSTYPE_SPI;
+-		spi_controller = SPI_CONTROLLER_SB600;
+-	}
++
++	ret = sb600_probe_spi(dev);
+ 
+ 	/* Read ROM strap override register. */
+ 	OUTB(0x8f, 0xcd6);
+@@ -945,7 +759,7 @@ static int enable_flash_sb600(struct pci_dev *dev, const char *name)
+ 	OUTB(0x0e, 0xcd7);
+ 	*/
+ 
+-	return 0;
++	return ret;
+ }
+ 
+ static int enable_flash_nvidia_nforce2(struct pci_dev *dev, const char *name)
+@@ -1054,18 +868,16 @@ static int enable_flash_mcp55(struct pci_dev *dev, const char *name)
+ 	return 0;
+ }
+ 
+-/* This is a shot in the dark. Even if the code is totally bogus for some
+- * chipsets, users will at least start to send in reports.
++/*
++ * The MCP6x/MCP7x code is based on cleanroom reverse engineering.
++ * It is assumed that LPC chips need the MCP55 code and SPI chips need the
++ * code provided in enable_flash_mcp6x_7x_common.
+  */
+-static int enable_flash_mcp6x_7x_common(struct pci_dev *dev, const char *name)
++static int enable_flash_mcp6x_7x(struct pci_dev *dev, const char *name)
+ {
+ 	int ret = 0;
++	int want_spi = 0;
+ 	uint8_t val;
+-	uint16_t status;
+-	char *busname;
+-	uint32_t mcp_spibaraddr;
+-	void *mcp_spibar;
+-	struct pci_dev *smbusdev;
+ 
+ 	msg_pinfo("This chipset is not really supported yet. Guesswork...\n");
+ 
+@@ -1073,20 +885,31 @@ static int enable_flash_mcp6x_7x_common(struct pci_dev *dev, const char *name)
+ 	val = pci_read_byte(dev, 0x8a);
+ 	msg_pdbg("ISA/LPC bridge reg 0x8a contents: 0x%02x, bit 6 is %i, bit 5 "
+ 		 "is %i\n", val, (val >> 6) & 0x1, (val >> 5) & 0x1);
++
+ 	switch ((val >> 5) & 0x3) {
+ 	case 0x0:
++		ret = enable_flash_mcp55(dev, name);
+ 		buses_supported = CHIP_BUSTYPE_LPC;
++		msg_pdbg("Flash bus type is LPC\n");
+ 		break;
+ 	case 0x2:
+-		buses_supported = CHIP_BUSTYPE_SPI;
++		want_spi = 1;
++		/* SPI is added in mcp6x_spi_init if it works.
++		 * Do we really want to disable LPC in this case?
++		 */
++		buses_supported = CHIP_BUSTYPE_NONE;
++		msg_pdbg("Flash bus type is SPI\n");
++		msg_perr("SPI on this chipset is WIP. Write is unsupported!\n");
++		programmer_may_write = 0;
+ 		break;
+ 	default:
+-		buses_supported = CHIP_BUSTYPE_UNKNOWN;
++		/* Should not happen. */
++		buses_supported = CHIP_BUSTYPE_NONE;
++		msg_pdbg("Flash bus type is unknown (none)\n");
++		msg_pinfo("Something went wrong with bus type detection.\n");
++		goto out_msg;
+ 		break;
+ 	}
+-	busname = flashbuses_to_text(buses_supported);
+-	msg_pdbg("Guessed flash bus type is %s\n", busname);
+-	free(busname);
+ 
+ 	/* Force enable SPI and disable LPC? Not a good idea. */
+ #if 0
+@@ -1095,62 +918,8 @@ static int enable_flash_mcp6x_7x_common(struct pci_dev *dev, const char *name)
+ 	pci_write_byte(dev, 0x8a, val);
+ #endif
+ 
+-	/* Look for the SMBus device (SMBus PCI class) */
+-	smbusdev = pci_dev_find_vendorclass(0x10de, 0x0c05);
+-	if (!smbusdev) {
+-		if (buses_supported & CHIP_BUSTYPE_SPI) {
+-			msg_perr("ERROR: SMBus device not found. Not enabling "
+-				 "SPI.\n");
+-			buses_supported &= ~CHIP_BUSTYPE_SPI;
+-			ret = 1;
+-		} else {
+-			msg_pinfo("Odd. SMBus device not found.\n");
+-		}
+-		goto out_msg;
+-	}
+-	msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n",
+-		smbusdev->vendor_id, smbusdev->device_id,
+-		smbusdev->bus, smbusdev->dev, smbusdev->func);
+-
+-	/* Locate the BAR where the SPI interface lives. */
+-	mcp_spibaraddr = pci_read_long(smbusdev, 0x74);
+-	msg_pdbg("SPI BAR is at 0x%08x, ", mcp_spibaraddr);
+-	/* We hope this has native alignment. We know the SPI interface (well,
+-	 * a set of GPIOs that is connected to SPI flash) is at offset 0x530,
+-	 * so we expect a size of at least 0x800. Clear the lower bits.
+-	 * It is entirely possible that the BAR is 64k big and the low bits are
+-	 * reserved for an entirely different purpose.
+-	 */
+-	mcp_spibaraddr &= ~0x7ff;
+-	msg_pdbg("after clearing low bits BAR is at 0x%08x\n", mcp_spibaraddr);
+-
+-	/* Accessing a NULL pointer BAR is evil. Don't do it. */
+-	if (mcp_spibaraddr && (buses_supported == CHIP_BUSTYPE_SPI)) {
+-		/* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
+-		mcp_spibar = physmap("MCP67 SPI", mcp_spibaraddr, 0x544);
+-
+-/* Guessed. If this is correct, migrate to a separate MCP67 SPI driver. */
+-#define MCP67_SPI_CS		(1 << 1)
+-#define MCP67_SPI_SCK		(1 << 2)
+-#define MCP67_SPI_MOSI		(1 << 3)
+-#define MCP67_SPI_MISO		(1 << 4)
+-#define MCP67_SPI_ENABLE	(1 << 0)
+-#define MCP67_SPI_IDLE		(1 << 8)
+-
+-		status = mmio_readw(mcp_spibar + 0x530);
+-		msg_pdbg("SPI control is 0x%04x, enable=%i, idle=%i\n",
+-			 status, status & 0x1, (status >> 8) & 0x1);
+-		/* FIXME: Remove the physunmap once the SPI driver exists. */
+-		physunmap(mcp_spibar, 0x544);
+-	} else if (!mcp_spibaraddr && (buses_supported & CHIP_BUSTYPE_SPI)) {
+-		msg_pdbg("Strange. MCP SPI BAR is invalid.\n");
+-		buses_supported &= ~CHIP_BUSTYPE_SPI;
++	if (mcp6x_spi_init(want_spi)) {
+ 		ret = 1;
+-	} else if (mcp_spibaraddr && !(buses_supported & CHIP_BUSTYPE_SPI)) {
+-		msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently"
+-			 " doesn't have SPI enabled.\n");
+-	} else {
+-		msg_pdbg("MCP SPI is not used.\n");
+ 	}
+ out_msg:
+ 	msg_pinfo("Please send the output of \"flashrom -V\" to "
+@@ -1160,68 +929,6 @@ out_msg:
+ 	return ret;
+ }
+ 
+-/**
+- * The MCP61/MCP67 code is guesswork based on cleanroom reverse engineering.
+- * Due to that, it only reads info and doesn't change any settings.
+- * It is assumed that LPC chips need the MCP55 code and SPI chips need the
+- * code provided in enable_flash_mcp6x_7x_common. Until we know for sure, call
+- * enable_flash_mcp55 from this function only if enable_flash_mcp6x_7x_common
+- * indicates the flash chip is LPC. Warning: enable_flash_mcp55
+- * might make SPI flash inaccessible. The same caveat applies to SPI init
+- * for LPC flash.
+- */
+-static int enable_flash_mcp67(struct pci_dev *dev, const char *name)
+-{
+-	int result = 0;
+-
+-	result = enable_flash_mcp6x_7x_common(dev, name);
+-	if (result)
+-		return result;
+-
+-	/* Not sure if this is correct. No docs as usual. */
+-	switch (buses_supported) {
+-	case CHIP_BUSTYPE_LPC:
+-		result = enable_flash_mcp55(dev, name);
+-		break;
+-	case CHIP_BUSTYPE_SPI:
+-		msg_pinfo("SPI on this chipset is not supported yet.\n");
+-		buses_supported = CHIP_BUSTYPE_NONE;
+-		break;
+-	default:
+-		msg_pinfo("Something went wrong with bus type detection.\n");
+-		buses_supported = CHIP_BUSTYPE_NONE;
+-		break;
+-	}
+-
+-	return result;
+-}
+-
+-static int enable_flash_mcp7x(struct pci_dev *dev, const char *name)
+-{
+-	int result = 0;
+-
+-	result = enable_flash_mcp6x_7x_common(dev, name);
+-	if (result)
+-		return result;
+-
+-	/* Not sure if this is correct. No docs as usual. */
+-	switch (buses_supported) {
+-	case CHIP_BUSTYPE_LPC:
+-		msg_pinfo("LPC on this chipset is not supported yet.\n");
+-		break;
+-	case CHIP_BUSTYPE_SPI:
+-		msg_pinfo("SPI on this chipset is not supported yet.\n");
+-		buses_supported = CHIP_BUSTYPE_NONE;
+-		break;
+-	default:
+-		msg_pinfo("Something went wrong with bus type detection.\n");
+-		buses_supported = CHIP_BUSTYPE_NONE;
+-		break;
+-	}
+-
+-	return result;
+-}
+-
+ static int enable_flash_ht1000(struct pci_dev *dev, const char *name)
+ {
+ 	uint8_t val;
+@@ -1238,7 +945,7 @@ static int enable_flash_ht1000(struct pci_dev *dev, const char *name)
+ 	return 0;
+ }
+ 
+-/**
++/*
+  * Usually on the x86 architectures (and on other PC-like platforms like some
+  * Alphas or Itanium) the system flash is mapped right below 4G. On the AMD
+  * Elan SC520 only a small piece of the system flash is mapped there, but the
+@@ -1285,8 +992,11 @@ static int get_flashbase_sc520(struct pci_dev *dev, const char *name)
+ 	return 0;
+ }
+ 
++#endif
++
+ /* Please keep this list alphabetically sorted by vendor/device. */
+ const struct penable chipset_enables[] = {
++#if defined(__i386__) || defined(__x86_64__)
+ 	{0x10B9, 0x1533, OK, "ALi", "M1533",		enable_flash_ali_m1533},
+ 	{0x1022, 0x7440, OK, "AMD", "AMD-768",		enable_flash_amd8111},
+ 	{0x1022, 0x7468, OK, "AMD", "AMD8111",		enable_flash_amd8111},
+@@ -1301,7 +1011,21 @@ const struct penable chipset_enables[] = {
+ 	{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, 0x3b02, NT, "Intel", "P55",		enable_flash_ich10},
++	{0x8086, 0x3b03, NT, "Intel", "PM55",		enable_flash_ich10},
++	{0x8086, 0x3b06, NT, "Intel", "H55",		enable_flash_ich10},
++	{0x8086, 0x3b07, OK, "Intel", "QM57",		enable_flash_ich10},
++	{0x8086, 0x3b08, NT, "Intel", "H57",		enable_flash_ich10},
++	{0x8086, 0x3b09, NT, "Intel", "HM55",		enable_flash_ich10},
++	{0x8086, 0x3b0a, NT, "Intel", "Q57",		enable_flash_ich10},
++	{0x8086, 0x3b0b, NT, "Intel", "HM57",		enable_flash_ich10},
+ 	{0x8086, 0x3b0d, NT, "Intel", "3400 Mobile SFF", enable_flash_ich10},
++	{0x8086, 0x3b0e, NT, "Intel", "B55",		enable_flash_ich10},
++	{0x8086, 0x3b0f, NT, "Intel", "QS57",		enable_flash_ich10},
++	{0x8086, 0x3b12, NT, "Intel", "3400",		enable_flash_ich10},
++	{0x8086, 0x3b14, NT, "Intel", "3420",		enable_flash_ich10},
++	{0x8086, 0x3b16, NT, "Intel", "3450",		enable_flash_ich10},
++	{0x8086, 0x3b1e, NT, "Intel", "B55",		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},
+@@ -1347,6 +1071,7 @@ const struct penable chipset_enables[] = {
+ 	{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},
++	{0x10de, 0x00e0, OK, "NVIDIA", "NForce3",       enable_flash_nvidia_nforce2},
+ 	/* Slave, should not be here, to fix known bug for A01. */
+ 	{0x10de, 0x00d3, OK, "NVIDIA", "CK804",		enable_flash_ck804},
+ 	{0x10de, 0x0260, NT, "NVIDIA", "MCP51",		enable_flash_ck804},
+@@ -1354,29 +1079,37 @@ const struct penable chipset_enables[] = {
+ 	{0x10de, 0x0262, NT, "NVIDIA", "MCP51",		enable_flash_ck804},
+ 	{0x10de, 0x0263, NT, "NVIDIA", "MCP51",		enable_flash_ck804},
+ 	{0x10de, 0x0360, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* M57SLI*/
+-	{0x10de, 0x0361, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
++	/* 10de:0361 is present in Tyan S2915 OEM systems, but not connected to
++	 * the flash chip. Instead, 10de:0364 is connected to the flash chip.
++	 * Until we have PCI device class matching or some fallback mechanism,
++	 * this is needed to get flashrom working on Tyan S2915 and maybe other
++	 * dual-MCP55 boards.
++	 */
++#if 0
++	{0x10de, 0x0361, NT, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
++#endif
+ 	{0x10de, 0x0362, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
+ 	{0x10de, 0x0363, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
+ 	{0x10de, 0x0364, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
+ 	{0x10de, 0x0365, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
+ 	{0x10de, 0x0366, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
+ 	{0x10de, 0x0367, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* Pro */
+-	{0x10de, 0x03e0, NT, "NVIDIA", "MCP61",		enable_flash_mcp67},
+-	{0x10de, 0x03e1, NT, "NVIDIA", "MCP61",		enable_flash_mcp67},
+-	{0x10de, 0x03e2, NT, "NVIDIA", "MCP61",		enable_flash_mcp67},
+-	{0x10de, 0x03e3, NT, "NVIDIA", "MCP61",		enable_flash_mcp67},
+-	{0x10de, 0x0440, NT, "NVIDIA", "MCP65",		enable_flash_mcp7x},
+-	{0x10de, 0x0441, NT, "NVIDIA", "MCP65",		enable_flash_mcp7x},
+-	{0x10de, 0x0442, NT, "NVIDIA", "MCP65",		enable_flash_mcp7x},
+-	{0x10de, 0x0443, NT, "NVIDIA", "MCP65",		enable_flash_mcp7x},
+-	{0x10de, 0x0548, OK, "NVIDIA", "MCP67",		enable_flash_mcp67},
+-	{0x10de, 0x075c, NT, "NVIDIA", "MCP78S",	enable_flash_mcp7x},
+-	{0x10de, 0x075d, NT, "NVIDIA", "MCP78S",	enable_flash_mcp7x},
+-	{0x10de, 0x07d7, NT, "NVIDIA", "MCP73",		enable_flash_mcp7x},
+-	{0x10de, 0x0aac, NT, "NVIDIA", "MCP79",		enable_flash_mcp7x},
+-	{0x10de, 0x0aad, NT, "NVIDIA", "MCP79",		enable_flash_mcp7x},
+-	{0x10de, 0x0aae, NT, "NVIDIA", "MCP79",		enable_flash_mcp7x},
+-	{0x10de, 0x0aaf, NT, "NVIDIA", "MCP79",		enable_flash_mcp7x},
++	{0x10de, 0x03e0, NT, "NVIDIA", "MCP61",		enable_flash_mcp6x_7x},
++	{0x10de, 0x03e1, NT, "NVIDIA", "MCP61",		enable_flash_mcp6x_7x},
++	{0x10de, 0x03e2, NT, "NVIDIA", "MCP61",		enable_flash_mcp6x_7x},
++	{0x10de, 0x03e3, NT, "NVIDIA", "MCP61",		enable_flash_mcp6x_7x},
++	{0x10de, 0x0440, NT, "NVIDIA", "MCP65",		enable_flash_mcp6x_7x},
++	{0x10de, 0x0441, NT, "NVIDIA", "MCP65",		enable_flash_mcp6x_7x},
++	{0x10de, 0x0442, NT, "NVIDIA", "MCP65",		enable_flash_mcp6x_7x},
++	{0x10de, 0x0443, NT, "NVIDIA", "MCP65",		enable_flash_mcp6x_7x},
++	{0x10de, 0x0548, OK, "NVIDIA", "MCP67",		enable_flash_mcp6x_7x},
++	{0x10de, 0x075c, NT, "NVIDIA", "MCP78S",	enable_flash_mcp6x_7x},
++	{0x10de, 0x075d, NT, "NVIDIA", "MCP78S",	enable_flash_mcp6x_7x},
++	{0x10de, 0x07d7, NT, "NVIDIA", "MCP73",		enable_flash_mcp6x_7x},
++	{0x10de, 0x0aac, NT, "NVIDIA", "MCP79",		enable_flash_mcp6x_7x},
++	{0x10de, 0x0aad, NT, "NVIDIA", "MCP79",		enable_flash_mcp6x_7x},
++	{0x10de, 0x0aae, NT, "NVIDIA", "MCP79",		enable_flash_mcp6x_7x},
++	{0x10de, 0x0aaf, NT, "NVIDIA", "MCP79",		enable_flash_mcp6x_7x},
+ 	{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},
+@@ -1397,14 +1130,23 @@ const struct penable chipset_enables[] = {
+ 	{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, 0x0661, OK, "SiS", "661",		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, 0x0745, OK, "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},
++	/* VIA northbridges */
++	{0x1106, 0x0585, NT, "VIA", "VT82C585VPX",	via_no_byte_merge},
++	{0x1106, 0x0595, NT, "VIA", "VT82C595",		via_no_byte_merge},
++	{0x1106, 0x0597, NT, "VIA", "VT82C597",		via_no_byte_merge},
++	{0x1106, 0x0691, NT, "VIA", "VT82C69x",		via_no_byte_merge}, /* 691, 693a, 694t, 694x checked */
++	{0x1106, 0x0601, NT, "VIA", "VT8601/VT8601A",	via_no_byte_merge},
++	{0x1106, 0x8601, NT, "VIA", "VT8601T",		via_no_byte_merge},
++	/* VIA southbridges */
+ 	{0x1106, 0x8324, OK, "VIA", "CX700",		enable_flash_vt823x},
+ 	{0x1106, 0x8231, NT, "VIA", "VT8231",		enable_flash_vt823x},
+ 	{0x1106, 0x3074, NT, "VIA", "VT8233",		enable_flash_vt823x},
+@@ -1417,7 +1159,7 @@ const struct penable chipset_enables[] = {
+ 	{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},
+-
++#endif
+ 	{},
+ };
+ 
+@@ -1431,22 +1173,36 @@ int chipset_flash_enable(void)
+ 	for (i = 0; chipset_enables[i].vendor_name != NULL; i++) {
+ 		dev = pci_dev_find(chipset_enables[i].vendor_id,
+ 				   chipset_enables[i].device_id);
+-		if (dev)
+-			break;
+-	}
+-
+-	if (dev) {
++		if (!dev)
++			continue;
++		if (ret != -2) {
++			msg_pinfo("WARNING: unexpected second chipset match: "
++			       "\"%s %s\"\nignoring, please report lspci and "
++			       "board URL to flashrom at flashrom.org!\n",
++				chipset_enables[i].vendor_name,
++					chipset_enables[i].device_name);
++			continue;
++		}
+ 		msg_pinfo("Found chipset \"%s %s\", enabling flash write... ",
+ 		       chipset_enables[i].vendor_name,
+ 		       chipset_enables[i].device_name);
++		msg_pdbg("chipset PCI ID is %04x:%04x, ",
++			 chipset_enables[i].vendor_id,
++			 chipset_enables[i].device_id);
+ 
+ 		ret = chipset_enables[i].doit(dev,
+ 					      chipset_enables[i].device_name);
+-		if (ret)
++		if (ret == NOT_DONE_YET) {
++			ret = -2;
++			msg_pinfo("OK - searching further chips.\n");
++		} else if (ret < 0)
+ 			msg_pinfo("FAILED!\n");
+-		else
++		else if(ret == 0)
+ 			msg_pinfo("OK.\n");
++		else if(ret == ERROR_NONFATAL)
++			msg_pinfo("PROBLEMS, continuing anyway\n");
+ 	}
++
+ 	msg_pinfo("This chipset supports the following protocols: %s.\n",
+ 	       flashbuses_to_text(buses_supported));
+ 
+diff --git a/cli_classic.c b/cli_classic.c
+index 6e8f098..d78c575 100644
+--- a/cli_classic.c
++++ b/cli_classic.c
+@@ -21,6 +21,7 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
++#include <stdio.h>
+ #include <fcntl.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+@@ -29,22 +30,22 @@
+ #include <getopt.h>
+ #include "flash.h"
+ #include "flashchips.h"
++#include "programmer.h"
+ 
+-void cli_classic_usage(const char *name)
++static void cli_classic_usage(const char *name)
+ {
+ 	const char *pname;
+ 	int pnamelen;
+ 	int remaining = 0;
+ 	enum programmer p;
+ 
+-	printf("Usage: %s [-n] [-V] [-f] [-h|-R|-L|"
+-#if PRINT_WIKI_SUPPORT == 1
++	printf("Usage: flashrom [-n] [-V] [-f] [-h|-R|-L|"
++#if CONFIG_PRINT_WIKI == 1
+ 	         "-z|"
+ #endif
+ 	         "-E|-r <file>|-w <file>|-v <file>]\n"
+ 	       "       [-c <chipname>] [-m [<vendor>:]<part>] [-l <file>]\n"
+-	       "       [-i <image>] [-p <programmername>[:<parameters>]]\n",
+-	       name);
++	       "       [-i <image>] [-p <programmername>[:<parameters>]]\n\n");
+ 
+ 	printf("Please note that the command line interface for flashrom has "
+ 	         "changed between\n"
+@@ -65,7 +66,7 @@ void cli_classic_usage(const char *name)
+ 	       "   -V | --verbose                    more verbose output\n"
+ 	       "   -c | --chip <chipname>            probe only for specified "
+ 	         "flash chip\n"
+-#if INTERNAL_SUPPORT == 1
++#if CONFIG_INTERNAL == 1
+ 	       /* FIXME: --mainboard should be a programmer parameter */
+ 	       "   -m | --mainboard <[vendor:]part>  override mainboard "
+ 	         "detection\n"
+@@ -78,7 +79,7 @@ void cli_classic_usage(const char *name)
+ 	       "   -i | --image <name>               only flash image <name> "
+ 	         "from flash layout\n"
+ 	       "   -L | --list-supported             print supported devices\n"
+-#if PRINT_WIKI_SUPPORT == 1
++#if CONFIG_PRINT_WIKI == 1
+ 	       "   -z | --list-supported-wiki        print supported devices "
+ 	         "in wiki syntax\n"
+ #endif
+@@ -110,7 +111,7 @@ void cli_classic_usage(const char *name)
+ 	}
+ 
+ 	printf("\nYou can specify one of -h, -R, -L, "
+-#if PRINT_WIKI_SUPPORT == 1
++#if CONFIG_PRINT_WIKI == 1
+ 	         "-z, "
+ #endif
+ 	         "-E, -r, -w, -v or no operation.\n"
+@@ -118,9 +119,9 @@ void cli_classic_usage(const char *name)
+ 	         "flash chips.\n\n");
+ }
+ 
+-void cli_classic_abort_usage(const char *name)
++static void cli_classic_abort_usage(void)
+ {
+-	printf("Please run \"%s --help\" for usage info.\n", name);
++	printf("Please run \"flashrom --help\" for usage info.\n");
+ 	exit(1);
+ }
+ 
+@@ -136,7 +137,7 @@ int cli_classic(int argc, char *argv[])
+ 	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
++#if CONFIG_PRINT_WIKI == 1
+ 	int list_supported_wiki = 0;
+ #endif
+ 	int operation_specified = 0;
+@@ -166,6 +167,7 @@ int cli_classic(int argc, char *argv[])
+ 	char *filename = NULL;
+ 
+ 	char *tempstr = NULL;
++	char *pparam = NULL;
+ 
+ 	print_version();
+ 	print_banner();
+@@ -184,7 +186,7 @@ int cli_classic(int argc, char *argv[])
+ 			if (++operation_specified > 1) {
+ 				fprintf(stderr, "More than one operation "
+ 					"specified. Aborting.\n");
+-				cli_classic_abort_usage(argv[0]);
++				cli_classic_abort_usage();
+ 			}
+ 			filename = strdup(optarg);
+ 			read_it = 1;
+@@ -193,7 +195,7 @@ int cli_classic(int argc, char *argv[])
+ 			if (++operation_specified > 1) {
+ 				fprintf(stderr, "More than one operation "
+ 					"specified. Aborting.\n");
+-				cli_classic_abort_usage(argv[0]);
++				cli_classic_abort_usage();
+ 			}
+ 			filename = strdup(optarg);
+ 			write_it = 1;
+@@ -203,12 +205,12 @@ int cli_classic(int argc, char *argv[])
+ 			if (++operation_specified > 1) {
+ 				fprintf(stderr, "More than one operation "
+ 					"specified. Aborting.\n");
+-				cli_classic_abort_usage(argv[0]);
++				cli_classic_abort_usage();
+ 			}
+ 			if (dont_verify_it) {
+ 				fprintf(stderr, "--verify and --noverify are"
+ 					"mutually exclusive. Aborting.\n");
+-				cli_classic_abort_usage(argv[0]);
++				cli_classic_abort_usage();
+ 			}
+ 			filename = strdup(optarg);
+ 			verify_it = 1;
+@@ -217,7 +219,7 @@ int cli_classic(int argc, char *argv[])
+ 			if (verify_it) {
+ 				fprintf(stderr, "--verify and --noverify are"
+ 					"mutually exclusive. Aborting.\n");
+-				cli_classic_abort_usage(argv[0]);
++				cli_classic_abort_usage();
+ 			}
+ 			dont_verify_it = 1;
+ 			break;
+@@ -231,19 +233,19 @@ int cli_classic(int argc, char *argv[])
+ 			if (++operation_specified > 1) {
+ 				fprintf(stderr, "More than one operation "
+ 					"specified. Aborting.\n");
+-				cli_classic_abort_usage(argv[0]);
++				cli_classic_abort_usage();
+ 			}
+ 			erase_it = 1;
+ 			break;
+ 		case 'm':
+-#if INTERNAL_SUPPORT == 1
++#if CONFIG_INTERNAL == 1
+ 			tempstr = strdup(optarg);
+ 			lb_vendor_dev_from_string(tempstr);
+ #else
+ 			fprintf(stderr, "Error: Internal programmer support "
+ 				"was not compiled in and --mainboard only\n"
+ 				"applies to the internal programmer. Aborting.\n");
+-			cli_classic_abort_usage(argv[0]);
++			cli_classic_abort_usage();
+ #endif
+ 			break;
+ 		case 'f':
+@@ -252,7 +254,7 @@ int cli_classic(int argc, char *argv[])
+ 		case 'l':
+ 			tempstr = strdup(optarg);
+ 			if (read_romlayout(tempstr))
+-				cli_classic_abort_usage(argv[0]);
++				cli_classic_abort_usage();
+ 			break;
+ 		case 'i':
+ 			tempstr = strdup(optarg);
+@@ -262,22 +264,22 @@ int cli_classic(int argc, char *argv[])
+ 			if (++operation_specified > 1) {
+ 				fprintf(stderr, "More than one operation "
+ 					"specified. Aborting.\n");
+-				cli_classic_abort_usage(argv[0]);
++				cli_classic_abort_usage();
+ 			}
+ 			list_supported = 1;
+ 			break;
+ 		case 'z':
+-#if PRINT_WIKI_SUPPORT == 1
++#if CONFIG_PRINT_WIKI == 1
+ 			if (++operation_specified > 1) {
+ 				fprintf(stderr, "More than one operation "
+ 					"specified. Aborting.\n");
+-				cli_classic_abort_usage(argv[0]);
++				cli_classic_abort_usage();
+ 			}
+ 			list_supported_wiki = 1;
+ #else
+ 			fprintf(stderr, "Error: Wiki output was not compiled "
+ 				"in. Aborting.\n");
+-			cli_classic_abort_usage(argv[0]);
++			cli_classic_abort_usage();
+ #endif
+ 			break;
+ 		case 'p':
+@@ -287,10 +289,10 @@ int cli_classic(int argc, char *argv[])
+ 				if (strncmp(optarg, name, namelen) == 0) {
+ 					switch (optarg[namelen]) {
+ 					case ':':
+-						programmer_param = strdup(optarg + namelen + 1);
+-						if (!strlen(programmer_param)) {
+-							free(programmer_param);
+-							programmer_param = NULL;
++						pparam = strdup(optarg + namelen + 1);
++						if (!strlen(pparam)) {
++							free(pparam);
++							pparam = NULL;
+ 						}
+ 						break;
+ 					case '\0':
+@@ -309,7 +311,7 @@ int cli_classic(int argc, char *argv[])
+ 			if (programmer == PROGRAMMER_INVALID) {
+ 				fprintf(stderr, "Error: Unknown programmer "
+ 					"%s.\n", optarg);
+-				cli_classic_abort_usage(argv[0]);
++				cli_classic_abort_usage();
+ 			}
+ 			break;
+ 		case 'R':
+@@ -317,7 +319,7 @@ int cli_classic(int argc, char *argv[])
+ 			if (++operation_specified > 1) {
+ 				fprintf(stderr, "More than one operation "
+ 					"specified. Aborting.\n");
+-				cli_classic_abort_usage(argv[0]);
++				cli_classic_abort_usage();
+ 			}
+ 			exit(0);
+ 			break;
+@@ -325,13 +327,13 @@ int cli_classic(int argc, char *argv[])
+ 			if (++operation_specified > 1) {
+ 				fprintf(stderr, "More than one operation "
+ 					"specified. Aborting.\n");
+-				cli_classic_abort_usage(argv[0]);
++				cli_classic_abort_usage();
+ 			}
+ 			cli_classic_usage(argv[0]);
+ 			exit(0);
+ 			break;
+ 		default:
+-			cli_classic_abort_usage(argv[0]);
++			cli_classic_abort_usage();
+ 			break;
+ 		}
+ 	}
+@@ -343,7 +345,7 @@ int cli_classic(int argc, char *argv[])
+ 		exit(0);
+ 	}
+ 
+-#if PRINT_WIKI_SUPPORT == 1
++#if CONFIG_PRINT_WIKI == 1
+ 	if (list_supported_wiki) {
+ 		print_supported_wiki();
+ 		exit(0);
+@@ -352,14 +354,14 @@ int cli_classic(int argc, char *argv[])
+ 
+ 	if (optind < argc) {
+ 		fprintf(stderr, "Error: Extra parameter found.\n");
+-		cli_classic_abort_usage(argv[0]);
++		cli_classic_abort_usage();
+ 	}
+ 
+-#if INTERNAL_SUPPORT == 1
++#if CONFIG_INTERNAL == 1
+ 	if ((programmer != PROGRAMMER_INTERNAL) && (lb_part || lb_vendor)) {
+ 		fprintf(stderr, "Error: --mainboard requires the internal "
+ 				"programmer. Aborting.\n");
+-		cli_classic_abort_usage(argv[0]);
++		cli_classic_abort_usage();
+ 	}
+ #endif
+ 
+@@ -378,16 +380,15 @@ int cli_classic(int argc, char *argv[])
+ 		flash = NULL;
+ 	}
+ 
+-	msg_pdbg("Initializing %s programmer\n",
+-		 programmer_table[programmer].name);
+-	if (programmer_init()) {
++	/* FIXME: Delay calibration should happen in programmer code. */
++	myusec_calibrate_delay();
++
++	if (programmer_init(pparam)) {
+ 		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);
+@@ -417,7 +418,7 @@ int cli_classic(int argc, char *argv[])
+ 				exit(1);
+ 			}
+ 			printf("Please note that forced reads most likely contain garbage.\n");
+-			return read_flash(flashes[0], filename);
++			return read_flash_to_file(flashes[0], filename);
+ 		}
+ 		// FIXME: flash writes stay enabled!
+ 		programmer_shutdown();
+diff --git a/dediprog.c b/dediprog.c
+index 2d0d060..cf4bd64 100644
+--- a/dediprog.c
++++ b/dediprog.c
+@@ -18,28 +18,28 @@
+  */
+ 
+ #include <string.h>
+-#include <stdlib.h>
+-#include <ctype.h>
+-#include <sys/types.h>
+ #include <usb.h>
+ #include "flash.h"
+ #include "chipdrivers.h"
++#include "programmer.h"
+ #include "spi.h"
+ 
+ #define DEFAULT_TIMEOUT 3000
+-usb_dev_handle *dediprog_handle;
++static usb_dev_handle *dediprog_handle;
+ 
+-int dediprog_do_stuff(void);
+-
+-void print_hex(void *buf, size_t len)
++#if 0
++/* Might be useful for other pieces of code as well. */
++static void print_hex(void *buf, size_t len)
+ {
+ 	size_t i;
+ 
+ 	for (i = 0; i < len; i++)
+ 		msg_pdbg(" %02x", ((uint8_t *)buf)[i]);
+ }
++#endif
+ 
+-struct usb_device *get_device_by_vid_pid(uint16_t vid, uint16_t pid)
++/* Might be useful for other USB devices as well. static for now. */
++static struct usb_device *get_device_by_vid_pid(uint16_t vid, uint16_t pid)
+ {
+ 	struct usb_bus *bus;
+ 	struct usb_device *dev;
+@@ -55,7 +55,7 @@ struct usb_device *get_device_by_vid_pid(uint16_t vid, uint16_t pid)
+ 
+ //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)
++static int dediprog_set_spi_voltage(uint16_t voltage)
+ {
+ 	int ret;
+ 	unsigned int mv;
+@@ -88,6 +88,7 @@ int dediprog_set_spi_voltage(uint16_t voltage)
+ 	return 0;
+ }
+ 
++#if 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.
+@@ -95,7 +96,7 @@ int dediprog_set_spi_voltage(uint16_t voltage)
+  * 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)
++static int dediprog_set_spi_speed(uint16_t speed)
+ {
+ 	int ret;
+ 	unsigned int khz;
+@@ -143,6 +144,7 @@ int dediprog_set_spi_speed(uint16_t speed)
+ 	}
+ 	return 0;
+ }
++#endif
+ 
+ int dediprog_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+ {
+@@ -186,7 +188,7 @@ int dediprog_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ 	return 0;
+ }
+ 
+-int dediprog_check_devicestring(void)
++static int dediprog_check_devicestring(void)
+ {
+ 	int ret;
+ 	char buf[0x11];
+@@ -226,7 +228,7 @@ int dediprog_check_devicestring(void)
+  * dediprog_check_devicestring (often) or Command A (often) or
+  * Command F (once).
+  */
+-int dediprog_command_a(void)
++static int dediprog_command_a(void)
+ {
+ 	int ret;
+ 	char buf[0x1];
+@@ -245,7 +247,7 @@ int dediprog_command_a(void)
+  * 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)
++static int dediprog_command_c(void)
+ {
+ 	int ret;
+ 
+@@ -257,11 +259,12 @@ int dediprog_command_c(void)
+ 	return 0;
+ }
+ 
++#if 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)
++static int dediprog_command_f(int timeout)
+ {
+ 	int ret;
+ 	char buf[0x1];
+@@ -274,6 +277,7 @@ int dediprog_command_f(int timeout)
+ 	}
+ 	return 0;
+ }
++#endif
+ 
+ /* URB numbers refer to the first log ever captured. */
+ int dediprog_init(void)
+@@ -326,10 +330,11 @@ int dediprog_init(void)
+ 	return 0;
+ }
+ 
++#if 0
+ /* Leftovers from reverse engineering. Keep for documentation purposes until
+  * completely understood.
+  */
+-int dediprog_do_stuff(void)
++static int dediprog_do_stuff(void)
+ {
+ 	char buf[0x4];
+ 	/* SPI command processing starts here. */
+@@ -344,7 +349,6 @@ int dediprog_do_stuff(void)
+ 		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))
+@@ -372,12 +376,10 @@ int dediprog_do_stuff(void)
+ 	/* 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;
+ }
++#endif	
+ 
+ int dediprog_shutdown(void)
+ {
+diff --git a/dmi.c b/dmi.c
+index cde11a5..f18907f 100644
+--- a/dmi.c
++++ b/dmi.c
+@@ -23,8 +23,29 @@
+ #include <stdlib.h>
+ 
+ #include "flash.h"
++#include "programmer.h"
+ 
+-const char *dmidecode_names[] = {
++int has_dmi_support = 0;
++
++#if STANDALONE
++
++/* Stub to indicate missing DMI functionality.
++ * has_dmi_support is 0 by default, so nothing to do here.
++ * Because dmidecode is not available on all systems, the goal is to implement
++ * the DMI subset we need directly in this file.
++ */
++void dmi_init(void)
++{
++}
++
++int dmi_match(const char *pattern)
++{
++	return 0;
++}
++
++#else /* STANDALONE */
++
++static const char *dmidecode_names[] = {
+ 	"system-manufacturer",
+ 	"system-product-name",
+ 	"system-version",
+@@ -34,10 +55,9 @@ const char *dmidecode_names[] = {
+ };
+ 
+ #define DMI_COMMAND_LEN_MAX 260
+-const char *dmidecode_command = "dmidecode";
++static const char *dmidecode_command = "dmidecode";
+ 
+-int has_dmi_support = 0;
+-char *dmistrings[ARRAY_SIZE(dmidecode_names)];
++static char *dmistrings[ARRAY_SIZE(dmidecode_names)];
+ 
+ /* Strings longer than 4096 in DMI are just insane. */
+ #define DMI_MAX_ANSWER_LEN 4096
+@@ -56,15 +76,24 @@ static char *get_dmi_string(const char *string_name)
+ 		msg_perr("DMI pipe open error\n");
+ 		return NULL;
+ 	}
+-	if (!fgets(answerbuf, DMI_MAX_ANSWER_LEN, dmidecode_pipe)) {
+-		if(ferror(dmidecode_pipe)) {
+-			msg_perr("DMI pipe read error\n");
+-			pclose(dmidecode_pipe);
+-			return NULL;
+-		} else {
+-			answerbuf[0] = 0;	/* Hit EOF */
++
++	/* Kill lines starting with '#', as recent dmidecode versions
++	   have the quirk to emit a "# SMBIOS implementations newer..."
++	   message even on "-s" if the SMBIOS declares a
++	   newer-than-supported version number, while it *should* only print
++	   the requested string. */
++	do {
++		if (!fgets(answerbuf, DMI_MAX_ANSWER_LEN, dmidecode_pipe)) {
++			if(ferror(dmidecode_pipe)) {
++				msg_perr("DMI pipe read error\n");
++				pclose(dmidecode_pipe);
++				return NULL;
++			} else {
++				answerbuf[0] = 0;	/* Hit EOF */
++			}
+ 		}
+-	}
++	} while(answerbuf[0] == '#');
++
+ 	/* Toss all output above DMI_MAX_ANSWER_LEN away to prevent
+ 	   deadlock on pclose. */
+ 	while (!feof(dmidecode_pipe))
+@@ -172,3 +201,5 @@ int dmi_match(const char *pattern)
+ 
+ 	return 0;
+ }
++
++#endif /* STANDALONE */
+diff --git a/drkaiser.c b/drkaiser.c
+index 572fee3..5f5e580 100644
+--- a/drkaiser.c
++++ b/drkaiser.c
+@@ -19,42 +19,43 @@
+  */
+ 
+ #include <stdlib.h>
+-#include <string.h>
+-#include <sys/types.h>
+ #include "flash.h"
++#include "programmer.h"
+ 
+ #define PCI_VENDOR_ID_DRKAISER		0x1803
+ 
+ #define PCI_MAGIC_DRKAISER_ADDR		0x50
+ #define PCI_MAGIC_DRKAISER_VALUE	0xa971
+ 
+-struct pcidev_status drkaiser_pcidev[] = {
++/* Mask to restrict flash accesses to the 128kB memory window. */
++#define DRKAISER_MEMMAP_MASK		((1 << 17) - 1)
++
++const struct pcidev_status drkaiser_pcidev[] = {
+ 	{0x1803, 0x5057, OK, "Dr. Kaiser", "PC-Waechter (Actel FPGA)"},
+ 	{},
+ };
+ 
+-uint8_t *drkaiser_bar;
++static 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);
++
++	addr = pcidev_init(PCI_VENDOR_ID_DRKAISER, PCI_BASE_ADDRESS_2,
++			   drkaiser_pcidev);
+ 
+ 	/* 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;
+ }
+ 
+@@ -62,7 +63,6 @@ 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;
+@@ -70,10 +70,10 @@ int drkaiser_shutdown(void)
+ 
+ void drkaiser_chip_writeb(uint8_t val, chipaddr addr)
+ {
+-	mmio_writeb(val, drkaiser_bar + addr);
++	pci_mmio_writeb(val, drkaiser_bar + (addr & DRKAISER_MEMMAP_MASK));
+ }
+ 
+ uint8_t drkaiser_chip_readb(const chipaddr addr)
+ {
+-	return mmio_readb(drkaiser_bar + addr);
++	return pci_mmio_readb(drkaiser_bar + (addr & DRKAISER_MEMMAP_MASK));
+ }
+diff --git a/dummyflasher.c b/dummyflasher.c
+index 8ebd695..a356d51 100644
+--- a/dummyflasher.c
++++ b/dummyflasher.c
+@@ -23,45 +23,49 @@
+ #include <ctype.h>
+ #include <sys/types.h>
+ #include "flash.h"
++#include "chipdrivers.h"
++#include "programmer.h"
++
++static void tolower_string(char *str)
++{
++	for (; *str != '\0'; str++)
++		*str = (char)tolower((unsigned char)*str);
++}
+ 
+ int dummy_init(void)
+ {
+-	int i;
++	char *bustext = NULL;
++
+ 	msg_pspew("%s\n", __func__);
+ 
+-	/* "all" is equivalent to specifying no type. */
+-	if (programmer_param && (!strcmp(programmer_param, "all"))) {
+-		free(programmer_param);
+-		programmer_param = NULL;
+-	}
+-	if (!programmer_param)
+-		programmer_param = strdup("parallel,lpc,fwh,spi");
++	bustext = extract_programmer_param("bus");
++	msg_pdbg("Requested buses are: %s\n", bustext ? bustext : "default");
++	if (!bustext)
++		bustext = strdup("parallel+lpc+fwh+spi");
+ 	/* Convert the parameters to lowercase. */
+-	for (i = 0; programmer_param[i] != '\0'; i++)
+-		programmer_param[i] =
+-		    (char)tolower((unsigned char)programmer_param[i]);
++	tolower_string(bustext);
+ 
+ 	buses_supported = CHIP_BUSTYPE_NONE;
+-	if (strstr(programmer_param, "parallel")) {
++	if (strstr(bustext, "parallel")) {
+ 		buses_supported |= CHIP_BUSTYPE_PARALLEL;
+ 		msg_pdbg("Enabling support for %s flash.\n", "parallel");
+ 	}
+-	if (strstr(programmer_param, "lpc")) {
++	if (strstr(bustext, "lpc")) {
+ 		buses_supported |= CHIP_BUSTYPE_LPC;
+ 		msg_pdbg("Enabling support for %s flash.\n", "LPC");
+ 	}
+-	if (strstr(programmer_param, "fwh")) {
++	if (strstr(bustext, "fwh")) {
+ 		buses_supported |= CHIP_BUSTYPE_FWH;
+ 		msg_pdbg("Enabling support for %s flash.\n", "FWH");
+ 	}
+-	if (strstr(programmer_param, "spi")) {
++	if (strstr(bustext, "spi")) {
+ 		buses_supported |= CHIP_BUSTYPE_SPI;
+ 		spi_controller = SPI_CONTROLLER_DUMMY;
+ 		msg_pdbg("Enabling support for %s flash.\n", "SPI");
+ 	}
+ 	if (buses_supported == CHIP_BUSTYPE_NONE)
+ 		msg_pdbg("Support for all flash bus types disabled.\n");
+-	free(programmer_param);
++	free(bustext);
+ 	return 0;
+ }
+ 
+@@ -157,3 +161,18 @@ int dummy_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ 	msg_pspew("\n");
+ 	return 0;
+ }
++
++int dummy_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len)
++{
++	/* Maximum read length is unlimited, use 64kB. */
++	return spi_read_chunked(flash, buf, start, len, 64 * 1024);
++}
++
++/* Is is impossible to trigger this code path because dummyflasher probing will
++ * never be successful, and the current frontend refuses to write in that case.
++ * Other frontends may allow writing even for non-detected chips, though.
++ */
++int dummy_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
++{
++	return spi_write_chunked(flash, buf, start, len, 256);
++}
+diff --git a/flash.h b/flash.h
+index f1ecb21..6f0d7c0 100644
+--- a/flash.h
++++ b/flash.h
+@@ -24,9 +24,8 @@
+ #ifndef __FLASH_H__
+ #define __FLASH_H__ 1
+ 
+-#include <unistd.h>
+ #include <stdint.h>
+-#include <stdio.h>
++#include <stddef.h>
+ #include "hwaccess.h"
+ #ifdef _WIN32
+ #include <windows.h>
+@@ -36,76 +35,7 @@
+ 
+ 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 ATAHPT_SUPPORT == 1
+-	PROGRAMMER_ATAHPT,
+-#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. */
+-};
+-
+-extern enum programmer programmer;
+-
+-struct programmer_entry {
+-	const char *vendor;
+-	const char *name;
+-
+-	int (*init) (void);
+-	int (*shutdown) (void);
+-
+-	void * (*map_flash_region) (const char *descr, unsigned long phys_addr,
+-				    size_t len);
+-	void (*unmap_flash_region) (void *virt_addr, size_t len);
+-
+-	void (*chip_writeb) (uint8_t val, chipaddr addr);
+-	void (*chip_writew) (uint16_t val, chipaddr addr);
+-	void (*chip_writel) (uint32_t val, chipaddr addr);
+-	void (*chip_writen) (uint8_t *buf, chipaddr addr, size_t len);
+-	uint8_t (*chip_readb) (const chipaddr addr);
+-	uint16_t (*chip_readw) (const chipaddr addr);
+-	uint32_t (*chip_readl) (const chipaddr addr);
+-	void (*chip_readn) (uint8_t *buf, const chipaddr addr, size_t len);
+-	void (*delay) (int usecs);
+-};
+-
+-extern const struct programmer_entry programmer_table[];
+-
+ int register_shutdown(void (*function) (void *data), void *data);
+-
+-int programmer_init(void);
+-int programmer_shutdown(void);
+ void *programmer_map_flash_region(const char *descr, unsigned long phys_addr,
+ 				  size_t len);
+ void programmer_unmap_flash_region(void *virt_addr, size_t len);
+@@ -119,21 +49,6 @@ 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 {
+@@ -154,8 +69,9 @@ enum chipbustype {
+ 
+ /*
+  * How many different erase functions do we have per chip?
++ * Atmel AT25FS010 has 6 different functions.
+  */
+-#define NUM_ERASEFUNCTIONS 5
++#define NUM_ERASEFUNCTIONS 6
+ 
+ #define FEATURE_REGISTERMAP	(1 << 0)
+ #define FEATURE_BYTEWRITES	(1 << 1)
+@@ -167,6 +83,9 @@ enum chipbustype {
+ #define FEATURE_ADDR_2AA	(1 << 2)
+ #define FEATURE_ADDR_AAA	(2 << 2)
+ #define FEATURE_ADDR_SHIFTED	(1 << 5)
++#define FEATURE_WRSR_EWSR	(1 << 6)
++#define FEATURE_WRSR_WREN	(1 << 7)
++#define FEATURE_WRSR_EITHER	(FEATURE_WRSR_EWSR | FEATURE_WRSR_WREN)
+ 
+ struct flashchip {
+ 	const char *vendor;
+@@ -250,269 +169,11 @@ struct flashchip {
+ 
+ extern struct flashchip flashchips[];
+ 
+-#if INTERNAL_SUPPORT == 1
+-struct penable {
+-	uint16_t vendor_id;
+-	uint16_t device_id;
+-	int status;
+-	const char *vendor_name;
+-	const char *device_name;
+-	int (*doit) (struct pci_dev *dev, const char *name);
+-};
+-
+-extern const struct penable chipset_enables[];
+-
+-struct board_pciid_enable {
+-	/* Any device, but make it sensible, like the ISA bridge. */
+-	uint16_t first_vendor;
+-	uint16_t first_device;
+-	uint16_t first_card_vendor;
+-	uint16_t first_card_device;
+-
+-	/* Any device, but make it sensible, like
+-	 * the host bridge. May be NULL.
+-	 */
+-	uint16_t second_vendor;
+-	uint16_t second_device;
+-	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;
+-
+-	const char *vendor_name;
+-	const char *board_name;
+-
+-	int max_rom_decode_parallel;
+-	int status;
+-	int (*enable) (const char *name);
+-};
+-
+-extern struct board_pciid_enable board_pciid_enables[];
+-
+-struct board_info {
+-	const char *vendor;
+-	const char *name;
+-};
+-
+-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 */
+-
+-extern uint32_t io_base_addr;
+-extern struct pci_access *pacc;
+-extern struct pci_dev *pcidev_dev;
+-struct pcidev_status {
+-	uint16_t vendor_id;
+-	uint16_t device_id;
+-	int status;
+-	const char *vendor_name;
+-	const char *device_name;
+-};
+-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(void);
+-#if (NIC3COM_SUPPORT == 1) || (GFXNVIDIA_SUPPORT == 1) || (DRKAISER_SUPPORT == 1) || (SATASII_SUPPORT == 1) || (ATAHPT_SUPPORT == 1)
+-void print_supported_pcidevs(struct pcidev_status *devs);
+-#endif
+ void print_supported_wiki(void);
+ 
+-/* board_enable.c */
+-void w836xx_ext_enter(uint16_t port);
+-void w836xx_ext_leave(uint16_t port);
+-uint8_t sio_read(uint16_t port, uint8_t reg);
+-void sio_write(uint16_t port, uint8_t reg, uint8_t data);
+-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 */
+-int chipset_flash_enable(void);
+-
+-/* 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);
+-
+-/* 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
+-extern int is_laptop;
+-extern int force_boardenable;
+-extern int force_boardmismatch;
+-void probe_superio(void);
+-int internal_init(void);
+-int internal_shutdown(void);
+-void internal_chip_writeb(uint8_t val, chipaddr addr);
+-void internal_chip_writew(uint16_t val, chipaddr addr);
+-void internal_chip_writel(uint32_t val, chipaddr addr);
+-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);
+-
+-/* 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);
+-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);
+-
+-/* 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);
+-void dummy_unmap(void *virt_addr, size_t len);
+-void dummy_chip_writeb(uint8_t val, chipaddr addr);
+-void dummy_chip_writew(uint16_t val, chipaddr addr);
+-void dummy_chip_writel(uint32_t val, chipaddr addr);
+-void dummy_chip_writen(uint8_t *buf, chipaddr addr, size_t len);
+-uint8_t dummy_chip_readb(const chipaddr addr);
+-uint16_t dummy_chip_readw(const chipaddr addr);
+-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
+-
+-/* atahpt.c */
+-#if ATAHPT_SUPPORT == 1
+-int atahpt_init(void);
+-int atahpt_shutdown(void);
+-void atahpt_chip_writeb(uint8_t val, chipaddr addr);
+-uint8_t atahpt_chip_readb(const chipaddr addr);
+-extern struct pcidev_status ata_hpt[];
+-#endif
+-
+-/* ft2232_spi.c */
+-#define FTDI_FT2232H 0x6010
+-#define FTDI_FT4232H 0x6011
+-int ft2232_spi_init(void);
+-int ft2232_spi_send_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr);
+-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);
+-int buspirate_spi_write_256(struct flashchip *flash, uint8_t *buf);
+-
+-/* 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 */
+ enum write_granularity {
+ 	write_gran_1bit,
+@@ -520,26 +181,14 @@ enum write_granularity {
+ 	write_gran_256bytes,
+ };
+ 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 const char * const 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 read_flash_to_file(struct flashchip *flash, char *filename);
+ int min(int a, int b);
+ int max(int a, int b);
+ char *extract_param(char **haystack, char *needle, char *delim);
+@@ -555,8 +204,12 @@ int doit(struct flashchip *flash, int force, char *filename, int read_it, int wr
+ #define OK 0
+ #define NT 1    /* Not tested */
+ 
++/* Something happened that shouldn't happen, but we can go on */
++#define ERROR_NONFATAL 0x100
++
+ /* cli_output.c */
+-int print(int type, const char *fmt, ...);
++/* Let gcc and clang check for correct printf-style format strings. */
++int print(int type, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
+ #define MSG_ERROR	0
+ #define MSG_INFO	1
+ #define MSG_DEBUG	2
+@@ -578,119 +231,20 @@ int print(int type, const char *fmt, ...);
+ 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);
+ 
+ /* 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;
+ struct spi_command {
+ 	unsigned int writecnt;
+ 	unsigned int readcnt;
+ 	const unsigned char *writearr;
+ 	unsigned char *readarr;
+ };
+-struct spi_programmer {
+-	int (*command)(unsigned int writecnt, unsigned int readcnt,
+-		   const unsigned char *writearr, unsigned char *readarr);
+-	int (*multicommand)(struct spi_command *cmds);
+-
+-	/* Optimized functions for this programmer */
+-	int (*read)(struct flashchip *flash, uint8_t *buf, int start, int len);
+-	int (*write_256)(struct flashchip *flash, uint8_t *buf);
+-};
+-
+-extern enum spi_controller spi_controller;
+-extern const struct spi_programmer spi_programmer[];
+-extern void *spibar;
+ int spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ 		const unsigned char *writearr, unsigned char *readarr);
+ 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 *cmds);
+ uint32_t spi_get_valid_read_addr(void);
+ 
+-/* ichspi.c */
+-int ich_init_opcodes(void);
+-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 *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,
+-			const unsigned char *writearr, unsigned char *readarr);
+-int it8716f_spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len);
+-int it8716f_spi_chip_write_256(struct flashchip *flash, uint8_t *buf);
+-
+-/* sb600spi.c */
+-int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+-		      const unsigned char *writearr, unsigned char *readarr);
+-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;
+-
+-/* wbsio_spi.c */
+-int wbsio_check_for_spi(const char *name);
+-int wbsio_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+-		      const unsigned char *writearr, unsigned char *readarr);
+-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);
+-
+-/* serprog.c */
+-int serprog_init(void);
+-int serprog_shutdown(void);
+-void serprog_chip_writeb(uint8_t val, chipaddr addr);
+-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);
+-
+ #endif				/* !__FLASH_H__ */
+diff --git a/flashchips.c b/flashchips.c
+index 10c0989..c9cae79 100644
+--- a/flashchips.c
++++ b/flashchips.c
+@@ -65,7 +65,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 128,
+ 		.page_size	= 16 * 1024,
+ 		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+@@ -105,90 +105,812 @@ struct flashchip flashchips[] = {
+ 				},
+ 				.block_erase = erase_sector_jedec,
+ 			}, {
+-				.eraseblocks = { {256 * 1024, 1} },
+-				.block_erase = erase_chip_block_jedec,
+-			},
++				.eraseblocks = { {256 * 1024, 1} },
++				.block_erase = erase_chip_block_jedec,
++			},
++		},
++		.write		= write_jedec_1,
++		.read		= read_memmapped,
++	},
++
++	{
++		.vendor		= "AMD",
++		.name		= "Am29F002(N)BT",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= AMD_ID,
++		.model_id	= AM_29F002BT,
++		.total_size	= 256,
++		.page_size	= 256,
++		.feature_bits	= FEATURE_EITHER_RESET | FEATURE_ADDR_2AA,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++	},
++
++	{
++		.vendor		= "AMD",
++		.name		= "Am29F016D",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= AMD_ID,
++		.model_id	= AM_29F016D,
++		.total_size	= 2 * 1024,
++		.page_size	= 64 * 1024,
++		.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) */
++		.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,
++	},
++
++	{
++		.vendor		= "AMD",
++		.name		= "Am29F040B",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= AMD_ID,
++		.model_id	= AM_29F040B,
++		.total_size	= 512,
++		.page_size	= 64 * 1024,
++		.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) */
++		.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		= "AMD",
++		.name		= "Am29F080B",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= AMD_ID,
++		.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_ZERO,
++		.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,
++	},
++
++	{
++		.vendor		= "AMD",
++		.name		= "Am29LV040B",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= AMD_ID,
++		.model_id	= AM_29LV040B,
++		.total_size	= 512,
++		.page_size	= 64 * 1024,
++		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
++		.tested		= TEST_OK_PREW,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_IGNORED, /* routine don't use probe_timing (am29f040b.c) */
++		.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		= "AMD",
++		.name		= "Am29LV081B",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= AMD_ID,
++		.model_id	= AM_29LV080B,
++		.total_size	= 1024,
++		.page_size	= 64 * 1024,
++		.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) */
++		.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,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L05PT",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID,
++		.model_id	= AMIC_A25L05PT,
++		.total_size	= 64,
++		.page_size	= 256,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid4,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			}
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L05PU",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID,
++		.model_id	= AMIC_A25L05PU,
++		.total_size	= 64,
++		.page_size	= 256,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid4,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			}
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L10PT",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID,
++		.model_id	= AMIC_A25L10PT,
++		.total_size	= 128,
++		.page_size	= 256,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid4,
++		.probe_timing	= TIMING_ZERO,
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = {
++					{64 * 1024, 1},
++					{32 * 1024, 1},
++					{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,
++			}
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L10PU",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID,
++		.model_id	= AMIC_A25L10PU,
++		.total_size	= 128,
++		.page_size	= 256,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid4,
++		.probe_timing	= TIMING_ZERO,
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = {
++					{4 * 1024, 2},
++					{8 * 1024, 1},
++					{16 * 1024, 1},
++					{32 * 1024, 1},
++					{64 * 1024, 1},
++				},
++				.block_erase = spi_block_erase_d8,
++			}, {
++				.eraseblocks = { {128 * 1024, 1} },
++				.block_erase = spi_block_erase_c7,
++			}
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L20PT",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID,
++		.model_id	= AMIC_A25L20PT,
++		.total_size	= 256,
++		.page_size	= 256,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid4,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			}
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L20PU",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID,
++		.model_id	= AMIC_A25L20PU,
++		.total_size	= 256,
++		.page_size	= 256,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid4,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			}
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	/* The A25L40P{T,U} chips are distinguished by their
++	 * erase block layouts, but without any distinction in RDID.
++	 * This inexplicable quirk was verified by Rudolf Marek
++	 * and discussed on the flashrom mailing list on 2010-07-12.
++	 */
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L40PT",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID,
++		.model_id	= AMIC_A25L40PT,
++		.total_size	= 512,
++		.page_size	= 256,
++		.tested		= TEST_OK_PRW,
++		.probe		= probe_spi_rdid4,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			}
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L40PU",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID,
++		.model_id	= AMIC_A25L40PU,
++		.total_size	= 512,
++		.page_size	= 256,
++		.tested		= TEST_OK_PRW,
++		.probe		= probe_spi_rdid4,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			}
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L80P",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID,
++		.model_id	= AMIC_A25L80P,
++		.total_size	= 1024,
++		.page_size	= 256,
++		.tested		= TEST_OK_PREW,
++		.probe		= probe_spi_rdid4,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			}
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L16PT",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID,
++		.model_id	= AMIC_A25L16PT,
++		.total_size	= 2048,
++		.page_size	= 256,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid4,
++		.probe_timing	= TIMING_ZERO,
++		.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 = { {2048 * 1024, 1} },
++				.block_erase = spi_block_erase_60,
++			}, {
++				.eraseblocks = { {2048 * 1024, 1} },
++				.block_erase = spi_block_erase_c7,
++			}
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L16PU",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID,
++		.model_id	= AMIC_A25L16PU,
++		.total_size	= 2048,
++		.page_size	= 256,
++		.tested		= TEST_OK_PRW,
++		.probe		= probe_spi_rdid4,
++		.probe_timing	= TIMING_ZERO,
++		.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 = { {2048 * 1024, 1} },
++				.block_erase = spi_block_erase_60,
++			}, {
++				.eraseblocks = { {2048 * 1024, 1} },
++				.block_erase = spi_block_erase_c7,
++			}
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L512",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID_NOPREFIX,
++		.model_id	= AMIC_A25L512,
++		.total_size	= 64,
++		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid,
++		.probe_timing	= TIMING_ZERO,
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = { { 4 * 1024, 16 } },
++				.block_erase = spi_block_erase_20,
++			}, {
++				.eraseblocks = { { 64 * 1024, 1 } },
++				.block_erase = spi_block_erase_d8,
++			}, {
++				.eraseblocks = { { 64 * 1024, 1 } },
++				.block_erase = spi_block_erase_c7,
++			}			
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L010",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID_NOPREFIX,
++		.model_id	= AMIC_A25L010,
++		.total_size	= 128,
++		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			}			
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L020",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID_NOPREFIX,
++		.model_id	= AMIC_A25L020,
++		.total_size	= 256,
++		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			}			
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L040",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID_NOPREFIX,
++		.model_id	= AMIC_A25L040,
++		.total_size	= 512,
++		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			}			
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L080",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID_NOPREFIX,
++		.model_id	= AMIC_A25L080,
++		.total_size	= 1024,
++		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			}			
++		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "AMIC",
++		.name		= "A25L016",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID_NOPREFIX,
++		.model_id	= AMIC_A25L016,
++		.total_size	= 2048,
++		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid,
++		.probe_timing	= TIMING_ZERO,
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = { { 4 * 1024, 512 } },
++				.block_erase = spi_block_erase_20,
++			}, {
++				.eraseblocks = { { 64 * 1024, 32 } },
++				.block_erase = spi_block_erase_d8,
++			}, {
++				.eraseblocks = { { 2048 * 1024, 1 } },
++				.block_erase = spi_block_erase_c7,
++			}			
+ 		},
+-		.write		= write_jedec_1,
+-		.read		= read_memmapped,
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
+ 	},
+ 
+ 	{
+-		.vendor		= "AMD",
+-		.name		= "Am29F002(N)BT",
+-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+-		.manufacture_id	= AMD_ID,
+-		.model_id	= AM_29F002BT,
+-		.total_size	= 256,
++		.vendor		= "AMIC",
++		.name		= "A25L032",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID_NOPREFIX,
++		.model_id	= AMIC_A25L032,
++		.total_size	= 4096,
+ 		.page_size	= 256,
+-		.feature_bits	= FEATURE_EITHER_RESET | FEATURE_ADDR_2AA,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+-		.probe		= probe_jedec,
++		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+ 		{
+ 			{
+-				.eraseblocks = {
+-					{64 * 1024, 3},
+-					{32 * 1024, 1},
+-					{8 * 1024, 2},
+-					{16 * 1024, 1},
+-				},
+-				.block_erase = erase_sector_jedec,
++				.eraseblocks = { { 4 * 1024, 1024 } },
++				.block_erase = spi_block_erase_20,
+ 			}, {
+-				.eraseblocks = { {256 * 1024, 1} },
+-				.block_erase = erase_chip_block_jedec,
+-			},
++				.eraseblocks = { { 64 * 1024, 64 } },
++				.block_erase = spi_block_erase_52,
++			}, {
++				.eraseblocks = { { 64 * 1024, 64 } },
++				.block_erase = spi_block_erase_d8,
++			}, {
++				.eraseblocks = { { 4096 * 1024, 1 } },
++				.block_erase = spi_block_erase_60,
++			}, {
++				.eraseblocks = { { 4096 * 1024, 1 } },
++				.block_erase = spi_block_erase_c7,
++			}			
+ 		},
+-		.write		= write_jedec_1,
+-		.read		= read_memmapped,
++		.unlock		= NULL, /* Two status reg bytes (read with 0x35 and 0x05) */
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
+ 	},
+ 
+ 	{
+-		.vendor		= "AMD",
+-		.name		= "Am29F016D",
+-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+-		.manufacture_id	= AMD_ID,
+-		.model_id	= AM_29F016D,
+-		.total_size	= 2 * 1024,
+-		.page_size	= 64 * 1024,
+-		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
++		.vendor		= "AMIC",
++		.name		= "A25LQ032",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID_NOPREFIX,
++		.model_id	= AMIC_A25LQ032,
++		.total_size	= 4096,
++		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+-		.probe		= probe_jedec,
+-		.probe_timing	= TIMING_IGNORED, /* routine don't use probe_timing (am29f040b.c) */
++		.probe		= probe_spi_rdid,
++		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+ 		{
+ 			{
+-				.eraseblocks = { {64 * 1024, 32} },
+-				.block_erase = erase_sector_jedec,
++				.eraseblocks = { { 4 * 1024, 1024 } },
++				.block_erase = spi_block_erase_20,
+ 			}, {
+-				.eraseblocks = { {2048 * 1024, 1} },
+-				.block_erase = erase_chip_block_jedec,
+-			},
++				.eraseblocks = { { 64 * 1024, 64 } },
++				.block_erase = spi_block_erase_52,
++			}, {
++				.eraseblocks = { { 64 * 1024, 64 } },
++				.block_erase = spi_block_erase_d8,
++			}, {
++				.eraseblocks = { { 4096 * 1024, 1 } },
++				.block_erase = spi_block_erase_60,
++			}, {
++				.eraseblocks = { { 4096 * 1024, 1 } },
++				.block_erase = spi_block_erase_c7,
++			}			
+ 		},
+-		.write		= write_jedec_1,
+-		.read		= read_memmapped,
++		.unlock		= NULL, /* Two status reg bytes (read with 0x35 and 0x05) */
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
+ 	},
+ 
+ 	{
+-		.vendor		= "AMD",
+-		.name		= "Am29F040B",
++		.vendor		= "AMIC",
++		.name		= "A29002B",
+ 		.bustype	= CHIP_BUSTYPE_PARALLEL,
+-		.manufacture_id	= AMD_ID,
+-		.model_id	= AM_29F040B,
+-		.total_size	= 512,
++		.manufacture_id	= AMIC_ID_NOPREFIX,
++		.model_id	= AMIC_A29002B,
++		.total_size	= 256,
+ 		.page_size	= 64 * 1024,
+ 		.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) */
++		.probe_timing	= TIMING_IGNORED, /* routine don't use probe_timing (mx29f002.c) */
+ 		.block_erasers	=
+ 		{
+ 			{
+-				.eraseblocks = { {64 * 1024, 8} },
++				.eraseblocks = { 
++					{16 * 1024, 1},
++					{8 * 1024, 2},
++					{32 * 1024, 1},
++					{64 * 1024, 3},
++				},
+ 				.block_erase = erase_sector_jedec,
+ 			}, {
+-				.eraseblocks = { {512 * 1024, 1} },
++				.eraseblocks = { {256 * 1024, 1} },
+ 				.block_erase = erase_chip_block_jedec,
+ 			},
+ 		},
+@@ -197,24 +919,29 @@ struct flashchip flashchips[] = {
+ 	},
+ 
+ 	{
+-		.vendor		= "AMD",
+-		.name		= "Am29F080B",
++		.vendor		= "AMIC",
++		.name		= "A29002T",
+ 		.bustype	= CHIP_BUSTYPE_PARALLEL,
+-		.manufacture_id	= AMD_ID,
+-		.model_id	= AM_29F080B,
+-		.total_size	= 1024,
++		.manufacture_id	= AMIC_ID_NOPREFIX,
++		.model_id	= AMIC_A29002T,
++		.total_size	= 256,
+ 		.page_size	= 64 * 1024,
+ 		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PRW,
+ 		.probe		= probe_jedec,
+-		.probe_timing	= TIMING_ZERO,
++		.probe_timing	= TIMING_IGNORED, /* routine don't use probe_timing (mx29f002.c) */
+ 		.block_erasers	=
+ 		{
+ 			{
+-				.eraseblocks = { {64 * 1024, 16} },
++				.eraseblocks = { 
++					{64 * 1024, 3},
++					{32 * 1024, 1},
++					{8 * 1024, 2},
++					{16 * 1024, 1},
++				},
+ 				.block_erase = erase_sector_jedec,
+ 			}, {
+-				.eraseblocks = { {1024 * 1024, 1} },
++				.eraseblocks = { {256 * 1024, 1} },
+ 				.block_erase = erase_chip_block_jedec,
+ 			},
+ 		},
+@@ -223,11 +950,11 @@ struct flashchip flashchips[] = {
+ 	},
+ 
+ 	{
+-		.vendor		= "AMD",
+-		.name		= "Am29LV040B",
++		.vendor		= "AMIC",
++		.name		= "A29040B",
+ 		.bustype	= CHIP_BUSTYPE_PARALLEL,
+-		.manufacture_id	= AMD_ID,
+-		.model_id	= AM_29LV040B,
++		.manufacture_id	= AMIC_ID_NOPREFIX,
++		.model_id	= AMIC_A29040B,
+ 		.total_size	= 512,
+ 		.page_size	= 64 * 1024,
+ 		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+@@ -249,27 +976,28 @@ struct flashchip flashchips[] = {
+ 	},
+ 
+ 	{
+-		.vendor		= "AMD",
+-		.name		= "Am29LV081B",
+-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+-		.manufacture_id	= AMD_ID,
+-		.model_id	= AM_29LV080B,
+-		.total_size	= 1024,
++		.vendor		= "AMIC",
++		.name		= "A49LF040A",
++		.bustype	= CHIP_BUSTYPE_LPC,
++		.manufacture_id	= AMIC_ID_NOPREFIX,
++		.model_id	= AMIC_A49LF040A,
++		.total_size	= 512,
+ 		.page_size	= 64 * 1024,
+-		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+-		.tested		= TEST_UNTESTED,
++		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+-		.probe_timing	= TIMING_IGNORED, /* routine don't use probe_timing (am29f040b.c) */
++		.probe_timing	= TIMING_ZERO,	/* routine is wrapper to probe_jedec (pm49fl00x.c) */
+ 		.block_erasers	=
+ 		{
+ 			{
+-				.eraseblocks = { {64 * 1024, 16} },
+-				.block_erase = erase_sector_jedec,
++				.eraseblocks = { {64 * 1024, 8} },
++				.block_erase = erase_block_jedec,
+ 			}, {
+-				.eraseblocks = { {1024 * 1024, 1} },
++				.eraseblocks = { {512 * 1024, 1} },
+ 				.block_erase = erase_chip_block_jedec,
+-			},
++			}
+ 		},
++		.unlock		= unlock_49fl00x,
+ 		.write		= write_jedec_1,
+ 		.read		= read_memmapped,
+ 	},
+@@ -313,6 +1041,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= AT_25DF021,
+ 		.total_size	= 256,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -335,6 +1064,8 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.printlock	= spi_prettyprint_status_register_at25df,
++		.unlock		= spi_disable_blockprotect_at25df,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -347,6 +1078,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= AT_25DF041A,
+ 		.total_size	= 512,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -369,6 +1101,8 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.printlock	= spi_prettyprint_status_register_at25df,
++		.unlock		= spi_disable_blockprotect_at25df,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -381,6 +1115,44 @@ struct flashchip flashchips[] = {
+ 		.model_id	= AT_25DF081,
+ 		.total_size	= 1024,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			}
++		},
++		.printlock	= spi_prettyprint_status_register_at25df,
++		.unlock		= spi_disable_blockprotect_at25df,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "Atmel",
++		.name		= "AT25DF081A",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= ATMEL_ID,
++		.model_id	= AT_25DF081A,
++		.total_size	= 1024,
++		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -403,6 +1175,8 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.printlock	= spi_prettyprint_status_register_at25df_sec,
++		.unlock		= spi_disable_blockprotect_at25df_sec,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -415,6 +1189,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= AT_25DF161,
+ 		.total_size	= 2048,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -437,6 +1212,8 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.printlock	= spi_prettyprint_status_register_at25df_sec,
++		.unlock		= spi_disable_blockprotect_at25df_sec,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -449,7 +1226,8 @@ struct flashchip flashchips[] = {
+ 		.model_id	= AT_25DF321,
+ 		.total_size	= 4096,
+ 		.page_size	= 256,
+-		.tested		= TEST_OK_PRW,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+@@ -471,6 +1249,8 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.printlock	= spi_prettyprint_status_register_at25df,
++		.unlock		= spi_disable_blockprotect_at25df,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -483,6 +1263,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= AT_25DF321A,
+ 		.total_size	= 4096,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -505,6 +1286,8 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.printlock	= spi_prettyprint_status_register_at25df_sec,
++		.unlock		= spi_disable_blockprotect_at25df_sec,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -517,6 +1300,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= AT_25DF641,
+ 		.total_size	= 8192,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -539,6 +1323,45 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.printlock	= spi_prettyprint_status_register_at25df_sec,
++		.unlock		= spi_disable_blockprotect_at25df_sec,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "Atmel",
++		.name		= "AT25DQ161",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= ATMEL_ID,
++		.model_id	= AT_25DQ161,
++		.total_size	= 2048,
++		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_spi_rdid,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			}
++		},
++		.printlock	= spi_prettyprint_status_register_at25df_sec,
++		.unlock		= spi_disable_blockprotect_at25df_sec,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -551,6 +1374,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= AT_25F512B,
+ 		.total_size	= 64,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -573,6 +1397,8 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.printlock	= spi_prettyprint_status_register_at25f,
++		.unlock		= spi_disable_blockprotect_at25f,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -594,6 +1420,9 @@ struct flashchip flashchips[] = {
+ 				.eraseblocks = { {4 * 1024, 32} },
+ 				.block_erase = spi_block_erase_20,
+ 			}, {
++				.eraseblocks = { {4 * 1024, 32} },
++				.block_erase = spi_block_erase_d7,
++			}, {
+ 				.eraseblocks = { {32 * 1024, 4} },
+ 				.block_erase = spi_block_erase_52,
+ 			}, {
+@@ -607,6 +1436,8 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.printlock	= spi_prettyprint_status_register_at25fs010,
++		.unlock		= spi_disable_blockprotect_at25fs010,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -641,6 +1472,8 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.printlock	= spi_prettyprint_status_register_at25fs040,
++		.unlock		= spi_disable_blockprotect_at25fs040,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -675,7 +1508,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= AT_26DF081A,
+ 		.total_size	= 1024,
+ 		.page_size	= 256,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PR,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+@@ -697,6 +1530,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -731,6 +1565,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -765,6 +1600,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -781,6 +1617,7 @@ struct flashchip flashchips[] = {
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	  },*/
+@@ -877,7 +1714,7 @@ struct flashchip flashchips[] = {
+ 		.feature_bits	= FEATURE_LONG_RESET,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_jedec,
+-		.probe_timing	= 10000,			/* 10ms */ 
++		.probe_timing	= 10000,			/* 10ms */
+ 		.block_erasers	=
+ 		{
+ 			{
+@@ -1049,20 +1886,43 @@ struct flashchip flashchips[] = {
+ 
+ 	{
+ 		.vendor		= "Atmel",
+-		.name		= "AT49BV512",
++		.name		= "AT49BV512",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= ATMEL_ID,
++		.model_id	= AT_49BV512,
++		.total_size	= 64,
++		.page_size	= 64,
++		.feature_bits	= FEATURE_EITHER_RESET,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = { {64 * 1024, 1} },
++				.block_erase = erase_chip_block_jedec,
++			}
++		},
++		.write		= write_jedec_1,
++		.read		= read_memmapped,
++	},
++
++	{
++		.vendor		= "Atmel",
++		.name		= "AT49F020",
+ 		.bustype	= CHIP_BUSTYPE_PARALLEL,
+ 		.manufacture_id	= ATMEL_ID,
+-		.model_id	= AT_49BV512,
+-		.total_size	= 64,
+-		.page_size	= 64,
++		.model_id	= AT_49F020,
++		.total_size	= 256,
++		.page_size	= 256,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+ 		.block_erasers	=
+ 		{
+ 			{
+-				.eraseblocks = { {64 * 1024, 1} },
++				.eraseblocks = { {256 * 1024, 1} },
+ 				.block_erase = erase_chip_block_jedec,
+ 			}
+ 		},
+@@ -1132,119 +1992,24 @@ struct flashchip flashchips[] = {
+ 		.read		= read_memmapped,
+ 	},
+ 
+-	/* The next two chip definitions have top/bottom boot blocks, but has no
+-	device differentiation between the two */
+-	{
+-		.vendor		= "AMIC",
+-		.name		= "A25L40PT",
+-		.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,
+-		.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,
+-		.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		= "AMIC",
+-		.name		= "A29002B",
++		.vendor		= "EMST",
++		.name		= "F49B002UA",
+ 		.bustype	= CHIP_BUSTYPE_PARALLEL,
+-		.manufacture_id	= AMIC_ID_NOPREFIX,
+-		.model_id	= AMIC_A29002B,
++		.manufacture_id	= EMST_ID,
++		.model_id	= EMST_F49B002UA,
+ 		.total_size	= 256,
+-		.page_size	= 64 * 1024,
+-		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
++		.page_size	= 4096,
++		.feature_bits	= FEATURE_EITHER_RESET,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_jedec,
+-		.probe_timing	= TIMING_IGNORED, /* routine don't use probe_timing (mx29f002.c) */
+-		.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,
+-	},
+-
+-	{
+-		.vendor		= "AMIC",
+-		.name		= "A29002T",
+-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+-		.manufacture_id	= AMIC_ID_NOPREFIX,
+-		.model_id	= AMIC_A29002T,
+-		.total_size	= 256,
+-		.page_size	= 64 * 1024,
+-		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+-		.tested		= TEST_OK_PRW,
+-		.probe		= probe_jedec,
+-		.probe_timing	= TIMING_IGNORED, /* routine don't use probe_timing (mx29f002.c) */
++		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+ 		.block_erasers	=
+ 		{
+ 			{
+ 				.eraseblocks = { 
+-					{64 * 1024, 3},
+-					{32 * 1024, 1},
++					{128 * 1024, 1},
++					{96 * 1024, 1},
+ 					{8 * 1024, 2},
+ 					{16 * 1024, 1},
+ 				},
+@@ -1252,94 +2017,42 @@ struct flashchip flashchips[] = {
+ 			}, {
+ 				.eraseblocks = { {256 * 1024, 1} },
+ 				.block_erase = erase_chip_block_jedec,
+-			},
+-		},
+-		.write		= write_jedec_1,
+-		.read		= read_memmapped,
+-	},
+-
+-	{
+-		.vendor		= "AMIC",
+-		.name		= "A29040B",
+-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+-		.manufacture_id	= AMIC_ID_NOPREFIX,
+-		.model_id	= AMIC_A29040B,
+-		.total_size	= 512,
+-		.page_size	= 64 * 1024,
+-		.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) */
+-		.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		= "AMIC",
+-		.name		= "A49LF040A",
+-		.bustype	= CHIP_BUSTYPE_LPC,
+-		.manufacture_id	= AMIC_ID_NOPREFIX,
+-		.model_id	= AMIC_A49LF040A,
+-		.total_size	= 512,
+-		.page_size	= 64 * 1024,
+-		.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) */
+-		.block_erasers	=
+-		{
+-			{
+-				.eraseblocks = { {64 * 1024, 8} },
+-				.block_erase = erase_block_jedec,
+-			}, {
+-				.eraseblocks = { {512 * 1024, 1} },
+-				.block_erase = erase_chip_block_jedec,
+ 			}
+ 		},
+-		.unlock		= unlock_49fl00x,
+ 		.write		= write_jedec_1,
+ 		.read		= read_memmapped,
+ 	},
+ 
+ 	{
+ 		.vendor		= "EMST",
+-		.name		= "F49B002UA",
+-		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.name		= "F25L008A",
++		.bustype	= CHIP_BUSTYPE_SPI,
+ 		.manufacture_id	= EMST_ID,
+-		.model_id	= EMST_F49B002UA,
+-		.total_size	= 256,
+-		.page_size	= 4096,
+-		.feature_bits	= FEATURE_EITHER_RESET,
++		.model_id	= EMST_F25L008A,
++		.total_size	= 1024,
++		.page_size	= 256,
+ 		.tested		= TEST_UNTESTED,
+-		.probe		= probe_jedec,
+-		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
++		.probe		= probe_spi_rdid,
++		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+ 		{
+ 			{
+-				.eraseblocks = { 
+-					{128 * 1024, 1},
+-					{96 * 1024, 1},
+-					{8 * 1024, 2},
+-					{16 * 1024, 1},
+-				},
+-				.block_erase = erase_sector_jedec,
++				.eraseblocks = { {4 * 1024, 256} },
++				.block_erase = spi_block_erase_20,
+ 			}, {
+-				.eraseblocks = { {256 * 1024, 1} },
+-				.block_erase = erase_chip_block_jedec,
++				.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		= write_jedec_1,
+-		.read		= read_memmapped,
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_1,
++		.read		= spi_chip_read,
+ 	},
+ 
+ 	{
+@@ -1368,6 +2081,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1398,6 +2112,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1428,6 +2143,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1458,6 +2174,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1489,6 +2206,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1520,6 +2238,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1551,6 +2270,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1582,6 +2302,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1613,6 +2334,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1644,6 +2366,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1675,6 +2398,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1706,6 +2430,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1737,6 +2462,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1768,6 +2494,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1780,6 +2507,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= EN_25B64,
+ 		.total_size	= 8192,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -1799,6 +2527,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1830,6 +2559,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1864,6 +2594,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1876,6 +2607,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= EN_25F05,
+ 		.total_size	= 64,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -1898,6 +2630,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1910,6 +2643,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= EN_25F10,
+ 		.total_size	= 128,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -1932,6 +2666,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1944,6 +2679,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= EN_25F20,
+ 		.total_size	= 256,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -1966,6 +2702,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -1978,6 +2715,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= EN_25F40,
+ 		.total_size	= 512,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_OK_PROBE,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -1997,6 +2735,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			},
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -2009,7 +2748,8 @@ struct flashchip flashchips[] = {
+ 		.model_id	= EN_25F80,
+ 		.total_size	= 1024,
+ 		.page_size	= 256,
+-		.tested		= TEST_UNTESTED,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+@@ -2028,6 +2768,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -2040,6 +2781,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= EN_25F16,
+ 		.total_size	= 2048,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -2059,6 +2801,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -2071,6 +2814,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= EN_25F32,
+ 		.total_size	= 4096,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -2090,6 +2834,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -2122,7 +2867,7 @@ struct flashchip flashchips[] = {
+ 	},
+ 
+ 	{
+-		.vendor		= "EON",
++		.vendor		= "Eon",
+ 		.name		= "EN29F002(A)(N)B",
+ 		.bustype	= CHIP_BUSTYPE_PARALLEL,
+ 		.manufacture_id	= EON_ID,
+@@ -2153,7 +2898,7 @@ struct flashchip flashchips[] = {
+ 	},
+ 
+ 	{
+-		.vendor		= "EON",
++		.vendor		= "Eon",
+ 		.name		= "EN29F002(A)(N)T",
+ 		.bustype	= CHIP_BUSTYPE_PARALLEL,
+ 		.manufacture_id	= EON_ID,
+@@ -2255,7 +3000,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 512,
+ 		.page_size	= 64 * 1024,
+ 		.feature_bits	= FEATURE_ADDR_SHIFTED | FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_BAD_WRITE, /* Implicit eraseblock layout in write_m29f400bt is broken. */
+ 		.probe		= probe_m29f400bt,
+ 		.probe_timing	= TIMING_IGNORED, /* routine don't use probe_timing (m29f400bt.c) */
+ 		.block_erasers	=
+@@ -2273,7 +3018,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = block_erase_chip_m29f400bt,
+ 			},
+ 		},
+-		.write		= write_coreboot_m29f400bt,
++		.write		= NULL,
+ 		.read		= read_memmapped,
+ 	},
+ 
+@@ -2298,13 +3043,75 @@ struct flashchip flashchips[] = {
+ 					{8 * 1024, 2},
+ 					{16 * 1024, 1},
+ 				},
+-				.block_erase = block_erase_m29f400bt,
++				.block_erase = block_erase_m29f400bt,
++			}, {
++				.eraseblocks = { {512 * 1024, 1} },
++				.block_erase = block_erase_chip_m29f400bt,
++			},
++		},
++		.write		= write_m29f400bt,
++		.read		= read_memmapped,
++	},
++
++	{
++		.vendor		= "Hyundai",
++		.name		= "HY29F002T",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= HYUNDAI_ID,
++		.model_id	= HY_29F002T,
++		.total_size	= 256,
++		.page_size	= 256 * 1024,
++		.feature_bits	= FEATURE_EITHER_RESET, /* Some revisions may need FEATURE_ADDR_2AA */
++		.tested		= TEST_OK_PREW,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO, /* Datasheet has no timing info specified */
++		.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,
++	},
++
++	{
++		.vendor		= "Hyundai",
++		.name		= "HY29F002B",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= HYUNDAI_ID,
++		.model_id	= HY_29F002B,
++		.total_size	= 256,
++		.page_size	= 256 * 1024,
++		.feature_bits	= FEATURE_EITHER_RESET, /* Some revisions may need FEATURE_ADDR_2AA */
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO, /* Datasheet has no timing info specified */
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = {
++					{16 * 1024, 1},
++					{8 * 1024, 2},
++					{32 * 1024, 1},
++					{64 * 1024, 3},
++				},
++				.block_erase = erase_sector_jedec,
+ 			}, {
+-				.eraseblocks = { {512 * 1024, 1} },
+-				.block_erase = block_erase_chip_m29f400bt,
++				.eraseblocks = { {256 * 1024, 1} },
++				.block_erase = erase_chip_block_jedec,
+ 			},
+ 		},
+-		.write		= write_coreboot_m29f400bt,
++		.write		= write_jedec_1,
+ 		.read		= read_memmapped,
+ 	},
+ 
+@@ -2362,6 +3169,33 @@ struct flashchip flashchips[] = {
+ 
+ 	{
+ 		.vendor		= "Intel",
++		.name		= "28F002BC-T",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= INTEL_ID,
++		.model_id	= P28F002BC,
++		.total_size	= 256,
++		.page_size	= 256 * 1024,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_82802ab,
++		.probe_timing	= TIMING_ZERO, /* Datasheet has no timing info specified */
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = {
++					{128 * 1024, 1},
++					{96 * 1024, 1},
++					{8 * 1024, 2},
++					{16 * 1024, 1},
++				},
++				.block_erase = erase_block_82802ab,
++			},
++		},
++		.write		= write_82802ab,
++		.read		= read_memmapped,
++	},
++
++	{
++		.vendor		= "Intel",
+ 		.name		= "28F004S5",
+ 		.bustype	= CHIP_BUSTYPE_PARALLEL,
+ 		.manufacture_id	= INTEL_ID,
+@@ -2549,6 +3383,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= MX_25L512,
+ 		.total_size	= 64,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -2571,6 +3406,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			},
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -2583,6 +3419,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= MX_25L1005,
+ 		.total_size	= 128,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -2602,6 +3439,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			},
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -2614,6 +3452,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= MX_25L2005,
+ 		.total_size	= 256,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -2636,6 +3475,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			},
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -2648,6 +3488,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= MX_25L4005,
+ 		.total_size	= 512,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_OK_PRW,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -2670,6 +3511,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			},
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -2682,7 +3524,8 @@ struct flashchip flashchips[] = {
+ 		.model_id	= MX_25L8005,
+ 		.total_size	= 1024,
+ 		.page_size	= 256,
+-		.tested		= TEST_OK_PRW,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+@@ -2704,6 +3547,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			},
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -2716,6 +3560,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= MX_25L1605,
+ 		.total_size	= 2048,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_OK_PRW,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -2738,6 +3583,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			},
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -2750,6 +3596,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= MX_25L1635D,
+ 		.total_size	= 2048,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -2769,6 +3616,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -2781,6 +3629,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= MX_25L3205,
+ 		.total_size	= 4096,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_OK_PRW,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -2800,6 +3649,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			},
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -2812,6 +3662,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= MX_25L3235D,
+ 		.total_size	= 4096,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -2831,6 +3682,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -2843,6 +3695,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= MX_25L6405,
+ 		.total_size	= 8192,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_OK_PROBE,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -2862,6 +3715,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -2874,6 +3728,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= MX_25L12805,
+ 		.total_size	= 16384,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -2893,6 +3748,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3050,6 +3906,188 @@ struct flashchip flashchips[] = {
+ 	},
+ 
+ 	{
++		.vendor		= "MoselVitelic",
++		.name		= "V29C51000B",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= MVC_V29C51000B,
++		.total_size	= 64,
++		.page_size	= 512,
++		.feature_bits	= FEATURE_EITHER_RESET,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO,
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = { {512, 128} },
++				.block_erase = erase_sector_jedec,
++			}, {
++				.eraseblocks = { {64 * 1024, 1} },
++				.block_erase = erase_chip_block_jedec,
++			},
++		},
++		.write		= write_jedec_1,
++		.read		= read_memmapped,
++	},
++
++	{
++		.vendor		= "MoselVitelic",
++		.name		= "V29C51000T",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= MVC_V29C51000T,
++		.total_size	= 64,
++		.page_size	= 512,
++		.feature_bits	= FEATURE_EITHER_RESET,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO,
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = { {512, 128} },
++				.block_erase = erase_sector_jedec,
++			}, {
++				.eraseblocks = { {64 * 1024, 1} },
++				.block_erase = erase_chip_block_jedec,
++			},
++		},
++		.write		= write_jedec_1,
++		.read		= read_memmapped,
++	},
++
++	{
++		.vendor		= "MoselVitelic",
++		.name		= "V29C51400B",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= MVC_V29C51400B,
++		.total_size	= 512,
++		.page_size	= 1024,
++		.feature_bits	= FEATURE_EITHER_RESET,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO,
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = { {1024, 512} },
++				.block_erase = erase_sector_jedec,
++			}, {
++				.eraseblocks = { {512 * 1024, 1} },
++				.block_erase = erase_chip_block_jedec,
++			},
++		},
++		.write		= write_jedec_1,
++		.read		= read_memmapped,
++	},
++
++	{
++		.vendor		= "MoselVitelic",
++		.name		= "V29C51400T",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= MVC_V29C51400T,
++		.total_size	= 512,
++		.page_size	= 1024,
++		.feature_bits	= FEATURE_EITHER_RESET,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO,
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = { {1024, 512} },
++				.block_erase = erase_sector_jedec,
++			}, {
++				.eraseblocks = { {512 * 1024, 1} },
++				.block_erase = erase_chip_block_jedec,
++			},
++		},
++		.write		= write_jedec_1,
++		.read		= read_memmapped,
++	},
++
++	{
++		.vendor		= "MoselVitelic",
++		.name		= "V29LC51000",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= MVC_V29LC51000,
++		.total_size	= 64,
++		.page_size	= 512,
++		.feature_bits	= FEATURE_EITHER_RESET,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO,
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = { {512, 128} },
++				.block_erase = erase_sector_jedec,
++			}, {
++				.eraseblocks = { {64 * 1024, 1} },
++				.block_erase = erase_chip_block_jedec,
++			},
++		},
++		.write		= write_jedec_1,
++		.read		= read_memmapped,
++	},
++
++	{
++		.vendor		= "MoselVitelic",
++		.name		= "V29LC51001",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= MVC_V29LC51001,
++		.total_size	= 128,
++		.page_size	= 512,
++		.feature_bits	= FEATURE_EITHER_RESET,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++	},
++
++	{
++		.vendor		= "MoselVitelic",
++		.name		= "V29LC51002",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= MVC_V29LC51002,
++		.total_size	= 256,
++		.page_size	= 512,
++		.feature_bits	= FEATURE_EITHER_RESET,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++	},
++
++	{
+ 		.vendor		= "Numonyx",
+ 		.name		= "M25PE10",
+ 		.bustype	= CHIP_BUSTYPE_SPI,
+@@ -3073,6 +4111,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3101,6 +4140,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3129,6 +4169,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3157,6 +4198,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3185,6 +4227,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3213,6 +4256,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3247,6 +4291,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3275,6 +4320,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3303,6 +4349,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3337,6 +4384,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3365,6 +4413,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3378,7 +4427,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 256,
+ 		.page_size	= 8 * 1024,
+ 		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= TIMING_FIXME, 
+ 		.block_erasers	=
+@@ -3440,7 +4489,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 128,
+ 		.page_size	= 4096,
+ 		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+-		.tested		= TEST_OK_PRW,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+ 		.block_erasers	=
+@@ -3521,7 +4570,7 @@ struct flashchip flashchips[] = {
+ 	{
+ 		.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,
+@@ -3551,13 +4600,13 @@ 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,
+ 		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= TIMING_ZERO,	/* routine is wrapper to probe_jedec (pm49fl00x.c) */
+ 		.block_erasers	=
+@@ -3599,6 +4648,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3656,6 +4706,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3681,6 +4732,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3715,6 +4767,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			},
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_1,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3749,6 +4802,42 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			},
+ 		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_1,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "SST",
++		.name		= "SST25VF064C",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= SST_ID,
++		.model_id	= SST_25VF064C,
++		.total_size	= 8192,
++		.page_size	= 256,
++		.tested		= TEST_OK_PREW,
++		.probe		= probe_spi_rdid,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			},
++		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_1,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3777,6 +4866,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_60,
+ 			},
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_1,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3811,6 +4901,36 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			},
+ 		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_1,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "SST",
++		.name		= "SST25LF040A.RES",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= SST_ID,
++		.model_id	= SST_25VF040_REMS,
++		.total_size	= 512,
++		.page_size	= 256,
++		.tested		= TEST_OK_PROBE,
++		.probe		= probe_spi_res2,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			},
++		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_1,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3845,6 +4965,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			},
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_1,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3857,7 +4978,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= SST_25VF080B,
+ 		.total_size	= 1024,
+ 		.page_size	= 256,
+-		.tested		= TEST_OK_PRW,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+@@ -3879,6 +5000,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			},
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_1,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -3964,7 +5086,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 256,
+ 		.page_size	= 128,
+ 		.feature_bits	= FEATURE_LONG_RESET,
+-		.tested		= TEST_OK_PR,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= 10,
+ 		.block_erasers	=
+@@ -4062,9 +5184,9 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 256,
+ 		.page_size	= 4096,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_OK_PRW,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+-		.probe_timing	= 1,			/* 150 ns */ 
++		.probe_timing	= 1,			/* 150 ns */
+ 		.block_erasers	=
+ 		{
+ 			{
+@@ -4088,9 +5210,9 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 512,
+ 		.page_size	= 4096,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+-		.probe_timing	= 1,			/* 150 ns */ 
++		.probe_timing	= 1,			/* 150 ns */
+ 		.block_erasers	=
+ 		{
+ 			{
+@@ -4114,9 +5236,9 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 64,
+ 		.page_size	= 4096,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+-		.probe_timing	= 1,			/* 150 ns*/ 
++		.probe_timing	= 1,			/* 150 ns */
+ 		.block_erasers	=
+ 		{
+ 			{
+@@ -4140,9 +5262,9 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 128,
+ 		.page_size	= 4096,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+-		.probe_timing	= 1,			/* 150 ns */ 
++		.probe_timing	= 1,			/* 150 ns */
+ 		.block_erasers	=
+ 		{
+ 			{
+@@ -4168,7 +5290,7 @@ struct flashchip flashchips[] = {
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_jedec,
+-		.probe_timing	= 1,			/* 150 ns */ 
++		.probe_timing	= 1,			/* 150 ns */
+ 		.block_erasers	=
+ 		{
+ 			{
+@@ -4194,7 +5316,7 @@ struct flashchip flashchips[] = {
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_jedec,
+-		.probe_timing	= 1,			/* 150 ns */ 
++		.probe_timing	= 1,			/* 150 ns */
+ 		.block_erasers	=
+ 		{
+ 			{
+@@ -4220,7 +5342,7 @@ struct flashchip flashchips[] = {
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_jedec,
+-		.probe_timing	= 1,			/* 150 ns */ 
++		.probe_timing	= 1,			/* 150 ns */
+ 		.block_erasers	=
+ 		{
+ 			{
+@@ -4247,7 +5369,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 256,
+ 		.page_size	= 16 * 1024,
+ 		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+-		.tested		= TEST_OK_PRW,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= 1,		/* 150 ns */
+ 		.block_erasers	=
+@@ -4375,7 +5497,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 1024,
+ 		.page_size	= 64 * 1024,
+ 		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+-		.tested		= TEST_OK_PRW,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= 1,		/* 150 ns */
+ 		.block_erasers	=
+@@ -4470,9 +5592,9 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 256,
+ 		.page_size	= 16 * 1024,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+-		.probe_timing	= 1,			/* 150 ns */ 
++		.probe_timing	= 1,			/* 150 ns */
+ 		.block_erasers	=
+ 		{
+ 			{
+@@ -4499,9 +5621,9 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 256,
+ 		.page_size	= 4 * 1024,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+-		.probe_timing	= 1,			/* 150 ns */ 
++		.probe_timing	= 1,			/* 150 ns */
+ 		.block_erasers	=
+ 		{
+ 			{
+@@ -4528,9 +5650,9 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 512,
+ 		.page_size	= 4096,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+-		.probe_timing	= 1,			/* 150 ns */ 
++		.probe_timing	= 1,			/* 150 ns */
+ 		.block_erasers	=
+ 		{
+ 			{
+@@ -4556,8 +5678,8 @@ struct flashchip flashchips[] = {
+ 		.model_id	= SST_49LF040B,
+ 		.total_size	= 512,
+ 		.page_size	= 64 * 1024,
+-		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.feature_bits	= FEATURE_EITHER_RESET | FEATURE_REGISTERMAP,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= 1,		/* 150ns */
+ 		.block_erasers	=
+@@ -4573,6 +5695,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = NULL,
+ 			}
+ 		},
++		.unlock		= unlock_82802ab,
+ 		.write		= write_jedec_1,
+ 		.read		= read_memmapped,
+ 	},
+@@ -4586,7 +5709,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 1024,
+ 		.page_size	= 4096,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_OK_PR,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= TIMING_FIXME, 
+ 		.block_erasers	=
+@@ -4615,7 +5738,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 2048,
+ 		.page_size	= 4 * 1024,
+ 		.feature_bits	= FEATURE_REGISTERMAP,
+-		.tested		= TEST_OK_PRW,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_82802ab,
+ 		.probe_timing	= TIMING_IGNORED, /* routine don't use probe_timing (sst49lfxxxc.c) */
+ 		.block_erasers	=
+@@ -4659,25 +5782,26 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+ 
+ 	/* The ST M25P05 is a bit of a problem. It has the same ID as the
+ 	 * ST M25P05-A in RES mode, but supports only 128 byte writes instead
+-	 * of 256 byte writes. We rely heavily on the fact that probe_spi_res
++	 * of 256 byte writes. We rely heavily on the fact that probe_spi_res1
+ 	 * only is successful if RDID does not work.
+ 	 */
+ 	{
+ 		.vendor		= "ST",
+ 		.name		= "M25P05.RES",
+ 		.bustype	= CHIP_BUSTYPE_SPI,
+-		.manufacture_id	= ST_ID,
++		.manufacture_id	= 0, /* Not used. */
+ 		.model_id	= ST_M25P05_RES,
+ 		.total_size	= 64,
+ 		.page_size	= 256,
+ 		.tested		= TEST_UNTESTED,
+-		.probe		= probe_spi_res,
++		.probe		= probe_spi_res1,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+ 		{
+@@ -4689,6 +5813,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_1, /* 128 */
+ 		.read		= spi_chip_read,
+ 	},
+@@ -4701,7 +5826,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= ST_M25P10A,
+ 		.total_size	= 128,
+ 		.page_size	= 256,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+@@ -4714,6 +5839,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -4723,12 +5849,12 @@ struct flashchip flashchips[] = {
+ 		.vendor		= "ST",
+ 		.name		= "M25P10.RES",
+ 		.bustype	= CHIP_BUSTYPE_SPI,
+-		.manufacture_id	= ST_ID,
++		.manufacture_id	= 0, /* Not used. */
+ 		.model_id	= ST_M25P10_RES,
+ 		.total_size	= 128,
+ 		.page_size	= 256,
+ 		.tested		= TEST_UNTESTED,
+-		.probe		= probe_spi_res,
++		.probe		= probe_spi_res1,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+ 		{
+@@ -4740,6 +5866,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_1, /* 128 */
+ 		.read		= spi_chip_read,
+ 	},
+@@ -4765,6 +5892,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -4790,6 +5918,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -4798,12 +5927,12 @@ struct flashchip flashchips[] = {
+ 		.vendor		= "ST",
+ 		.name		= "M25P40-old",
+ 		.bustype	= CHIP_BUSTYPE_SPI,
+-		.manufacture_id	= ST_ID,
++		.manufacture_id	= 0, /* Not used. */
+ 		.model_id	= ST_M25P40_RES,
+ 		.total_size	= 512,
+ 		.page_size	= 256,
+ 		.tested		= TEST_UNTESTED,
+-		.probe		= probe_spi_res,
++		.probe		= probe_spi_res1,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+ 		{
+@@ -4815,6 +5944,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -4840,6 +5970,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -4865,6 +5996,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -4890,6 +6022,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -4915,6 +6048,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -4940,6 +6074,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -5017,7 +6152,7 @@ struct flashchip flashchips[] = {
+ 		.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) */
++		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (am29f040b.c) */
+ 		.block_erasers	=
+ 		{
+ 			{
+@@ -5035,6 +6170,37 @@ struct flashchip flashchips[] = {
+ 	{
+ 		/* FIXME: this has WORD/BYTE sequences; 2AA for word, 555 for byte */
+ 		.vendor		= "ST",
++		.name		= "M29F400BB",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= ST_ID,
++		.model_id	= ST_M29F400BB,
++		.total_size	= 512,
++		.page_size	= 64 * 1024,
++		.feature_bits	= FEATURE_ADDR_SHIFTED | FEATURE_EITHER_RESET,
++		.tested		= TEST_BAD_WRITE, /* Implicit eraseblock layout in write_m29f400bt is broken. */
++		.probe		= probe_m29f400bt,
++		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (m29f400bt.c) */
++		.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		= NULL,
++		.read		= read_memmapped,
++	},
++	{
++		/* FIXME: this has WORD/BYTE sequences; 2AA for word, 555 for byte */
++		.vendor		= "ST",
+ 		.name		= "M29F400BT",
+ 		.bustype	= CHIP_BUSTYPE_PARALLEL,
+ 		.manufacture_id	= ST_ID,
+@@ -5060,7 +6226,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = block_erase_chip_m29f400bt,
+ 			}
+ 		},
+-		.write		= write_coreboot_m29f400bt,
++		.write		= write_m29f400bt,
+ 		.read		= read_memmapped,
+ 	},
+ 
+@@ -5155,7 +6321,7 @@ struct flashchip flashchips[] = {
+ 		{
+ 			{
+ 				.eraseblocks = {
+-					{4 * 1024, 16},  /* sector */
++					{4 * 1024, 16}, /* sector */
+ 					{64 * 1024, 5}, /* block */
+ 					{4 * 1024, 16}, /* sector */
+ 					{4 * 1024, 16}, /* sector */
+@@ -5190,7 +6356,7 @@ struct flashchip flashchips[] = {
+ 		{
+ 			{
+ 				.eraseblocks = {
+-					{4 * 1024, 16},  /* sector */
++					{4 * 1024, 16}, /* sector */
+ 					{4 * 1024, 16}, /* sector */
+ 					{64 * 1024, 5}, /* block */
+ 					{4 * 1024, 16}, /* sector */
+@@ -5218,14 +6384,14 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 1024,
+ 		.page_size	= 64 * 1024,
+ 		.feature_bits	= FEATURE_REGISTERMAP,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_82802ab,
+ 		.probe_timing	= TIMING_FIXME,
+ 		.block_erasers	=
+ 		{
+ 			{
+ 				.eraseblocks = {
+-					{4 * 1024, 16},  /* sector */
++					{4 * 1024, 16}, /* sector */
+ 					{64 * 1024, 13}, /* block */
+ 					{4 * 1024, 16}, /* sector */
+ 					{4 * 1024, 16}, /* sector */
+@@ -5260,7 +6426,7 @@ struct flashchip flashchips[] = {
+ 		{
+ 			{
+ 				.eraseblocks = {
+-					{4 * 1024, 16},  /* sector */
++					{4 * 1024, 16}, /* sector */
+ 					{4 * 1024, 16}, /* sector */
+ 					{64 * 1024, 13}, /* block */
+ 					{4 * 1024, 16}, /* sector */
+@@ -5426,13 +6592,13 @@ struct flashchip flashchips[] = {
+ 	},
+ 
+ 	{
+-		.vendor		= "SyncMOS",
+-		.name		= "S29C31004T",
++		.vendor		= "SyncMOS/MoselVitelic",
++		.name		= "{F,S,V}29C51001B",
+ 		.bustype	= CHIP_BUSTYPE_PARALLEL,
+-		.manufacture_id	= SYNCMOS_ID,
+-		.model_id	= S29C31004T,
+-		.total_size	= 512,
+-		.page_size	= 128,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= SM_MVC_29C51001B,
++		.total_size	= 128,
++		.page_size	= 512,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_jedec,
+@@ -5440,10 +6606,10 @@ struct flashchip flashchips[] = {
+ 		.block_erasers	=
+ 		{
+ 			{
+-				.eraseblocks = { {1 * 1024, 512} },
++				.eraseblocks = { {512, 256} },
+ 				.block_erase = erase_sector_jedec,
+ 			}, {
+-				.eraseblocks = { {512 * 1024, 1} },
++				.eraseblocks = { {128 * 1024, 1} },
+ 				.block_erase = erase_chip_block_jedec,
+ 			},
+ 		},
+@@ -5452,13 +6618,13 @@ struct flashchip flashchips[] = {
+ 	},
+ 
+ 	{
+-		.vendor		= "SyncMOS",
+-		.name		= "S29C51001T",
++		.vendor		= "SyncMOS/MoselVitelic",
++		.name		= "{F,S,V}29C51001T",
+ 		.bustype	= CHIP_BUSTYPE_PARALLEL,
+-		.manufacture_id	= SYNCMOS_ID,
+-		.model_id	= S29C51001T,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= SM_MVC_29C51001T,
+ 		.total_size	= 128,
+-		.page_size	= 128,
++		.page_size	= 512,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_jedec,
+@@ -5478,15 +6644,41 @@ struct flashchip flashchips[] = {
+ 	},
+ 
+ 	{
+-		.vendor		= "SyncMOS",
+-		.name		= "S29C51002T",
++		.vendor		= "SyncMOS/MoselVitelic",
++		.name		= "{F,S,V}29C51002B",
+ 		.bustype	= CHIP_BUSTYPE_PARALLEL,
+-		.manufacture_id	= SYNCMOS_ID,
+-		.model_id	= S29C51002T,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= SM_MVC_29C51002B,
+ 		.total_size	= 256,
+-		.page_size	= 128,
++		.page_size	= 512,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_OK_PRW,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
++		.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,
++	},
++
++	{
++		.vendor		= "SyncMOS/MoselVitelic",
++		.name		= "{F,S,V}29C51002T",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= SM_MVC_29C51002T,
++		.total_size	= 256,
++		.page_size	= 512,
++		.feature_bits	= FEATURE_EITHER_RESET,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+ 		.block_erasers	=
+@@ -5504,13 +6696,39 @@ struct flashchip flashchips[] = {
+ 	},
+ 
+ 	{
+-		.vendor		= "SyncMOS",
+-		.name		= "S29C51004T",
++		.vendor		= "SyncMOS/MoselVitelic",
++		.name		= "{F,S,V}29C51004B",
+ 		.bustype	= CHIP_BUSTYPE_PARALLEL,
+-		.manufacture_id	= SYNCMOS_ID,
+-		.model_id	= S29C51004T,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= SM_MVC_29C51004B,
+ 		.total_size	= 512,
+-		.page_size	= 128,
++		.page_size	= 1024,
++		.feature_bits	= FEATURE_EITHER_RESET,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO,
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = { {1024, 512} },
++				.block_erase = erase_sector_jedec,
++			}, {
++				.eraseblocks = { {512 * 1024, 1} },
++				.block_erase = erase_chip_block_jedec,
++			},
++		},
++		.write		= write_jedec_1,
++		.read		= read_memmapped,
++	},
++
++	{
++		.vendor		= "SyncMOS/MoselVitelic",
++		.name		= "{F,S,V}29C51004T",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= SM_MVC_29C51004T,
++		.total_size	= 512,
++		.page_size	= 1024,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_jedec,
+@@ -5518,7 +6736,59 @@ struct flashchip flashchips[] = {
+ 		.block_erasers	=
+ 		{
+ 			{
+-				.eraseblocks = { {1 * 1024, 512} },
++				.eraseblocks = { {1024, 512} },
++				.block_erase = erase_sector_jedec,
++			}, {
++				.eraseblocks = { {512 * 1024, 1} },
++				.block_erase = erase_chip_block_jedec,
++			},
++		},
++		.write		= write_jedec_1,
++		.read		= read_memmapped,
++	},
++
++	{
++		.vendor		= "SyncMOS/MoselVitelic",
++		.name		= "{S,V}29C31004B",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= SM_MVC_29C31004B,
++		.total_size	= 512,
++		.page_size	= 1024,
++		.feature_bits	= FEATURE_EITHER_RESET,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = { {1024, 512} },
++				.block_erase = erase_sector_jedec,
++			}, {
++				.eraseblocks = { {512 * 1024, 1} },
++				.block_erase = erase_chip_block_jedec,
++			},
++		},
++		.write		= write_jedec_1,
++		.read		= read_memmapped,
++	},
++
++	{
++		.vendor		= "SyncMOS/MoselVitelic",
++		.name		= "{S,V}29C31004T",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= SYNCMOS_MVC_ID,
++		.model_id	= SM_MVC_29C31004T,
++		.total_size	= 512,
++		.page_size	= 1024,
++		.feature_bits	= FEATURE_EITHER_RESET,
++		.tested		= TEST_UNTESTED,
++		.probe		= probe_jedec,
++		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = { {1024, 512} },
+ 				.block_erase = erase_sector_jedec,
+ 			}, {
+ 				.eraseblocks = { {512 * 1024, 1} },
+@@ -5599,7 +6869,8 @@ struct flashchip flashchips[] = {
+ 		.model_id	= W_25Q80,
+ 		.total_size	= 1024,
+ 		.page_size	= 256,
+-		.tested		= TEST_UNTESTED,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+@@ -5621,6 +6892,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -5633,6 +6905,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= W_25Q16,
+ 		.total_size	= 2048,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -5655,6 +6928,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -5667,7 +6941,8 @@ struct flashchip flashchips[] = {
+ 		.model_id	= W_25Q32,
+ 		.total_size	= 4096,
+ 		.page_size	= 256,
+-		.tested		= TEST_UNTESTED,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+@@ -5689,6 +6964,43 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
++		.write		= spi_chip_write_256,
++		.read		= spi_chip_read,
++	},
++
++	{
++		.vendor		= "Winbond",
++		.name		= "W25Q64",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= WINBOND_NEX_ID,
++		.model_id	= W_25Q64,
++		.total_size	= 8192,
++		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_OK_PRW,
++		.probe		= probe_spi_rdid,
++		.probe_timing	= TIMING_ZERO,
++		.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,
++			}
++		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -5701,6 +7013,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= W_25X10,
+ 		.total_size	= 128,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -5717,6 +7030,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -5729,6 +7043,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= W_25X20,
+ 		.total_size	= 256,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -5745,6 +7060,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -5757,7 +7073,8 @@ struct flashchip flashchips[] = {
+ 		.model_id	= W_25X40,
+ 		.total_size	= 512,
+ 		.page_size	= 256,
+-		.tested		= TEST_OK_PRW,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+@@ -5773,6 +7090,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -5785,7 +7103,8 @@ struct flashchip flashchips[] = {
+ 		.model_id	= W_25X80,
+ 		.total_size	= 1024,
+ 		.page_size	= 256,
+-		.tested		= TEST_OK_PRW,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+@@ -5801,6 +7120,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -5813,7 +7133,8 @@ struct flashchip flashchips[] = {
+ 		.model_id	= W_25X16,
+ 		.total_size	= 2048,
+ 		.page_size	= 256,
+-		.tested		= TEST_OK_PR,
++		.feature_bits	= FEATURE_WRSR_WREN,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+ 		.block_erasers	=
+@@ -5835,6 +7156,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -5847,6 +7169,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= W_25X32,
+ 		.total_size	= 4096,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_OK_PROBE,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -5869,6 +7192,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -5881,6 +7205,7 @@ struct flashchip flashchips[] = {
+ 		.model_id	= W_25X64,
+ 		.total_size	= 8192,
+ 		.page_size	= 256,
++		.feature_bits	= FEATURE_WRSR_WREN,
+ 		.tested		= TEST_UNTESTED,
+ 		.probe		= probe_spi_rdid,
+ 		.probe_timing	= TIMING_ZERO,
+@@ -5903,6 +7228,7 @@ struct flashchip flashchips[] = {
+ 				.block_erase = spi_block_erase_c7,
+ 			}
+ 		},
++		.unlock		= spi_disable_blockprotect,
+ 		.write		= spi_chip_write_256,
+ 		.read		= spi_chip_read,
+ 	},
+@@ -5939,7 +7265,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 256,
+ 		.page_size	= 128,
+ 		.feature_bits	= FEATURE_LONG_RESET,
+-		.tested		= TEST_OK_PRW,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= 10, 
+ 		.block_erasers	=
+@@ -5985,7 +7311,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 128,
+ 		.page_size	= 128,
+ 		.feature_bits	= FEATURE_LONG_RESET,
+-		.tested		= TEST_OK_PRW,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_w29ee011,
+ 		.probe_timing	= TIMING_IGNORED, /* routine don't use probe_timing (w29ee011.c) */
+ 		.block_erasers	=
+@@ -6034,7 +7360,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 512,
+ 		.page_size	= 64 * 1024,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_OK_PRW,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= 10, 
+ 		.block_erasers	=
+@@ -6086,8 +7412,8 @@ struct flashchip flashchips[] = {
+ 		.model_id	= W_39V040FA,
+ 		.total_size	= 512,
+ 		.page_size	= 64 * 1024,
+-		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= 10, 
+ 		.block_erasers	=
+@@ -6103,6 +7429,8 @@ struct flashchip flashchips[] = {
+ 				.block_erase = erase_chip_block_jedec,
+ 			}
+ 		},
++		.printlock	= printlock_sst_fwhub,
++		.unlock		= unlock_sst_fwhub,
+ 		.write		= write_jedec_1,
+ 		.read		= read_memmapped,
+ 	},
+@@ -6142,7 +7470,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 256,
+ 		.page_size	= 128,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_OK_PRW,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= 10,
+ 		.block_erasers	=
+@@ -6166,6 +7494,29 @@ struct flashchip flashchips[] = {
+ 
+ 	{
+ 		.vendor		= "Winbond",
++		.name		= "W49F020",
++		.bustype	= CHIP_BUSTYPE_PARALLEL,
++		.manufacture_id	= WINBOND_ID,
++		.model_id	= W_49F020,
++		.total_size	= 256,
++		.page_size	= 128,
++		.feature_bits	= FEATURE_EITHER_RESET,
++		.tested		= TEST_OK_PROBE,
++		.probe		= probe_jedec,
++		.probe_timing	= 10,
++		.block_erasers	=
++		{
++			{
++				.eraseblocks = { {256 * 1024, 1} },
++				.block_erase = erase_chip_block_jedec,
++			}
++		},
++		.write		= write_jedec_1,
++		.read		= read_memmapped,
++	},
++
++	{
++		.vendor		= "Winbond",
+ 		.name		= "W49V002A",
+ 		.bustype	= CHIP_BUSTYPE_LPC,
+ 		.manufacture_id	= WINBOND_ID,
+@@ -6173,7 +7524,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 256,
+ 		.page_size	= 128,
+ 		.feature_bits	= FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= 10, 
+ 		.block_erasers	=
+@@ -6235,7 +7586,7 @@ struct flashchip flashchips[] = {
+ 		.total_size	= 1024,
+ 		.page_size	= 64 * 1024,
+ 		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+-		.tested		= TEST_UNTESTED,
++		.tested		= TEST_OK_PREW,
+ 		.probe		= probe_jedec,
+ 		.probe_timing	= TIMING_FIXME,
+ 		.block_erasers	=
+@@ -6281,6 +7632,21 @@ struct flashchip flashchips[] = {
+ 	},
+ 
+ 	{
++		.vendor		= "AMIC",
++		.name		= "unknown AMIC SPI chip",
++		.bustype	= CHIP_BUSTYPE_SPI,
++		.manufacture_id	= AMIC_ID,
++		.model_id	= GENERIC_DEVICE_ID,
++		.total_size	= 0,
++		.page_size	= 256,
++		.tested		= TEST_BAD_PREW,
++		.probe		= probe_spi_rdid4,
++		.probe_timing	= TIMING_ZERO,
++		.write		= NULL,
++		.read		= NULL,
++	},
++
++	{
+ 		.vendor		= "Atmel",
+ 		.name		= "unknown Atmel SPI chip",
+ 		.bustype	= CHIP_BUSTYPE_SPI,
+@@ -6296,8 +7662,8 @@ struct flashchip flashchips[] = {
+ 	},
+ 
+ 	{
+-		.vendor		= "EON",
+-		.name		= "unknown EON SPI chip",
++		.vendor		= "Eon",
++		.name		= "unknown Eon SPI chip",
+ 		.bustype	= CHIP_BUSTYPE_SPI,
+ 		.manufacture_id	= EON_ID_NOPREFIX,
+ 		.model_id	= GENERIC_DEVICE_ID,
+diff --git a/flashchips.h b/flashchips.h
+index bfd8145..8a7e89b 100644
+--- a/flashchips.h
++++ b/flashchips.h
+@@ -75,7 +75,27 @@
+ 
+ #define AMIC_ID			0x7F37	/* AMIC */
+ #define AMIC_ID_NOPREFIX	0x37	/* AMIC */
+-#define AMIC_A25L40P		0x2013
++#define AMIC_A25L05PT		0x2020
++#define AMIC_A25L05PU		0x2010
++#define AMIC_A25L10PT		0x2021
++#define AMIC_A25L10PU		0x2011
++#define AMIC_A25L20PT		0x2022
++#define AMIC_A25L20PU		0x2012
++#define AMIC_A25L40PT		0x2013	/* Datasheet says T and U have
++					   same device ID. Confirmed by
++					   hardware testing. */
++#define AMIC_A25L40PU		0x2013
++#define AMIC_A25L80P		0x2014	/* Seems that no A25L80PT exists */
++#define AMIC_A25L16PT		0x2025
++#define AMIC_A25L16PU		0x2015
++#define AMIC_A25L512		0x3010
++#define AMIC_A25L010		0x3011
++#define AMIC_A25L020		0x3012
++#define AMIC_A25L040		0x3013
++#define AMIC_A25L080		0x3014
++#define AMIC_A25L016		0x3015
++#define AMIC_A25L032		0x3016
++#define AMIC_A25LQ032		0x4016
+ #define AMIC_A29002B		0x0d
+ #define AMIC_A29002T		0x8C	/* Same as A290021T */
+ #define AMIC_A29040B		0x86
+@@ -98,12 +118,19 @@
+ #define AT_25DF021		0x4300
+ #define AT_25DF041A		0x4401
+ #define AT_25DF081		0x4502
++#define AT_25DF081A		0x4501	/* Yes, 81A has a lower number than 81 */
+ #define AT_25DF161		0x4602
+ #define AT_25DF321		0x4700	/* Same as 26DF321 */
+ #define AT_25DF321A		0x4701
+ #define AT_25DF641		0x4800
+-#define AT_25F512A		0x65 /* Needs special RDID. AT25F512A_RDID 15 1d */
++#define AT_25DQ161		0x8600
++#define AT25F512		/* No device ID found in datasheet. Vendor ID
++				 * can be read with AT25F512A_RDID */
++#define AT_25F512A		0x65 /* Needs AT25F512A_RDID */
+ #define AT_25F512B		0x6500
++#define AT25F1024		/* No device ID found in datasheet. Vendor ID
++				 * can be read with AT25F512A_RDID */
++#define AT_25F1024A		0x60 /* Needs AT25F512A_RDID */
+ #define AT_25FS010		0x6601
+ #define AT_25FS040		0x6604
+ #define AT_26DF041		0x4400
+@@ -144,12 +171,14 @@
+ #define AT_45DB642		/* No ID available */
+ #define AT_45DB642D		0x2800
+ #define AT_49BV512		0x03
++#define AT_49F020		0x0B
+ #define AT_49F002N		0x07	/* for AT49F002(N)  */
+ #define AT_49F002NT		0x08	/* for AT49F002(N)T */
+ 
+ #define CATALYST_ID		0x31	/* Catalyst */
+ 
+ #define EMST_ID			0x8C	/* EMST / EFST Elite Flash Storage */
++#define EMST_F25L008A		0x2014
+ #define EMST_F49B002UA		0x00
+ 
+ /*
+@@ -240,7 +269,8 @@
+ #define HY_29LV800B		0x5B
+ #define HY_29F040A		0xA4
+ #define HY_29F400B		0xAB	/* Same as HY_29F400AB */
+-#define HY_29F002		0xB0
++#define HY_29F002B		0x34
++#define HY_29F002T		0xB0
+ #define HY_29LV400T		0xB9
+ #define HY_29LV400B		0xBA
+ #define HY_29F080		0xD5
+@@ -259,6 +289,7 @@
+ #define E_28F016S5		0xAA
+ #define P28F001BXT		0x94	/* 28F001BX-T */
+ #define P28F001BXB		0x95	/* 28F001BX-B */
++#define P28F002BC		0x7C	/* 28F002BC-T */
+ #define P28F004BT		0x78	/* 28F004BV/BE-T */
+ #define P28F004BB		0x79	/* 28F004BV/BE-B */
+ #define P28F400BT		0x70	/* 28F400BV/CV/CE-T */
+@@ -387,7 +418,7 @@
+ #define SST_25VF512A_REMS	0x48	/* REMS or RES opcode */
+ #define SST_25VF010_REMS	0x49	/* REMS or RES opcode */
+ #define SST_25VF020_REMS	0x43	/* REMS or RES opcode */
+-#define SST_25VF040_REMS	0x44	/* REMS or RES opcode */
++#define SST_25VF040_REMS	0x44	/* REMS or RES opcode, same as SST25LF040A */
+ #define SST_25VF040B		0x258D
+ #define SST_25VF040B_REMS	0x8D	/* REMS or RES opcode */
+ #define SST_25VF080_REMS	0x80	/* REMS or RES opcode */
+@@ -396,6 +427,7 @@
+ #define SST_25VF016B		0x2541
+ #define SST_25VF032B		0x254A
+ #define SST_25VF032B_REMS	0x4A	/* REMS or RES opcode */
++#define SST_25VF064C		0x254B
+ #define SST_26VF016		0x2601
+ #define SST_26VF032		0x2602
+ #define SST_27SF512		0xA4
+@@ -482,11 +514,22 @@
+ #define ST_M29W040B		0xE3
+ #define ST_M29W512B		0x27
+ 
+-#define SYNCMOS_ID		0x40	/* SyncMOS and Mosel Vitelic */
+-#define S29C51001T		0x01
+-#define S29C51002T		0x02
+-#define S29C51004T		0x03
+-#define S29C31004T		0x63
++#define SYNCMOS_MVC_ID		0x40	/* SyncMOS (SM) and Mosel Vitelic Corporation (MVC) */
++#define MVC_V29C51000T		0x00
++#define MVC_V29C51400T		0x13
++#define MVC_V29LC51000		0x20
++#define MVC_V29LC51001		0x60
++#define MVC_V29LC51002		0x82
++#define MVC_V29C51000B		0xA0
++#define MVC_V29C51400B		0xB3
++#define SM_MVC_29C51001T	0x01	/* Identical chips: {F,S,V}29C51001T */
++#define SM_MVC_29C51002T	0x02	/* Identical chips: {F,S,V}29C51002T */
++#define SM_MVC_29C51004T	0x03	/* Identical chips: {F,S,V}29C51004T */
++#define SM_MVC_29C31004T	0x63	/* Identical chips: {S,V}29C31004T */
++#define SM_MVC_29C31004B	0x73	/* Identical chips: {S,V}29C31004B */
++#define SM_MVC_29C51001B	0xA1	/* Identical chips: {F,S,V}29C51001B */
++#define SM_MVC_29C51002B	0xA2	/* Identical chips: {F,S,V}29C51002B */
++#define SM_MVC_29C51004B	0xA3	/* Identical chips: {F,S,V}29C51004B */
+ 
+ #define TI_ID			0x97	/* Texas Instruments */
+ #define TI_OLD_ID		0x01	/* TI chips from last century */
+@@ -509,6 +552,7 @@
+ #define W_25Q80			0x4014
+ #define W_25Q16			0x4015
+ #define W_25Q32			0x4016
++#define W_25Q64			0x4017
+ #define W_29C011		0xC1
+ #define W_29C020C		0x45	/* Same as W29C020 and ASD AE29F2008 */
+ #define W_29C040P		0x46	/* Same as W29C040 */
+diff --git a/flashrom.8 b/flashrom.8
+index 5ca14fc..47312a0 100644
+--- a/flashrom.8
++++ b/flashrom.8
+@@ -1,11 +1,11 @@
+-.TH FLASHROM 8 "Apr 29, 2010"
++.TH FLASHROM 8 "Jun 06, 2010"
+ .SH NAME
+ flashrom \- detect, read, write, verify and erase flash chips
+ .SH SYNOPSIS
+ .B flashrom \fR[\fB\-n\fR] [\fB\-V\fR] [\fB\-f\fR] [\fB\-h\fR|\fB\-R\fR|\
+ \fB\-L\fR|\fB\-z\fR|\fB\-E\fR|\fB\-r\fR <file>|\fB\-w\fR <file>|\
+ \fB\-v\fR <file>]
+-         [\fB\-c\fR <chipname>] [\fB\-m\fR [<vendor>:]<part>] \
++         [\fB\-c\fR <chipname>] [\fB\-m\fR [<vendor>:]<board>] \
+ [\fB\-l\fR <file>]
+          [\fB\-i\fR <image>] [\fB\-p\fR <programmername>[:<parameters>]]
+ .SH DESCRIPTION
+@@ -16,20 +16,24 @@ using a supported mainboard, but it also supports flashing of network cards
+ (NICs), SATA controller cards, and other external devices which can program
+ flash chips.
+ .PP
+-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,
++It supports a wide range of DIP32, PLCC32, DIP8, SO8/SOIC8, TSOP32, TSOP40,
++and TSOP48 chips, which use various protocols such as LPC, FWH, parallel flash,
+ or SPI.
+ .SH OPTIONS
++.B IMPORTANT:
+ Please note that the command line interface for flashrom will change before
+ flashrom 1.0. Do not use flashrom in scripts or other automated tools without
+ checking that your flashrom version won't interpret options in a different way.
+ .PP
+-You can specify one of \-h, \-R, \-L, \-z, \-E, \-r, \-w, \-v or no operation.
++You can specify one of
++.BR \-h ", " \-R ", " \-L ", " \-z ", " \-E ", " \-r ", " \-w ", " \-v
++or no operation.
+ If no operation is specified, flashrom will only probe for flash chips. It is
+ recommended that if you try flashrom the first time on a system, you run it
+ in probe only mode and check the output. Also you are advised to make a
+-backup of your current ROM contents with \-r before you try to write a new
+-image.
++backup of your current ROM contents with
++.B \-r
++before you try to write a new image.
+ .TP
+ .B "\-r, \-\-read <file>"
+ Read flash ROM contents and save them into the given
+@@ -38,7 +42,9 @@ Read flash ROM contents and save them into the given
+ .B "\-w, \-\-write <file>"
+ Write
+ .B <file>
+-into flash ROM.
++into flash ROM. This will first automatically
++.B erase
++the chip, then write to it.
+ .TP
+ .B "\-n, \-\-noverify"
+ Skip the automatic verification of flash ROM contents after writing. Using this
+@@ -48,7 +54,7 @@ recommended, you should only use it if you know what you are doing and if you
+ feel that the time for verification takes too long.
+ .sp
+ Typical usage is:
+-.B "flashrom -n -w file"
++.B "flashrom \-n \-w <file>"
+ .sp
+ This option is only useful in combination with
+ .BR \-\-write .
+@@ -61,34 +67,38 @@ Verify the flash ROM contents against the given
+ Erase the flash ROM chip.
+ .TP
+ .B "\-V, \-\-verbose"
+-More verbose output.
++More verbose output. This option can be supplied multiple times
++(max. 2 times, i.e.
++.BR \-VV )
++for even more debug output.
+ .TP
+ .B "\-c, \-\-chip" <chipname>
+-Probe only for specified flash ROM chip. This option takes the chip name as
++Probe only for the specified flash ROM chip. This option takes the chip name as
+ printed by
+ .B "flashrom \-L"
+-without the vendor name. Please note that the chip name is case sensitive.
++without the vendor name as parameter. Please note that the chip name is
++case sensitive.
+ .TP
+-.B "\-m, \-\-mainboard" [<vendor>:]<part>
++.B "\-m, \-\-mainboard" [<vendor>:]<board>
+ Override mainboard settings.
+ .sp
+ flashrom reads the coreboot table to determine the current mainboard. If no
+ coreboot table could be read or if you want to override these values, you can
+ specify \-m, e.g.:
+ .sp
+-.B "  flashrom --mainboard AGAMI:ARUMA -w agami_aruma.rom"
++.B "  flashrom \-\-mainboard AGAMI:ARUMA \-w agami_aruma.rom"
+ .sp
+-See the 'Supported mainboards' section in the output of 'flashrom \-L' for
+-a list of boards which require the specification of the board name, if no
+-coreboot table is found.
++See the 'Known boards' or 'Known laptops' section in the output
++of 'flashrom \-L' for a list of boards which require the specification of
++the board name, if no coreboot table is found.
+ .TP
+ .B "\-f, \-\-force"
+ Force one or more of the following actions:
+ .sp
+ * Force chip read and pretend the chip is there.
+ .sp
+-* Force chip access even if the chip is bigger than max decode size for\
+- the flash bus.
++* Force chip access even if the chip is bigger than the maximum supported\
++size for the flash bus.
+ .sp
+ * Force erase even if erase is known bad.
+ .sp
+@@ -111,13 +121,13 @@ the flash chip only. A ROM layout file looks like follows:
+ All addresses are offsets within the file, not absolute addresses!
+ If you only want to update the normal image in a ROM you can say:
+ .sp
+-.B "  flashrom --layout rom.layout --image normal -w agami_aruma.rom"
++.B "  flashrom \-\-layout rom.layout \-\-image normal \-w agami_aruma.rom"
+ .sp
+ To update normal and fallback but leave the VGA BIOS alone, say:
+ .sp
+-.B "  flashrom -l rom.layout -i normal \"
++.B "  flashrom \-l rom.layout \-i normal \"
+ .br
+-.B "           -i fallback -w agami_aruma.rom"
++.B "           \-i fallback \-w agami_aruma.rom"
+ .sp
+ Currently overlapping sections are not supported.
+ .TP
+@@ -132,7 +142,10 @@ supported by flashrom.
+ .sp
+ There are many unlisted boards which will work out of the box, without
+ special support in flashrom. Please let us know if you can verify that
+-other boards work or do not work out of the box. For verification you have
++other boards work or do not work out of the box.
++.sp
++.B IMPORTANT:
++For verification you have
+ to test an ERASE and/or WRITE operation, so make sure you only do that
+ if you have proper means to recover from failure!
+ .TP
+@@ -152,6 +165,10 @@ Specify the programmer device. Currently supported are:
+ .sp
+ .BR "* nic3com" " (for flash ROMs on 3COM network cards)"
+ .sp
++.BR "* nicrealtek" " (for flash ROMs on Realtek network cards)"
++.sp
++.BR "* nicsmc1211" " (for flash ROMs on RTL8139-compatible SMC2 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)"
+@@ -160,19 +177,27 @@ Specify the programmer device. Currently supported are:
+ .sp
+ .BR "* atahpt" " (for flash ROMs on Highpoint ATA/RAID controllers)"
+ .sp
+-.BR "* it87spi" " (for flash ROMs behind an ITE IT87xx Super I/O LPC/SPI translation unit)"
++.BR "* it87spi" " (for flash ROMs behind an ITE IT87xx Super I/O LPC/SPI \
++translation unit)"
++.sp
++.BR "* ft2232_spi" " (for SPI flash ROMs attached to a FT2232H/FT4232H/JTAGkey \
++based USB SPI programmer)"
++.sp
++.BR "* serprog" " (for flash ROMs attached to a programmer speaking serprog)"
+ .sp
+-.BR "* ft2232spi" " (for flash ROMs attached to a FT2232H/FT4232H based USB SPI programmer)"
++.BR "* buspirate_spi" " (for SPI flash ROMs attached to a Bus Pirate)"
+ .sp
+-.BR "* serprog" " (for flash ROMs attached to Urja's AVR programmer)"
++.BR "* rayer_spi" " (for SPI flash ROMs attached to a RayeR parport \
++based programmer)"
+ .sp
+-.BR "* buspiratespi" " (for flash ROMs attached to a Bus Pirate)"
++.BR "* nicintel_spi" " (for SPI flash ROMs attached to an Intel Gigabit \
++network cards)"
+ .sp
+ Some programmers have optional or mandatory parameters which are described
+ in detail in the
+ .B PROGRAMMER SPECIFIC INFO
+ section. Support for some programmers can be disabled at compile time.
+-.B "flashrom -h"
++.B "flashrom \-h"
+ lists all supported programmers.
+ .TP
+ .B "\-h, \-\-help"
+@@ -192,13 +217,16 @@ Some mainboards require to run mainboard specific code to enable flash erase
+ and write support (and probe support on old systems with parallel flash).
+ The mainboard brand and model (if it requires specific code) is usually
+ autodetected using one of the following mechanisms: If your system is
+-running coreboot, the mainboard type is determined from the coreboot table,
+-otherwise, the mainboard is detected by examining the onboard PCI devices
++running coreboot, the mainboard type is determined from the coreboot table.
++Otherwise, the mainboard is detected by examining the onboard PCI devices
+ and possibly DMI info. If PCI and DMI do not contain information to uniquely
+ identify the mainboard (which is the exception), it might be necessary to
+-specify the mainboard using the \-m switch (see above).
++specify the mainboard using the
++.B \-m
++switch (see above).
+ .sp
+-Some of these board-specific flash enabling functions (called board enables)
++Some of these board-specific flash enabling functions (called
++.BR "board enables" )
+ in flashrom have not yet been tested. If your mainboard is detected needing
+ an untested board enable function, a warning message is printed and the
+ board enable is not executed, because a wrong board enable function might
+@@ -213,15 +241,17 @@ and write (which includes erase).
+ The suggested procedure for a mainboard with untested board specific code is
+ to first try to probe the ROM (just invoke flashrom and check that it
+ detects your flash chip type) without running the board enable code (i.e.
+-without any parameters). If it finds your chip, fine, otherwise, retry
++without any parameters). If it finds your chip, fine. Otherwise, retry
+ probing your chip with the board-enable code running, using
+ .sp
+-.B "flashrom -p internal:boardenable=force"
++.B "  flashrom \-p internal:boardenable=force"
+ .sp
+ If your chip is still not detected, the board enable code seems to be broken
+ or the flash chip unsupported. Otherwise, make a backup of your current ROM
+-contents (using \-r) and store it to a medium outside of your computer, like
+-an USB drive or a network share. If you needed to run the board enable code
++contents (using
++.BR \-r )
++and store it to a medium outside of your computer, like
++a USB drive or a network share. If you needed to run the board enable code
+ already for probing, use it for reading too. Now you can try to write the
+ new image. You should enable the board enable code in any case now, as it
+ has been written because it is known that writing/erasing without the board
+@@ -233,21 +263,27 @@ your mainboard. This needs some special board ID to be present in the image.
+ If flashrom detects that the image you want to write and the current board
+ do not match, it will refuse to write the image unless you specify
+ .sp
+-.B "flashrom -p internal:boardmismatch=force"
++.B "  flashrom \-p internal:boardmismatch=force"
+ .sp
+ If your mainboard uses an ITE IT87 series Super I/O for LPC<->SPI flash bus
+-translation, flashrom should autodetect that configuration. You can use
++translation, flashrom should autodetect that configuration. You can use the
++.sp
++.B "  flashrom \-p internal:it87spiport=portnum"
+ .sp
+-.B "flashrom -p internal:it87spiport=portnum"
+ syntax as explained in the
+ .B it87spi
+ programmer section to use a non-default port for controlling the IT87 series
+-Super I/O. In the unlikely case flashrom doesn't detect an active IT87 LPC<->SPI
+-bridge, you can try to force recognition by using the it87spi programmer.
++Super I/O. In the unlikely case flashrom doesn't detect an active
++IT87 LPC<->SPI bridge, you can try to force recognition by using the
++.B it87spi
++programmer.
+ .sp
+ Using flashrom on laptops is dangerous and may easily make your hardware
+-unusable (see also the BUGS section).  The embedded controller (EC) in these
+-machines often interacts badly with flashing. http://www.flashrom.org/Laptops
++unusable (see also the
++.B BUGS
++section). The embedded controller (EC) in these
++machines often interacts badly with flashing.
++.B http://www.flashrom.org/Laptops
+ has more information. If flash is shared with the EC, erase is guaranteed to
+ brick your laptop and write is very likely to brick your laptop.
+ Chip read and probe may irritate your EC and cause fan failure, backlight
+@@ -256,7 +292,7 @@ flashrom will attempt to detect laptops and abort immediately for safety
+ reasons.
+ If you want to proceed anyway at your own risk, use
+ .sp
+-.B "flashrom -p internal:laptop=force_I_want_a_brick"
++.B "  flashrom \-p internal:laptop=force_I_want_a_brick"
+ .sp
+ You have been warned.
+ .sp
+@@ -266,22 +302,24 @@ dumb idea.
+ .BR "dummy " programmer
+ An optional parameter specifies the bus types it
+ should support. For that you have to use the
+-.B "flashrom -p dummy:type"
++.B "flashrom \-p dummy:bus=[type[+type[+type]]]"
+ syntax where
+ .B type
+-can be any comma-separated combination of
+-.B parallel lpc fwh spi all
+-in any order.
++can be any of
++.BR parallel ", " lpc ", " fwh ", " spi
++in any order. If you specify bus without type, all buses will be disabled.
++If you do not specify bus, all buses will be enabled.
+ .sp
+ Example:
+-.B "flashrom -p dummy:lpc,fwh"
++.B "flashrom \-p dummy:bus=lpc+fwh"
+ .TP
+-.BR "nic3com" , " gfxnvidia" , " satasii " and " atahpt " programmers
++.BR "nic3com" , " nicrealtek" , " nicsmc1211" , " gfxnvidia" , " satasii \
++" and " atahpt " programmers
+ These programmers have an option to specify the PCI address of the card
+ your want to use, which must be specified if more than one card supported
+ by the selected programmer is installed in your system. The syntax is
+-.B "flashrom -p xxxx:bb:dd.f"
+-, where
++.BR "flashrom \-p xxxx:pci=bb:dd.f" ,
++where
+ .B xxxx
+ is the name of the programmer
+ .B bb
+@@ -289,32 +327,38 @@ is the PCI bus number,
+ .B dd
+ is the PCI device number, and
+ .B f
+-is the PCI function number of the desired NIC.
++is the PCI function number of the desired device.
+ .sp
+ Example:
+-.B "flashrom -p nic3com:05:04.0"
++.B "flashrom \-p nic3com:pci=05:04.0"
+ .TP
+ .BR "it87spi " programmer
+-An optional parameter sets the I/O base port of the IT87* SPI controller
++An optional
++.B it87spiport
++parameter sets the I/O base port of the IT87 series SPI controller
+ interface to the port specified in the parameter instead of using the port
+ address set by the BIOS. For that you have to use the
+-.B "flashrom -p it87spi:it87spiport=portnum"
++.sp
++.B "  flashrom \-p it87spi:it87spiport=portnum"
++.sp
+ syntax where
+ .B portnum
+ is an I/O port number which must be a multiple of 8.
+ .TP
+-.BR "ft2232spi " programmer
+-An optional parameter species the controller
++.BR "ft2232_spi " programmer
++An optional parameter specifies the controller
+ type and interface/port it should support. For that you have to use the
+-.B "flashrom -p ft2232spi:model,port=interface"
++.sp
++.B "  flashrom \-p ft2232_spi:type=model,port=interface"
++.sp
+ syntax where
+ .B model
+ can be any of
+-.B 2232H 4232H
++.BR 2232H ", " JTAGkey ", or " 4232H
+ and
+ .B interface
+ can be any of
+-.BR "A B" .
++.BR A ", or " B .
+ The default model is
+ .B 4232H
+ and the default interface is
+@@ -325,32 +369,95 @@ A mandatory parameter specifies either a serial
+ device/baud combination or an IP/port combination for communication with the
+ programmer. In the device/baud combination, the device has to start with a
+ slash. For serial, you have to use the
+-.B "flashrom -p serprog:/dev/device:baud"
++.sp
++.B "  flashrom \-p serprog:dev=/dev/device:baud"
++.sp
+ syntax and for IP, you have to use
+-.B "flashrom -p serprog:ip:port"
+-instead. More information about serprog is available in serprog-protocol.txt in
+-the source distribution.
++.sp
++.B "  flashrom \-p serprog:ip=ipaddr:port"
++.sp
++instead. More information about serprog is available in
++.B serprog-protocol.txt
++in the source distribution.
+ .TP
+-.BR "buspiratespi " programmer
+-A required dev parameter specifies the Bus Pirate device node and an optional
+-spispeed parameter specifies the frequency of the SPI bus. The parameter
++.BR "buspirate_spi " programmer
++A required
++.B dev
++parameter specifies the Bus Pirate device node and an optional
++.B spispeed
++parameter specifies the frequency of the SPI bus. The parameter
+ delimiter is a comma. Syntax is
+ .sp
+-.B "flashrom -p buspiratespi:dev=/dev/device,spispeed=frequency"
++.B "flashrom \-p buspirate_spi:dev=/dev/device,spispeed=frequency"
+ .sp
+ where
+ .B frequency
+ can be any of
+-.B 30k 125k 250k 1M 2M 2.6M 4M 8M
++.BR 30k ", " 125k ", " 250k ", " 1M ", " 2M ", " 2.6M ", " 4M ", " 8M
+ (in Hz). The default is the maximum frequency of 8 MHz.
++.TP
++.BR "rayer_spi " programmer
++No parameters defined yet. More information about the hardware is available at
++http://rayer.ic.cz/elektro/spipgm.htm
+ .SH EXIT STATUS
+ flashrom exits with 0 on success, 1 on most failures but with 2 if /dev/mem
+ (/dev/xsvc on Solaris) can not be opened and with 3 if a call to mmap() fails.
++.SH REQUIREMENTS
++flashrom needs different access permissions for different programmers.
++.sp
++.B internal
++needs raw memory access, PCI configuration space access, raw I/O port
++access (x86) and MSR access (x86).
++.sp
++.B it87spi
++needs raw I/O port access (x86).
++.sp
++.BR nic3com ", " nicrealtek ", " nicsmc1211 " and " nicnatsemi "
++need PCI configuration space read access and raw I/O port access.
++.sp
++.B atahpt
++needs PCI configuration space access and raw I/O port access.
++.sp
++.BR gfxnvidia " and " drkaiser
++need PCI configuration space access and raw memory access.
++.sp
++.B rayer_spi
++needs raw I/O port access.
++.sp
++.B satasii
++needs PCI configuration space read access and raw memory access.
++.sp
++.B serprog
++needs TCP access to the network or userspace access to a serial port.
++.sp
++.B buspirate_spi
++needs userspace access to a serial port.
++.sp
++.BR dediprog " and " ft2232_spi
++need access to the USB device via libusb.
++.sp
++.B dummy
++needs no access permissions at all.
++.sp
++.BR internal ", " it87spi ", " nic3com ", " nicrealtek ", " nicsmc1211 ", "
++.BR nicnatsemi ", " "gfxnvidia" ", " drkaiser ", " satasii " and " atahpt
++have to be run as superuser/root, and need additional raw access permission.
++.sp
++.BR serprog ", " buspirate_spi ", " dediprog " and " ft2232_spi
++can be run as normal user on most operating systems if appropriate device
++permissions are set.
++.sp
++On OpenBSD, you can obtain raw access permission by setting
++securelevel=-1 in /etc/rc.securelevel and rebooting, or rebooting into single
++user mode.
+ .SH BUGS
+ Please report any bugs at
+-.BR http://www.flashrom.org/trac/flashrom/newticket ","
++.sp
++.B "  http://www.flashrom.org/trac/flashrom/newticket"
++.sp
+ or on the flashrom mailing list at
+-.BR http://www.flashrom.org/mailman/listinfo/flashrom "."
++.sp
++.B "  http://www.flashrom.org/mailman/listinfo/flashrom"
+ .sp
+ Using flashrom on laptops is dangerous and may easily make your hardware
+ unusable unless you can desolder the flash chip and have a full flash chip
+@@ -411,6 +518,6 @@ Yinghai Lu
+ .br
+ some others 
+ .PP
+-This manual page was written by Uwe Hermann <uwe at hermann-uwe.de> and Carl-Daniel
+-Hailfinger.
++This manual page was written by Uwe Hermann <uwe at hermann-uwe.de>
++and Carl-Daniel Hailfinger.
+ It is licensed under the terms of the GNU GPL (version 2 or later).
+diff --git a/flashrom.c b/flashrom.c
+index 1b01381..f722029 100644
+--- a/flashrom.c
++++ b/flashrom.c
+@@ -21,6 +21,7 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
++#include <stdio.h>
+ #include <fcntl.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+@@ -32,14 +33,15 @@
+ #endif
+ #include "flash.h"
+ #include "flashchips.h"
++#include "programmer.h"
+ 
+-const char *flashrom_version = FLASHROM_VERSION;
++const char * const flashrom_version = FLASHROM_VERSION;
+ char *chip_to_probe = NULL;
+ int verbose = 0;
+ 
+-#if INTERNAL_SUPPORT == 1
++#if CONFIG_INTERNAL == 1
+ enum programmer programmer = PROGRAMMER_INTERNAL;
+-#elif DUMMY_SUPPORT == 1
++#elif CONFIG_DUMMY == 1
+ enum programmer programmer = PROGRAMMER_DUMMY;
+ #else
+ /* If neither internal nor dummy are selected, we must pick a sensible default.
+@@ -47,62 +49,72 @@ enum programmer programmer = PROGRAMMER_DUMMY;
+  * 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+ATAHPT_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.
++#if CONFIG_NIC3COM+CONFIG_NICREALTEK+CONFIG_NICNATSEMI+CONFIG_GFXNVIDIA+CONFIG_DRKAISER+CONFIG_SATASII+CONFIG_ATAHPT+CONFIG_FT2232_SPI+CONFIG_SERPROG+CONFIG_BUSPIRATE_SPI+CONFIG_DEDIPROG+CONFIG_RAYER_SPI+CONFIG_NICINTEL_SPI > 1
++#error Please enable either CONFIG_DUMMY or CONFIG_INTERNAL or disable support for all programmers except one.
+ #endif
+ enum programmer programmer =
+-#if NIC3COM_SUPPORT == 1
++#if CONFIG_NIC3COM == 1
+ 	PROGRAMMER_NIC3COM
+ #endif
+-#if GFXNVIDIA_SUPPORT == 1
++#if CONFIG_NICREALTEK == 1
++	PROGRAMMER_NICREALTEK
++	PROGRAMMER_NICREALTEK2
++#endif
++#if CONFIG_NICNATSEMI == 1
++	PROGRAMMER_NICNATSEMI
++#endif
++#if CONFIG_GFXNVIDIA == 1
+ 	PROGRAMMER_GFXNVIDIA
+ #endif
+-#if DRKAISER_SUPPORT == 1
++#if CONFIG_DRKAISER == 1
+ 	PROGRAMMER_DRKAISER
+ #endif
+-#if SATASII_SUPPORT == 1
++#if CONFIG_SATASII == 1
+ 	PROGRAMMER_SATASII
+ #endif
+-#if ATAHPT_SUPPORT == 1
++#if CONFIG_ATAHPT == 1
+ 	PROGRAMMER_ATAHPT
+ #endif
+-#if FT2232_SPI_SUPPORT == 1
+-	PROGRAMMER_FT2232SPI
++#if CONFIG_FT2232_SPI == 1
++	PROGRAMMER_FT2232_SPI
+ #endif
+-#if SERPROG_SUPPORT == 1
++#if CONFIG_SERPROG == 1
+ 	PROGRAMMER_SERPROG
+ #endif
+-#if BUSPIRATE_SPI_SUPPORT == 1
+-	PROGRAMMER_BUSPIRATESPI
++#if CONFIG_BUSPIRATE_SPI == 1
++	PROGRAMMER_BUSPIRATE_SPI
+ #endif
+-#if DEDIPROG_SUPPORT == 1
++#if CONFIG_DEDIPROG == 1
+ 	PROGRAMMER_DEDIPROG
+ #endif
++#if CONFIG_RAYER_SPI == 1
++	PROGRAMMER_RAYER_SPI
++#endif
++#if CONFIG_NICINTEL_SPI == 1
++	PROGRAMMER_NICINTEL_SPI
++#endif
+ ;
+ #endif
+ 
+-char *programmer_param = NULL;
++static 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;
++/* Supported buses for the current programmer. */
++enum chipbustype buses_supported;
+ 
+-/**
++/*
+  * 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
+-};
++struct decode_sizes max_rom_decode;
++
++/* If nonzero, used as the start address of bottom-aligned flash. */
++unsigned long flashbase;
++
++/* Is writing allowed with this programmer? */
++int programmer_may_write;
+ 
+ const struct programmer_entry programmer_table[] = {
+-#if INTERNAL_SUPPORT == 1
++#if CONFIG_INTERNAL == 1
+ 	{
+ 		.name			= "internal",
+ 		.init			= internal_init,
+@@ -121,7 +133,7 @@ const struct programmer_entry programmer_table[] = {
+ 	},
+ #endif
+ 
+-#if DUMMY_SUPPORT == 1
++#if CONFIG_DUMMY == 1
+ 	{
+ 		.name			= "dummy",
+ 		.init			= dummy_init,
+@@ -140,7 +152,7 @@ const struct programmer_entry programmer_table[] = {
+ 	},
+ #endif
+ 
+-#if NIC3COM_SUPPORT == 1
++#if CONFIG_NIC3COM == 1
+ 	{
+ 		.name			= "nic3com",
+ 		.init			= nic3com_init,
+@@ -159,7 +171,61 @@ const struct programmer_entry programmer_table[] = {
+ 	},
+ #endif
+ 
+-#if GFXNVIDIA_SUPPORT == 1
++#if CONFIG_NICREALTEK == 1
++	{
++		.name                   = "nicrealtek",
++		.init                   = nicrealtek_init,
++		.shutdown               = nicrealtek_shutdown,
++		.map_flash_region       = fallback_map,
++		.unmap_flash_region     = fallback_unmap,
++		.chip_readb             = nicrealtek_chip_readb,
++		.chip_readw             = fallback_chip_readw,
++		.chip_readl             = fallback_chip_readl,
++		.chip_readn             = fallback_chip_readn,
++		.chip_writeb            = nicrealtek_chip_writeb,
++		.chip_writew            = fallback_chip_writew,
++		.chip_writel            = fallback_chip_writel,
++		.chip_writen            = fallback_chip_writen,
++		.delay                  = internal_delay,
++	},
++	{
++		.name                   = "nicsmc1211",
++		.init                   = nicsmc1211_init,
++		.shutdown               = nicrealtek_shutdown,
++		.map_flash_region       = fallback_map,
++		.unmap_flash_region     = fallback_unmap,
++		.chip_readb             = nicrealtek_chip_readb,
++		.chip_readw             = fallback_chip_readw,
++		.chip_readl             = fallback_chip_readl,
++		.chip_readn             = fallback_chip_readn,
++		.chip_writeb            = nicrealtek_chip_writeb,
++		.chip_writew            = fallback_chip_writew,
++		.chip_writel            = fallback_chip_writel,
++		.chip_writen            = fallback_chip_writen,
++		.delay                  = internal_delay,
++	},
++#endif
++
++#if CONFIG_NICNATSEMI == 1
++	{
++		.name                   = "nicnatsemi",
++		.init                   = nicnatsemi_init,
++		.shutdown               = nicnatsemi_shutdown,
++		.map_flash_region       = fallback_map,
++		.unmap_flash_region     = fallback_unmap,
++		.chip_readb             = nicnatsemi_chip_readb,
++		.chip_readw             = fallback_chip_readw,
++		.chip_readl             = fallback_chip_readl,
++		.chip_readn             = fallback_chip_readn,
++		.chip_writeb            = nicnatsemi_chip_writeb,
++		.chip_writew            = fallback_chip_writew,
++		.chip_writel            = fallback_chip_writel,
++		.chip_writen            = fallback_chip_writen,
++		.delay                  = internal_delay,
++	},
++#endif
++
++#if CONFIG_GFXNVIDIA == 1
+ 	{
+ 		.name			= "gfxnvidia",
+ 		.init			= gfxnvidia_init,
+@@ -178,7 +244,7 @@ const struct programmer_entry programmer_table[] = {
+ 	},
+ #endif
+ 
+-#if DRKAISER_SUPPORT == 1
++#if CONFIG_DRKAISER == 1
+ 	{
+ 		.name			= "drkaiser",
+ 		.init			= drkaiser_init,
+@@ -197,7 +263,7 @@ const struct programmer_entry programmer_table[] = {
+ 	},
+ #endif
+ 
+-#if SATASII_SUPPORT == 1
++#if CONFIG_SATASII == 1
+ 	{
+ 		.name			= "satasii",
+ 		.init			= satasii_init,
+@@ -216,7 +282,7 @@ const struct programmer_entry programmer_table[] = {
+ 	},
+ #endif
+ 
+-#if ATAHPT_SUPPORT == 1
++#if CONFIG_ATAHPT == 1
+ 	{
+ 		.name			= "atahpt",
+ 		.init			= atahpt_init,
+@@ -235,7 +301,8 @@ const struct programmer_entry programmer_table[] = {
+ 	},
+ #endif
+ 
+-#if INTERNAL_SUPPORT == 1
++#if CONFIG_INTERNAL == 1
++#if defined(__i386__) || defined(__x86_64__)
+ 	{
+ 		.name			= "it87spi",
+ 		.init			= it87spi_init,
+@@ -253,10 +320,11 @@ const struct programmer_entry programmer_table[] = {
+ 		.delay			= internal_delay,
+ 	},
+ #endif
++#endif
+ 
+-#if FT2232_SPI_SUPPORT == 1
++#if CONFIG_FT2232_SPI == 1
+ 	{
+-		.name			= "ft2232spi",
++		.name			= "ft2232_spi",
+ 		.init			= ft2232_spi_init,
+ 		.shutdown		= noop_shutdown, /* Missing shutdown */
+ 		.map_flash_region	= fallback_map,
+@@ -273,7 +341,7 @@ const struct programmer_entry programmer_table[] = {
+ 	},
+ #endif
+ 
+-#if SERPROG_SUPPORT == 1
++#if CONFIG_SERPROG == 1
+ 	{
+ 		.name			= "serprog",
+ 		.init			= serprog_init,
+@@ -292,9 +360,9 @@ const struct programmer_entry programmer_table[] = {
+ 	},
+ #endif
+ 
+-#if BUSPIRATE_SPI_SUPPORT == 1
++#if CONFIG_BUSPIRATE_SPI == 1
+ 	{
+-		.name			= "buspiratespi",
++		.name			= "buspirate_spi",
+ 		.init			= buspirate_spi_init,
+ 		.shutdown		= buspirate_spi_shutdown,
+ 		.map_flash_region	= fallback_map,
+@@ -311,7 +379,7 @@ const struct programmer_entry programmer_table[] = {
+ 	},
+ #endif
+ 
+-#if DEDIPROG_SUPPORT == 1
++#if CONFIG_DEDIPROG == 1
+ 	{
+ 		.name			= "dediprog",
+ 		.init			= dediprog_init,
+@@ -330,6 +398,44 @@ const struct programmer_entry programmer_table[] = {
+ 	},
+ #endif
+ 
++#if CONFIG_RAYER_SPI == 1
++	{
++		.name			= "rayer_spi",
++		.init			= rayer_spi_init,
++		.shutdown		= noop_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 CONFIG_NICINTEL_SPI == 1
++	{
++		.name = "nicintel_spi",
++		.init = nicintel_spi_init,
++		.shutdown = nicintel_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
++
+ 	{}, /* This entry corresponds to PROGRAMMER_INVALID. */
+ };
+ 
+@@ -338,7 +444,11 @@ static int shutdown_fn_count = 0;
+ struct shutdown_func_data {
+ 	void (*func) (void *data);
+ 	void *data;
+-} shutdown_fn[SHUTDOWN_MAXFN];
++} static shutdown_fn[SHUTDOWN_MAXFN];
++/* Initialize to 0 to make sure nobody registers a shutdown function before
++ * programmer init.
++ */
++static int may_register_shutdown = 0;
+ 
+ /* Register a function to be executed on programmer shutdown.
+  * The advantage over atexit() is that you can supply a void pointer which will
+@@ -351,10 +461,15 @@ struct shutdown_func_data {
+ int register_shutdown(void (*function) (void *data), void *data)
+ {
+ 	if (shutdown_fn_count >= SHUTDOWN_MAXFN) {
+-		msg_perr("Tried to register more than %n shutdown functions.\n",
++		msg_perr("Tried to register more than %i shutdown functions.\n",
+ 			 SHUTDOWN_MAXFN);
+ 		return 1;
+ 	}
++	if (!may_register_shutdown) {
++		msg_perr("Tried to register a shutdown function before "
++			 "programmer init.\n");
++		return 1;
++	}
+ 	shutdown_fn[shutdown_fn_count].func = function;
+ 	shutdown_fn[shutdown_fn_count].data = data;
+ 	shutdown_fn_count++;
+@@ -362,17 +477,48 @@ int register_shutdown(void (*function) (void *data), void *data)
+ 	return 0;
+ }
+ 
+-int programmer_init(void)
++int programmer_init(char *param)
+ {
+-	return programmer_table[programmer].init();
++	int ret;
++	/* Initialize all programmer specific data. */
++	/* Default to unlimited decode sizes. */
++	max_rom_decode = (const struct decode_sizes) {
++		.parallel	= 0xffffffff,
++		.lpc		= 0xffffffff,
++		.fwh		= 0xffffffff,
++		.spi		= 0xffffffff
++	};
++	/* Default to Parallel/LPC/FWH flash devices. If a known host controller
++	 * is found, the init routine sets the buses_supported bitfield.
++	 */
++	buses_supported = CHIP_BUSTYPE_NONSPI;
++	/* Default to top aligned flash at 4 GB. */
++	flashbase = 0;
++	/* Registering shutdown functions is now allowed. */
++	may_register_shutdown = 1;
++	/* Default to allowing writes. Broken programmers set this to 0. */
++	programmer_may_write = 1;
++
++	programmer_param = param;
++	msg_pdbg("Initializing %s programmer\n",
++		 programmer_table[programmer].name);
++	ret = programmer_table[programmer].init();
++	if (programmer_param && strlen(programmer_param)) {
++		msg_perr("Unhandled programmer parameters: %s\n",
++			 programmer_param);
++		/* Do not error out here, the init itself was successful. */
++	}
++	return ret;
+ }
+ 
+ int programmer_shutdown(void)
+ {
+-	int i;
+-
+-	for (i = shutdown_fn_count - 1; i >= 0; i--)
++	/* Registering shutdown functions is no longer allowed. */
++	may_register_shutdown = 0;
++	while (shutdown_fn_count > 0) {
++		int i = --shutdown_fn_count;
+ 		shutdown_fn[i].func(shutdown_fn[i].data);
++	}
+ 	return programmer_table[programmer].shutdown();
+ }
+ 
+@@ -448,8 +594,6 @@ 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;
+@@ -472,22 +616,24 @@ int bitcount(unsigned long a)
+ char *strcat_realloc(char *dest, const char *src)
+ {
+ 	dest = realloc(dest, strlen(dest) + strlen(src) + 1);
+-	if (!dest)
++	if (!dest) {
++		msg_gerr("Out of memory!\n");
+ 		return NULL;
++	}
+ 	strcat(dest, 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.
++ * It will look for needle with a subsequent '=' 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;
++	char *param_pos, *opt_pos, *rest;
++	char *opt = NULL;
++	int optlen;
+ 	int needlelen;
+ 
+ 	needlelen = strlen(needle);
+@@ -503,43 +649,46 @@ char *extract_param(char **haystack, char *needle, char *delim)
+ 	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;
++		/* Needle followed by '='? */
++		if (param_pos[needlelen] == '=') {
++			
++			/* 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) {
+-				msg_gerr("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) {
++		/* Get the string after needle and '='. */
++		opt_pos = param_pos + needlelen + 1;
++		optlen = strcspn(opt_pos, delim);
++		/* Return an empty string if the parameter was empty. */
++		opt = malloc(optlen + 1);
++		if (!opt) {
+ 			msg_gerr("Out of memory!\n");
+ 			exit(1);
+ 		}
+-		*haystack = tmp;
++		strncpy(opt, opt_pos, optlen);
++		opt[optlen] = '\0';
++		rest = opt_pos + optlen;
++		/* Skip all delimiters after the current parameter. */
++		rest += strspn(rest, delim);
++		memmove(param_pos, rest, strlen(rest) + 1);
++		/* We could shrink haystack, but the effort is not worth it. */
+ 	}
+-	
+ 
+-	return dev;
++	return opt;
++}
++
++char *extract_programmer_param(char *param_name)
++{
++	return extract_param(&programmer_param, param_name, ",");
+ }
+ 
+ /* start is an offset to the base address of the flash chip */
+@@ -558,7 +707,7 @@ int check_erased_range(struct flashchip *flash, int start, int len)
+ 	return ret;
+ }
+ 
+-/**
++/*
+  * @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
+@@ -609,7 +758,12 @@ int verify_range(struct flashchip *flash, uint8_t *cmpbuf, int start, int len, c
+ 		starthere = max(start, i * page_size);
+ 		/* Length of bytes in the range in this page. */
+ 		lenhere = min(start + len, (i + 1) * page_size) - starthere;
+-		flash->read(flash, readbuf, starthere, lenhere);
++		ret = flash->read(flash, readbuf, starthere, lenhere);
++		if (ret) {
++			msg_gerr("Verification impossible because read failed "
++				 "at 0x%x (len 0x%x)\n", starthere, lenhere);
++			break;
++		}
+ 		for (j = 0; j < lenhere; j++) {
+ 			if (cmpbuf[starthere - start + j] != readbuf[j]) {
+ 				/* Only print the first failure. */
+@@ -633,7 +787,7 @@ out_free:
+ 	return ret;
+ }
+ 
+-/**
++/*
+  * Check if the buffer @have can be programmed to the content of @want without
+  * erasing. This is only possible if all chunks of size @gran are either kept
+  * as-is or changed from an all-ones state to any other state.
+@@ -960,42 +1114,64 @@ int verify_flash(struct flashchip *flash, uint8_t *buf)
+ 	return ret;
+ }
+ 
+-int read_flash(struct flashchip *flash, char *filename)
++int write_buf_to_file(unsigned char *buf, unsigned long size, char *filename)
+ {
+ 	unsigned long numbytes;
+ 	FILE *image;
+-	unsigned long size = flash->total_size * 1024;
+-	unsigned char *buf = calloc(size, sizeof(char));
+ 
+ 	if (!filename) {
+-		msg_gerr("Error: No filename specified.\n");
++		msg_gerr("No filename specified.\n");
+ 		return 1;
+ 	}
+ 	if ((image = fopen(filename, "wb")) == NULL) {
+ 		perror(filename);
+-		exit(1);
+-	}
+-	msg_cinfo("Reading flash... ");
+-	if (!flash->read) {
+-		msg_cinfo("FAILED!\n");
+-		msg_cerr("ERROR: flashrom has no read function for this flash chip.\n");
+ 		return 1;
+-	} else
+-		flash->read(flash, buf, 0, size);
++	}
+ 
+ 	numbytes = fwrite(buf, 1, size, image);
+ 	fclose(image);
+-	free(buf);
+-	msg_cinfo("%s.\n", numbytes == size ? "done" : "FAILED");
+-	if (numbytes != size)
++	if (numbytes != size) {
++		msg_gerr("File %s could not be written completely.\n",
++			 filename);
+ 		return 1;
++	}
+ 	return 0;
+ }
+ 
++int read_flash_to_file(struct flashchip *flash, char *filename)
++{
++	unsigned long size = flash->total_size * 1024;
++	unsigned char *buf = calloc(size, sizeof(char));
++	int ret = 0;
++
++	msg_cinfo("Reading flash... ");
++	if (!buf) {
++		msg_gerr("Memory allocation failed!\n");
++		msg_cinfo("FAILED.\n");
++		return 1;
++	}
++	if (!flash->read) {
++		msg_cerr("No read function available for this flash chip.\n");
++		ret = 1;
++		goto out_free;
++	}
++	if (flash->read(flash, buf, 0, size)) {
++		msg_cerr("Read operation failed!\n");
++		ret = 1;
++		goto out_free;
++	}
++
++	ret = write_buf_to_file(buf, flash->total_size * 1024, filename);
++out_free:
++	free(buf);
++	msg_cinfo("%s.\n", ret ? "FAILED" : "done");
++	return ret;
++}
++
+ /* 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)
++static int selfcheck_eraseblocks(struct flashchip *flash)
+ {
+ 	int i, j, k;
+ 	int ret = 0;
+@@ -1060,14 +1236,37 @@ int selfcheck_eraseblocks(struct flashchip *flash)
+ 	return ret;
+ }
+ 
++static int walk_eraseregions(struct flashchip *flash, int erasefunction,
++			     int (*do_something) (struct flashchip *flash,
++						  unsigned int addr,
++						  unsigned int len))
++{
++	int i, j;
++	unsigned int start = 0;
++	unsigned int len;
++	struct block_eraser eraser = flash->block_erasers[erasefunction];
++	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.
++		 */
++		len = eraser.eraseblocks[i].size;
++		for (j = 0; j < eraser.eraseblocks[i].count; j++) {
++			msg_cdbg("0x%06x-0x%06x, ", start,
++				     start + len - 1);
++			if (do_something(flash, start, len))
++				return 1;
++			start += len;
++		}
++	}
++	return 0;
++}
++
+ int erase_flash(struct flashchip *flash)
+ {
+-	int i, j, k, ret = 0, found = 0;
+-	unsigned int start, len;
++	int k, ret = 0, found = 0;
+ 
+ 	msg_cinfo("Erasing flash chip... ");
+ 	for (k = 0; k < NUM_ERASEFUNCTIONS; k++) {
+-		unsigned int done = 0;
+ 		struct block_eraser eraser = flash->block_erasers[k];
+ 
+ 		msg_cdbg("Looking at blockwise erase function %i... ", k);
+@@ -1090,24 +1289,7 @@ int erase_flash(struct flashchip *flash)
+ 		}
+ 		found = 1;
+ 		msg_cdbg("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;
+-				msg_cdbg("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;
+-		}
++		ret = walk_eraseregions(flash, k, eraser.block_erase);
+ 		msg_cdbg("\n");
+ 		/* If everything is OK, don't try another erase function. */
+ 		if (!ret)
+@@ -1168,16 +1350,26 @@ void print_sysinfo(void)
+ #endif
+ #endif
+ #ifdef __clang__
+-	msg_ginfo(" LLVM %i/clang %i", __llvm__, __clang__);
++	msg_ginfo(" LLVM Clang");
++#ifdef __clang_version__
++	msg_ginfo(" %s,", __clang_version__);
++#else
++	msg_ginfo(" unknown version (before r102686),");
++#endif
+ #elif defined(__GNUC__)
+ 	msg_ginfo(" GCC");
+ #ifdef __VERSION__
+-	msg_ginfo(" %s", __VERSION__);
++	msg_ginfo(" %s,", __VERSION__);
+ #else
+-	msg_ginfo(" unknown version");
++	msg_ginfo(" unknown version,");
+ #endif
+ #else
+-	msg_ginfo(" unknown compiler");
++	msg_ginfo(" unknown compiler,");
++#endif
++#if defined (__FLASHROM_LITTLE_ENDIAN__)
++	msg_ginfo(" little endian");
++#else
++	msg_ginfo(" big endian");
+ #endif
+ 	msg_ginfo("\n");
+ }
+@@ -1211,12 +1403,6 @@ int selfcheck(void)
+ 		msg_gerr("SPI programmer table miscompilation!\n");
+ 		ret = 1;
+ 	}
+-#if BITBANG_SPI_SUPPORT == 1
+-	if (bitbang_spi_master_count - 1 != BITBANG_SPI_INVALID) {
+-		msg_gerr("Bitbanging SPI master table miscompilation!\n");
+-		ret = 1;
+-	}
+-#endif
+ 	for (flash = flashchips; flash && flash->name; flash++)
+ 		if (selfcheck_eraseblocks(flash))
+ 			ret = 1;
+@@ -1291,6 +1477,21 @@ int doit(struct flashchip *flash, int force, char *filename, int read_it, int wr
+ 	size = flash->total_size * 1024;
+ 	buf = (uint8_t *) calloc(size, sizeof(char));
+ 
++	if (!programmer_may_write && (write_it || erase_it)) {
++		msg_perr("Write/erase is not working yet on your programmer in "
++			 "its current configuration.\n");
++		/* --force is the wrong approach, but it's the best we can do
++		 * until the generic programmer parameter parser is merged.
++		 */
++		if (!force) {
++			msg_perr("Aborting.\n");
++			programmer_shutdown();
++			return 1;
++		} else {
++			msg_cerr("Continuing anyway.\n");
++		}
++	}
++
+ 	if (erase_it) {
+ 		if (flash->tested & TEST_BAD_ERASE) {
+ 			msg_cerr("Erase is not working on this chip. ");
+@@ -1314,7 +1515,7 @@ int doit(struct flashchip *flash, int force, char *filename, int read_it, int wr
+ 		if (flash->unlock)
+ 			flash->unlock(flash);
+ 
+-		if (read_flash(flash, filename)) {
++		if (read_flash_to_file(flash, filename)) {
+ 			programmer_shutdown();
+ 			return 1;
+ 		}
+@@ -1362,7 +1563,7 @@ int doit(struct flashchip *flash, int force, char *filename, int read_it, int wr
+ 		}
+ 
+ 		numbytes = fread(buf, 1, size, image);
+-#if INTERNAL_SUPPORT == 1
++#if CONFIG_INTERNAL == 1
+ 		show_id(buf, size, force);
+ #endif
+ 		fclose(image);
+diff --git a/ft2232_spi.c b/ft2232_spi.c
+index 97b4867..0ffe1ce 100644
+--- a/ft2232_spi.c
++++ b/ft2232_spi.c
+@@ -2,7 +2,7 @@
+  * This file is part of the flashrom project.
+  *
+  * Copyright (C) 2009 Paul Fox <pgf at laptop.org>
+- * Copyright (C) 2009 Carl-Daniel Hailfinger
++ * 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
+@@ -18,7 +18,7 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
+-#if FT2232_SPI_SUPPORT == 1
++#if CONFIG_FT2232_SPI == 1
+ 
+ #include <stdio.h>
+ #include <stdint.h>
+@@ -27,9 +27,22 @@
+ #include <ctype.h>
+ #include "flash.h"
+ #include "chipdrivers.h"
++#include "programmer.h"
+ #include "spi.h"
+ #include <ftdi.h>
+ 
++#define FTDI_VID		0x0403
++#define FTDI_FT2232H_PID	0x6010
++#define FTDI_FT4232H_PID	0x6011
++#define AMONTEC_JTAGKEY_PID	0xCFF8
++
++const struct usbdev_status devs_ft2232spi[] = {
++	{FTDI_VID, FTDI_FT2232H_PID, OK, "FTDI", "FT2232H"},
++	{FTDI_VID, FTDI_FT4232H_PID, OK, "FTDI", "FT4232H"},
++	{FTDI_VID, AMONTEC_JTAGKEY_PID, OK, "Amontec", "JTAGkey"},
++	{},
++};
++
+ /*
+  * The 'H' chips can run internally at either 12MHz or 60MHz.
+  * The non-H chips can only run at 12MHz.
+@@ -45,9 +58,42 @@
+ #define BITMODE_BITBANG_NORMAL	1
+ #define BITMODE_BITBANG_SPI	2
+ 
++/* 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
++ *
++ * JTAGkey(2) needs to enable its output via Bit4 / GPIOL0
++ *  value: 0x18  OE=high, CS=high, DI=low, DO=low, SK=low
++ *    dir: 0x1b  OE=output, CS=output, DI=input, DO=output, SK=output
++ */
++static uint8_t cs_bits = 0x08;
++static uint8_t pindir = 0x0b;
+ static struct ftdi_context ftdic_context;
+ 
+-int send_buf(struct ftdi_context *ftdic, const unsigned char *buf, int size)
++static const char *get_ft2232_devicename(int ft2232_vid, int ft2232_type)
++{
++	int i;
++	for (i = 0; devs_ft2232spi[i].vendor_name != NULL; i++) {
++		if ((devs_ft2232spi[i].device_id == ft2232_type)
++			&& (devs_ft2232spi[i].vendor_id == ft2232_vid))
++				return devs_ft2232spi[i].device_name;
++	}
++	return "unknown device";
++}
++
++static const char *get_ft2232_vendorname(int ft2232_vid, int ft2232_type)
++{
++	int i;
++	for (i = 0; devs_ft2232spi[i].vendor_name != NULL; i++) {
++		if ((devs_ft2232spi[i].device_id == ft2232_type)
++			&& (devs_ft2232spi[i].vendor_id == ft2232_vid))
++				return devs_ft2232spi[i].vendor_name;
++	}
++	return "unknown vendor";
++}
++
++static int send_buf(struct ftdi_context *ftdic, const unsigned char *buf,
++		    int size)
+ {
+ 	int r;
+ 	r = ftdi_write_data(ftdic, (unsigned char *) buf, size);
+@@ -59,7 +105,8 @@ int send_buf(struct ftdi_context *ftdic, const unsigned char *buf, int size)
+ 	return 0;
+ }
+ 
+-int get_buf(struct ftdi_context *ftdic, const unsigned char *buf, int size)
++static int get_buf(struct ftdi_context *ftdic, const unsigned char *buf,
++		   int size)
+ {
+ 	int r;
+ 	r = ftdi_read_data(ftdic, (unsigned char *) buf, size);
+@@ -76,47 +123,58 @@ int ft2232_spi_init(void)
+ 	int f;
+ 	struct ftdi_context *ftdic = &ftdic_context;
+ 	unsigned char buf[512];
+-	char *portpos = NULL;
+-	int ft2232_type = FTDI_FT4232H;
++	int ft2232_vid = FTDI_VID;
++	int ft2232_type = FTDI_FT4232H_PID;
+ 	enum ftdi_interface ft2232_interface = INTERFACE_B;
++	char *arg;
++
++	arg = extract_programmer_param("type");
++	if (arg) {
++		if (!strcasecmp(arg, "2232H"))
++			ft2232_type = FTDI_FT2232H_PID;
++		else if (!strcasecmp(arg, "4232H"))
++			ft2232_type = FTDI_FT4232H_PID;
++		else if (!strcasecmp(arg, "jtagkey")) {
++			ft2232_type = AMONTEC_JTAGKEY_PID;
++			ft2232_interface = INTERFACE_A;
++			cs_bits = 0x18;
++			pindir = 0x1b;
++		}
++		else {
++			msg_perr("Error: Invalid device type specified.\n");
++			free(arg);
++			return 1;
++		}
++	}
++	free(arg);
++	arg = extract_programmer_param("port");
++	if (arg) {
++		switch (toupper(*arg)) {
++		case 'A':
++			ft2232_interface = INTERFACE_A;
++			break;
++		case 'B':
++			ft2232_interface = INTERFACE_B;
++			break;
++		default:
++			msg_perr("Error: Invalid port/interface specified.\n");
++			free(arg);
++			return 1;
++		}
++	}
++	free(arg);
++	msg_pdbg("Using device type %s %s ",
++		 get_ft2232_vendorname(ft2232_vid, ft2232_type),
++		 get_ft2232_devicename(ft2232_vid, ft2232_type));
++	msg_pdbg("interface %s\n",
++		 (ft2232_interface == INTERFACE_A) ? "A" : "B");
+ 
+ 	if (ftdi_init(ftdic) < 0) {
+ 		msg_perr("ftdi_init failed\n");
+ 		return EXIT_FAILURE; // TODO
+ 	}
+ 
+-	if (programmer_param && !strlen(programmer_param)) {
+-		free(programmer_param);
+-		programmer_param = NULL;
+-	}
+-	if (programmer_param) {
+-		if (strstr(programmer_param, "2232"))
+-			ft2232_type = FTDI_FT2232H;
+-		if (strstr(programmer_param, "4232"))
+-			ft2232_type = FTDI_FT4232H;
+-		portpos = strstr(programmer_param, "port=");
+-		if (portpos) {
+-			portpos += 5;
+-			switch (toupper(*portpos)) {
+-			case 'A':
+-				ft2232_interface = INTERFACE_A;
+-				break;
+-			case 'B':
+-				ft2232_interface = INTERFACE_B;
+-				break;
+-			default:
+-				msg_perr("Invalid interface specified, "
+-					"using default.\n");
+-			}
+-		}
+-		free(programmer_param);
+-	}
+-	msg_pdbg("Using device type %s ",
+-		     (ft2232_type == FTDI_FT2232H) ? "2232H" : "4232H");
+-	msg_pdbg("interface %s\n",
+-		     (ft2232_interface == INTERFACE_A) ? "A" : "B");
+-
+-	f = ftdi_usb_open(ftdic, 0x0403, ft2232_type);
++	f = ftdi_usb_open(ftdic, FTDI_VID, ft2232_type);
+ 
+ 	if (f < 0 && f != -5) {
+ 		msg_perr("Unable to open FTDI device: %d (%s)\n", f,
+@@ -166,7 +224,7 @@ int ft2232_spi_init(void)
+ 		return -1;
+ 
+ 	msg_pdbg("SPI clock is %fMHz\n",
+-	       (double)(MPSSE_CLK / (((DIVIDE_BY - 1) + 1) * 2)));
++		 (double)(MPSSE_CLK / (((DIVIDE_BY - 1) + 1) * 2)));
+ 
+ 	/* Disconnect TDI/DO to TDO/DI for loopback. */
+ 	msg_pdbg("No loopback of TDI/DO TDO/DI\n");
+@@ -175,14 +233,9 @@ int ft2232_spi_init(void)
+ 		return -1;
+ 
+ 	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] = CS_BIT;
+-	buf[2] = 0x0b;
++	buf[1] = cs_bits;
++	buf[2] = pindir;
+ 	if (send_buf(ftdic, buf, 3))
+ 		return -1;
+ 
+@@ -227,8 +280,8 @@ int ft2232_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ 	 */
+ 	msg_pspew("Assert CS#\n");
+ 	buf[i++] = SET_BITS_LOW;
+-	buf[i++] = 0 & ~CS_BIT; /* assertive */
+-	buf[i++] = 0x0b;
++	buf[i++] = 0 & ~cs_bits; /* assertive */
++	buf[i++] = pindir;
+ 
+ 	if (writecnt) {
+ 		buf[i++] = 0x11;
+@@ -269,8 +322,8 @@ int ft2232_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ 
+ 	msg_pspew("De-assert CS#\n");
+ 	buf[i++] = SET_BITS_LOW;
+-	buf[i++] = CS_BIT;
+-	buf[i++] = 0x0b;
++	buf[i++] = cs_bits;
++	buf[i++] = pindir;
+ 	ret = send_buf(ftdic, buf, i);
+ 	failed |= ret;
+ 	if (ret)
+@@ -285,37 +338,21 @@ int ft2232_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+ 	return spi_read_chunked(flash, buf, start, len, 64 * 1024);
+ }
+ 
+-int ft2232_spi_write_256(struct flashchip *flash, uint8_t *buf)
++int ft2232_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
+ {
+-	int total_size = 1024 * flash->total_size;
+-	int i;
++	return spi_write_chunked(flash, buf, start, len, 256);
++}
+ 
+-	spi_disable_blockprotect();
+-	/* Erase first. */
+-	msg_pinfo("Erasing flash before programming... ");
+-	if (erase_flash(flash)) {
+-		msg_perr("ERASE FAILED!\n");
+-		return -1;
+-	}
+-	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)
+-			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;
+-		}
++void print_supported_usbdevs(const struct usbdev_status *devs)
++{
++	int i;
+ 
+-		while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP)
+-			/* loop */;
++	for (i = 0; devs[i].vendor_name != NULL; i++) {
++		msg_pinfo("%s %s [%04x:%04x]%s\n", devs[i].vendor_name,
++			  devs[i].device_name, devs[i].vendor_id,
++			  devs[i].device_id,
++			  (devs[i].status == NT) ? " (untested)" : "");
+ 	}
+-
+-	return 0;
+ }
+ 
+ #endif
+diff --git a/gfxnvidia.c b/gfxnvidia.c
+index ec41279..b0bf79b 100644
+--- a/gfxnvidia.c
++++ b/gfxnvidia.c
+@@ -22,12 +22,18 @@
+ #include <string.h>
+ #include <sys/types.h>
+ #include "flash.h"
++#include "programmer.h"
+ 
+ #define PCI_VENDOR_ID_NVIDIA	0x10de
+ 
++/* Mask to restrict flash accesses to a 128kB memory window.
++ * FIXME: Is this size a one-fits-all or card dependent?
++ */
++#define GFXNVIDIA_MEMMAP_MASK		((1 << 17) - 1)
++
+ uint8_t *nvidia_bar;
+ 
+-struct pcidev_status gfx_nvidia[] = {
++const struct pcidev_status gfx_nvidia[] = {
+ 	{0x10de, 0x0010, NT, "NVIDIA", "Mutara V08 [NV2]" },
+ 	{0x10de, 0x0018, NT, "NVIDIA", "RIVA 128" },
+ 	{0x10de, 0x0020, NT, "NVIDIA", "RIVA TNT" },
+@@ -62,7 +68,8 @@ int gfxnvidia_init(void)
+ 	get_io_perms();
+ 
+ 	io_base_addr = pcidev_init(PCI_VENDOR_ID_NVIDIA, PCI_BASE_ADDRESS_0,
+-				   gfx_nvidia, programmer_param);
++				   gfx_nvidia);
++
+ 	io_base_addr += 0x300000;
+ 	msg_pinfo("Detected NVIDIA I/O base address: 0x%x.\n", io_base_addr);
+ 
+@@ -75,6 +82,9 @@ int gfxnvidia_init(void)
+ 
+ 	buses_supported = CHIP_BUSTYPE_PARALLEL;
+ 
++	/* Write/erase doesn't work. */
++	programmer_may_write = 0;
++
+ 	return 0;
+ }
+ 
+@@ -87,7 +97,6 @@ int gfxnvidia_shutdown(void)
+ 	reg32 |= (1 << 0);
+ 	pci_write_long(pcidev_dev, 0x50, reg32);
+ 
+-	free(programmer_param);
+ 	pci_cleanup(pacc);
+ 	release_io_perms();
+ 	return 0;
+@@ -95,10 +104,10 @@ int gfxnvidia_shutdown(void)
+ 
+ void gfxnvidia_chip_writeb(uint8_t val, chipaddr addr)
+ {
+-	mmio_writeb(val, nvidia_bar + addr);
++	pci_mmio_writeb(val, nvidia_bar + (addr & GFXNVIDIA_MEMMAP_MASK));
+ }
+ 
+ uint8_t gfxnvidia_chip_readb(const chipaddr addr)
+ {
+-	return mmio_readb(nvidia_bar + addr);
++	return pci_mmio_readb(nvidia_bar + (addr & GFXNVIDIA_MEMMAP_MASK));
+ }
+diff --git a/hwaccess.c b/hwaccess.c
+index 830013e..3a61e60 100644
+--- a/hwaccess.c
++++ b/hwaccess.c
+@@ -21,30 +21,50 @@
+ #include <stdint.h>
+ #include <string.h>
+ #include <stdlib.h>
+-#include <fcntl.h>
+ #include <sys/types.h>
++#if !defined (__DJGPP__)
++#include <unistd.h>
++#include <fcntl.h>
+ #include <errno.h>
++#endif
+ #include "flash.h"
+ 
++#if defined(__i386__) || defined(__x86_64__)
++
++/* sync primitive is not needed because x86 uses uncached accesses
++ * which have a strongly ordered memory model.
++ */
++static inline void sync_primitive(void)
++{
++}
++
+ #if defined(__FreeBSD__) || defined(__DragonFly__)
+ int io_fd;
+ #endif
+ 
+ void get_io_perms(void)
+ {
++#if defined(__DJGPP__)
++	/* We have full permissions by default. */
++	return;
++#else
+ #if defined (__sun) && (defined(__i386) || defined(__amd64))
+ 	if (sysi86(SI86V86, V86SC_IOPL, PS_IOPL) != 0) {
+ #elif defined(__FreeBSD__) || defined (__DragonFly__)
+ 	if ((io_fd = open("/dev/io", O_RDWR)) < 0) {
+-#elif __DJGPP__
+-	if (0) {
+ #else 
+ 	if (iopl(3) != 0) {
+ #endif
+ 		msg_perr("ERROR: Could not get I/O privileges (%s).\n"
+ 			"You need to be root.\n", strerror(errno));
++#if defined (__OpenBSD__)
++		msg_perr("Please set securelevel=-1 in /etc/rc.securelevel "
++			   "and reboot, or reboot into \n");
++		msg_perr("single user mode.\n");
++#endif
+ 		exit(1);
+ 	}
++#endif
+ }
+ 
+ void release_io_perms(void)
+@@ -54,19 +74,68 @@ void release_io_perms(void)
+ #endif
+ }
+ 
++#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || defined(__ppc64__)
++
++static inline void sync_primitive(void)
++{
++	/* Prevent reordering and/or merging of reads/writes to hardware.
++	 * Such reordering and/or merging would break device accesses which
++	 * depend on the exact access order.
++	 */
++	asm("eieio" : : : "memory");
++}
++
++/* PCI port I/O is not yet implemented on PowerPC. */
++void get_io_perms(void)
++{
++}
++
++/* PCI port I/O is not yet implemented on PowerPC. */
++void release_io_perms(void)
++{
++}
++
++#elif defined (__mips) || defined (__mips__) || defined (_mips) || defined (mips)
++
++/* sync primitive is not needed because /dev/mem on MIPS uses uncached accesses
++ * in mode 2 which has a strongly ordered memory model.
++ */
++static inline void sync_primitive(void)
++{
++}
++
++/* PCI port I/O is not yet implemented on MIPS. */
++void get_io_perms(void)
++{
++}
++
++/* PCI port I/O is not yet implemented on MIPS. */
++void release_io_perms(void)
++{
++}
++
++#else
++
++#error Unknown architecture
++
++#endif
++
+ void mmio_writeb(uint8_t val, void *addr)
+ {
+ 	*(volatile uint8_t *) addr = val;
++	sync_primitive();
+ }
+ 
+ void mmio_writew(uint16_t val, void *addr)
+ {
+ 	*(volatile uint16_t *) addr = val;
++	sync_primitive();
+ }
+ 
+ void mmio_writel(uint32_t val, void *addr)
+ {
+ 	*(volatile uint32_t *) addr = val;
++	sync_primitive();
+ }
+ 
+ uint8_t mmio_readb(void *addr)
+@@ -83,3 +152,33 @@ uint32_t mmio_readl(void *addr)
+ {
+ 	return *(volatile uint32_t *) addr;
+ }
++
++void mmio_le_writeb(uint8_t val, void *addr)
++{
++	mmio_writeb(cpu_to_le8(val), addr);
++}
++
++void mmio_le_writew(uint16_t val, void *addr)
++{
++	mmio_writew(cpu_to_le16(val), addr);
++}
++
++void mmio_le_writel(uint32_t val, void *addr)
++{
++	mmio_writel(cpu_to_le32(val), addr);
++}
++
++uint8_t mmio_le_readb(void *addr)
++{
++	return le_to_cpu8(mmio_readb(addr));
++}
++
++uint16_t mmio_le_readw(void *addr)
++{
++	return le_to_cpu16(mmio_readw(addr));
++}
++
++uint32_t mmio_le_readl(void *addr)
++{
++	return le_to_cpu32(mmio_readl(addr));
++}
+diff --git a/hwaccess.h b/hwaccess.h
+index 7a76925..2d17326 100644
+--- a/hwaccess.h
++++ b/hwaccess.h
+@@ -24,13 +24,147 @@
+ #ifndef __HWACCESS_H__
+ #define __HWACCESS_H__ 1
+ 
++#if defined (__i386__) || defined (__x86_64__)
+ #if defined(__GLIBC__)
+ #include <sys/io.h>
+ #endif
++#endif
++
+ #if NEED_PCI == 1
++/*
++ * libpci headers use the variable name "index" which triggers shadowing
++ * warnings on systems which have the index() function in a default #include
++ * or as builtin.
++ */
++#define index shadow_workaround_index
+ #include <pci/pci.h>
++#undef index
++#endif
++
++#if defined (__i386__) || defined (__x86_64__)
++
++/* All x86 is little-endian. */
++#define __FLASHROM_LITTLE_ENDIAN__ 1
++
++#elif defined (__mips) || defined (__mips__) || defined (_mips) || defined (mips)
++
++/* MIPS can be either endian. */
++#if defined (__MIPSEL) || defined (__MIPSEL__) || defined (_MIPSEL) || defined (MIPSEL)
++#define __FLASHROM_LITTLE_ENDIAN__ 1
++#elif defined (__MIPSEB) || defined (__MIPSEB__) || defined (_MIPSEB) || defined (MIPSEB)
++#define __FLASHROM_BIG_ENDIAN__ 1
++#endif
++
++#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || defined(__ppc64__)
++
++/* PowerPC can be either endian. */
++#if defined (_BIG_ENDIAN) || defined (__BIG_ENDIAN__)
++#define __FLASHROM_BIG_ENDIAN__ 1
++/* Error checking in case some weird header has #defines for LE as well. */
++#if defined (_LITTLE_ENDIAN) || defined (__LITTLE_ENDIAN__)
++#error Conflicting endianness #define
++#endif
++#else
++#error Little-endian PowerPC #defines are unknown
++#endif
++
++#endif
++
++#if !defined (__FLASHROM_BIG_ENDIAN__) && !defined (__FLASHROM_LITTLE_ENDIAN__)
++/* Nonstandard libc-specific macros for determining endianness. */
++#if defined(__GLIBC__)
++#include <endian.h>
++#if BYTE_ORDER == LITTLE_ENDIAN
++#define __FLASHROM_LITTLE_ENDIAN__ 1
++#elif BYTE_ORDER == BIG_ENDIAN
++#define __FLASHROM_BIG_ENDIAN__ 1
++#endif
++#endif
++#endif
++
++#if !defined (__FLASHROM_BIG_ENDIAN__) && !defined (__FLASHROM_LITTLE_ENDIAN__)
++#error Unable to determine endianness. Please add support for your arch or libc.
++#endif
++
++#define ___constant_swab8(x) ((uint8_t) (				\
++	(((uint8_t)(x) & (uint8_t)0xffU))))
++
++#define ___constant_swab16(x) ((uint16_t) (				\
++	(((uint16_t)(x) & (uint16_t)0x00ffU) << 8) |			\
++	(((uint16_t)(x) & (uint16_t)0xff00U) >> 8)))
++
++#define ___constant_swab32(x) ((uint32_t) (				\
++	(((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) |		\
++	(((uint32_t)(x) & (uint32_t)0x0000ff00UL) <<  8) |		\
++	(((uint32_t)(x) & (uint32_t)0x00ff0000UL) >>  8) |		\
++	(((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24)))
++
++#define ___constant_swab64(x) ((uint64_t) (				\
++	(((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) |	\
++	(((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) |	\
++	(((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) |	\
++	(((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) <<  8) |	\
++	(((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >>  8) |	\
++	(((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) |	\
++	(((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) |	\
++	(((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56)))
++
++#if defined (__FLASHROM_BIG_ENDIAN__)
++
++#define cpu_to_le(bits)							\
++static inline uint##bits##_t cpu_to_le##bits(uint##bits##_t val)	\
++{									\
++	return ___constant_swab##bits(val);				\
++}
++
++cpu_to_le(8)
++cpu_to_le(16)
++cpu_to_le(32)
++cpu_to_le(64)
++
++#define cpu_to_be8
++#define cpu_to_be16
++#define cpu_to_be32
++#define cpu_to_be64
++
++#elif defined (__FLASHROM_LITTLE_ENDIAN__)
++
++#define cpu_to_be(bits)							\
++static inline uint##bits##_t cpu_to_be##bits(uint##bits##_t val)	\
++{									\
++	return ___constant_swab##bits(val);				\
++}
++
++cpu_to_be(8)
++cpu_to_be(16)
++cpu_to_be(32)
++cpu_to_be(64)
++
++#define cpu_to_le8
++#define cpu_to_le16
++#define cpu_to_le32
++#define cpu_to_le64
++
++#else
++
++#error Could not determine endianness.
++
+ #endif
+ 
++#define be_to_cpu8 cpu_to_be8
++#define be_to_cpu16 cpu_to_be16
++#define be_to_cpu32 cpu_to_be32
++#define be_to_cpu64 cpu_to_be64
++#define le_to_cpu8 cpu_to_le8
++#define le_to_cpu16 cpu_to_le16
++#define le_to_cpu32 cpu_to_le32
++#define le_to_cpu64 cpu_to_le64
++
++#if NEED_PCI == 1
++#if defined (__i386__) || defined (__x86_64__)
++
++#define __FLASHROM_HAVE_OUTB__ 1
++
+ /* for iopl and outb under Solaris */
+ #if defined (__sun) && (defined(__i386) || defined(__amd64))
+ #include <strings.h>
+@@ -43,6 +177,10 @@
+ #define __DARWIN__
+ #endif
+ 
++/* Clarification about OUTB/OUTW/OUTL argument order:
++ * OUT[BWL](val, port)
++ */
++
+ #if defined(__FreeBSD__) || defined(__DragonFly__)
+   #include <machine/cpufunc.h>
+   #define off64_t off_t
+@@ -95,17 +233,25 @@
+ #endif
+ #endif
+ 
+-#if defined(__NetBSD__)
++#if defined(__NetBSD__) || defined (__OpenBSD__)
+   #define off64_t off_t
+   #define lseek64 lseek
+   #if defined(__i386__) || defined(__x86_64__)
+     #include <sys/types.h>
+     #include <machine/sysarch.h>
++#if defined(__NetBSD__)
+     #if defined(__i386__)
+       #define iopl i386_iopl
+     #elif defined(__x86_64__)
+       #define iopl x86_64_iopl
+     #endif
++#elif defined (__OpenBSD__)
++    #if defined(__i386__)
++      #define iopl i386_iopl
++    #elif defined(__amd64__)
++      #define iopl amd64_iopl
++    #endif
++#endif
+   #include <stdint.h>
+ 
+ static inline void outb(uint8_t value, uint16_t port)
+@@ -162,4 +308,19 @@ msr_t freebsd_rdmsr(int addr);
+ int freebsd_wrmsr(int addr, msr_t msr);
+ #endif
+ 
++#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || defined(__ppc64__)
++
++/* PCI port I/O is not yet implemented on PowerPC. */
++
++#elif defined (__mips) || defined (__mips__) || defined (_mips) || defined (mips)
++
++/* PCI port I/O is not yet implemented on MIPS. */
++
++#else
++
++#error Unknown architecture, please check if it supports PCI port IO.
++
++#endif
++#endif
++
+ #endif /* !__HWACCESS_H__ */
+diff --git a/ichspi.c b/ichspi.c
+index fbe9092..bb6007e 100644
+--- a/ichspi.c
++++ b/ichspi.c
+@@ -5,7 +5,7 @@
+  * Copyright (C) 2008 Claus Gindhart <claus.gindhart at kontron.com>
+  * Copyright (C) 2008 Dominik Geyer <dominik.geyer at kontron.com>
+  * Copyright (C) 2008 coresystems GmbH <info at coresystems.de>
+- * Copyright (C) 2009 Carl-Daniel Hailfinger
++ * 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
+@@ -33,9 +33,12 @@
+  *
+  */
+ 
++#if defined(__i386__) || defined(__x86_64__)
++
+ #include <string.h>
+ #include "flash.h"
+ #include "chipdrivers.h"
++#include "programmer.h"
+ #include "spi.h"
+ 
+ /* ICH9 controller register definition */
+@@ -99,7 +102,11 @@
+ #define ICH7_REG_OPMENU                0x58	/* 64 Bits */
+ 
+ /* ICH SPI configuration lock-down. May be set during chipset enabling. */
+-int ichspi_lock = 0;
++static int ichspi_lock = 0;
++
++uint32_t ichspi_bbar = 0;
++
++static void *ich_spibar = NULL;
+ 
+ typedef struct _OPCODE {
+ 	uint8_t opcode;		//This commands spi opcode
+@@ -130,17 +137,17 @@ static OPCODES *curopcodes = NULL;
+ /* HW access functions */
+ static uint32_t REGREAD32(int X)
+ {
+-	return mmio_readl(spibar + X);
++	return mmio_readl(ich_spibar + X);
+ }
+ 
+ static uint16_t REGREAD16(int X)
+ {
+-	return mmio_readw(spibar + X);
++	return mmio_readw(ich_spibar + X);
+ }
+ 
+-#define REGWRITE32(X,Y) mmio_writel(Y, spibar+X)
+-#define REGWRITE16(X,Y) mmio_writew(Y, spibar+X)
+-#define REGWRITE8(X,Y)  mmio_writeb(Y, spibar+X)
++#define REGWRITE32(X,Y) mmio_writel(Y, ich_spibar+X)
++#define REGWRITE16(X,Y) mmio_writew(Y, ich_spibar+X)
++#define REGWRITE8(X,Y)  mmio_writeb(Y, ich_spibar+X)
+ 
+ /* Common SPI functions */
+ static int find_opcode(OPCODES *op, uint8_t opcode);
+@@ -149,8 +156,6 @@ static int generate_opcodes(OPCODES * op);
+ static int program_opcodes(OPCODES * op);
+ static int run_opcode(OPCODE op, uint32_t offset,
+ 		      uint8_t datalength, uint8_t * data);
+-static int ich_spi_write_page(struct flashchip *flash, uint8_t * bytes,
+-			      int offset, int maxdata);
+ 
+ /* for pairing opcodes with their required preop */
+ struct preop_opcode_pair {
+@@ -159,7 +164,7 @@ struct preop_opcode_pair {
+ };
+ 
+ /* List of opcodes which need preopcodes and matching preopcodes. Unused. */
+-struct preop_opcode_pair pops[] = {
++const struct preop_opcode_pair pops[] = {
+ 	{JEDEC_WREN, JEDEC_BYTE_PROGRAM},
+ 	{JEDEC_WREN, JEDEC_SE}, /* sector erase */
+ 	{JEDEC_WREN, JEDEC_BE_52}, /* block erase */
+@@ -175,7 +180,7 @@ struct preop_opcode_pair pops[] = {
+ /* Reasonable default configuration. Needs ad-hoc modifications if we
+  * encounter unlisted opcodes. Fun.
+  */
+-OPCODES O_ST_M25P = {
++static OPCODES O_ST_M25P = {
+ 	{
+ 	 JEDEC_WREN,
+ 	 JEDEC_EWSR,
+@@ -192,7 +197,7 @@ OPCODES O_ST_M25P = {
+ 	}
+ };
+ 
+-OPCODES O_EXISTING = {};
++static OPCODES O_EXISTING = {};
+ 
+ static int find_opcode(OPCODES *op, uint8_t opcode)
+ {
+@@ -327,12 +332,40 @@ int program_opcodes(OPCODES * op)
+ 	return 0;
+ }
+ 
++/*
++ * Try to set BBAR (BIOS Base Address Register), but read back the value in case
++ * it didn't stick.
++ */
++void ich_set_bbar(uint32_t minaddr)
++{
++	switch (spi_controller) {
++	case SPI_CONTROLLER_ICH7:
++		mmio_writel(minaddr, ich_spibar + 0x50);
++		ichspi_bbar = mmio_readl(ich_spibar + 0x50);
++		/* We don't have any option except complaining. */
++		if (ichspi_bbar != minaddr)
++			msg_perr("Setting BBAR failed!\n");
++		break;
++	case SPI_CONTROLLER_ICH9:
++		mmio_writel(minaddr, ich_spibar + 0xA0);
++		ichspi_bbar = mmio_readl(ich_spibar + 0xA0);
++		/* We don't have any option except complaining. */
++		if (ichspi_bbar != minaddr)
++			msg_perr("Setting BBAR failed!\n");
++		break;
++	default:
++		/* Not sure if BBAR actually exists on VIA. */
++		msg_pdbg("Setting BBAR is not implemented for VIA yet.\n");
++		break;
++	}
++}
++
+ /* This function generates OPCODES from or programs OPCODES to ICH according to
+  * the chipset's SPI configuration lock.
+  *
+  * It should be called before ICH sends any spi command.
+  */
+-int ich_init_opcodes(void)
++static int ich_init_opcodes(void)
+ {
+ 	int rc = 0;
+ 	OPCODES *curopcodes_done;
+@@ -341,13 +374,18 @@ int ich_init_opcodes(void)
+ 		return 0;
+ 
+ 	if (ichspi_lock) {
+-		msg_pdbg("Generating OPCODES... ");
++		msg_pdbg("Reading OPCODES... ");
+ 		curopcodes_done = &O_EXISTING;
+ 		rc = generate_opcodes(curopcodes_done);
+ 	} else {
+ 		msg_pdbg("Programming OPCODES... ");
+ 		curopcodes_done = &O_ST_M25P;
+ 		rc = program_opcodes(curopcodes_done);
++		/* Technically not part of opcode init, but it allows opcodes
++		 * to run without transaction errors by setting the lowest
++		 * allowed address to zero.
++		 */
++		ich_set_bbar(0);
+ 	}
+ 
+ 	if (rc) {
+@@ -522,7 +560,9 @@ static int ich9_run_opcode(OPCODE op, uint32_t offset,
+ 	}
+ 
+ 	/* Assemble SSFS + SSFC */
+-	temp32 = 0;
++	/* keep reserved bits (23-19,7,0) */
++	temp32 = REGREAD32(ICH9_REG_SSFS);
++	temp32 &= 0xF8008100;
+ 
+ 	/* clear error status registers */
+ 	temp32 |= (SSFS_CDS + SSFS_FCERR);
+@@ -636,28 +676,6 @@ static int run_opcode(OPCODE op, uint32_t offset,
+ 	return -1;
+ }
+ 
+-static int ich_spi_write_page(struct flashchip *flash, uint8_t * bytes,
+-			      int offset, int maxdata)
+-{
+-	int page_size = flash->page_size;
+-	uint32_t remaining = page_size;
+-	int towrite;
+-
+-	msg_pspew("ich_spi_write_page: offset=%d, number=%d, buf=%p\n",
+-		     offset, page_size, bytes);
+-
+-	for (; remaining > 0; remaining -= towrite) {
+-		towrite = min(remaining, maxdata);
+-		if (spi_nbyte_program(offset + (page_size - remaining),
+-				      &bytes[page_size - remaining], towrite)) {
+-			msg_perr("Error writing");
+-			return 1;
+-		}
+-	}
+-
+-	return 0;
+-}
+-
+ int ich_spi_read(struct flashchip *flash, uint8_t * buf, int start, int len)
+ {
+ 	int maxdata = 64;
+@@ -668,38 +686,14 @@ int ich_spi_read(struct flashchip *flash, uint8_t * buf, int start, int len)
+ 	return spi_read_chunked(flash, buf, start, len, maxdata);
+ }
+ 
+-int ich_spi_write_256(struct flashchip *flash, uint8_t * buf)
++int ich_spi_write_256(struct flashchip *flash, uint8_t * buf, int start, int len)
+ {
+-	int i, j, rc = 0;
+-	int total_size = flash->total_size * 1024;
+-	int page_size = flash->page_size;
+-	int erase_size = 64 * 1024;
+ 	int maxdata = 64;
+ 
+-	spi_disable_blockprotect();
+-	/* Erase first */
+-	msg_pinfo("Erasing flash before programming... ");
+-	if (erase_flash(flash)) {
+-		msg_perr("ERASE FAILED!\n");
+-		return -1;
+-	}
+-	msg_pinfo("done.\n");
+-
+-	msg_pinfo("Programming page: \n");
+-	for (i = 0; i < total_size / erase_size; i++) {
+-		if (spi_controller == SPI_CONTROLLER_VIA)
+-			maxdata = 16;
+-
+-		for (j = 0; j < erase_size / page_size; j++) {
+-			ich_spi_write_page(flash,
+-			   (void *)(buf + (i * erase_size) + (j * page_size)),
+-			   (i * erase_size) + (j * page_size), maxdata);
+-		}
+-	}
+-
+-	msg_pinfo("\n");
++	if (spi_controller == SPI_CONTROLLER_VIA)
++		maxdata = 16;
+ 
+-	return rc;
++	return spi_write_chunked(flash, buf, start, len, maxdata);
+ }
+ 
+ int ich_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+@@ -767,6 +761,19 @@ int ich_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ 	    opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) {
+ 		addr = (writearr[1] << 16) |
+ 		    (writearr[2] << 8) | (writearr[3] << 0);
++		switch (spi_controller) {
++		case SPI_CONTROLLER_ICH7:
++		case SPI_CONTROLLER_ICH9:
++			if (addr < ichspi_bbar) {
++				msg_perr("%s: Address 0x%06x below allowed "
++					 "range 0x%06x-0xffffff\n", __func__,
++					 addr, ichspi_bbar);
++				return SPI_INVALID_ADDRESS;
++			}
++			break;
++		default:
++			break;
++		}
+ 	}
+ 
+ 	/* translate read/write array/count */
+@@ -858,3 +865,212 @@ int ich_spi_send_multicommand(struct spi_command *cmds)
+ 	}
+ 	return ret;
+ }
++
++#define ICH_BMWAG(x) ((x >> 24) & 0xff)
++#define ICH_BMRAG(x) ((x >> 16) & 0xff)
++#define ICH_BRWA(x)  ((x >>  8) & 0xff)
++#define ICH_BRRA(x)  ((x >>  0) & 0xff)
++
++#define ICH_FREG_BASE(x)  ((x >>  0) & 0x1fff)
++#define ICH_FREG_LIMIT(x) ((x >> 16) & 0x1fff)
++
++static void do_ich9_spi_frap(uint32_t frap, int i)
++{
++	const char *access_names[4] = {
++		"locked", "read-only", "write-only", "read-write"
++	};
++	const char *region_names[5] = {
++		"Flash Descriptor", "BIOS", "Management Engine",
++		"Gigabit Ethernet", "Platform Data"
++	};
++	uint32_t base, limit;
++	int rwperms = (((ICH_BRWA(frap) >> i) & 1) << 1) |
++		      (((ICH_BRRA(frap) >> i) & 1) << 0);
++	int offset = 0x54 + i * 4;
++	uint32_t freg = mmio_readl(ich_spibar + offset);
++
++	msg_pdbg("0x%02X: 0x%08x (FREG%i: %s)\n",
++		     offset, freg, i, region_names[i]);
++
++	base  = ICH_FREG_BASE(freg);
++	limit = ICH_FREG_LIMIT(freg);
++	if (base == 0x1fff && limit == 0) {
++		/* this FREG is disabled */
++		msg_pdbg("%s region is unused.\n", region_names[i]);
++		return;
++	}
++
++	msg_pdbg("0x%08x-0x%08x is %s\n",
++		    (base << 12), (limit << 12) | 0x0fff,
++		    access_names[rwperms]);
++}
++
++int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
++			int ich_generation)
++{
++	int i;
++	uint8_t old, new;
++	uint16_t spibar_offset, tmp2;
++	uint32_t tmp;
++
++	buses_supported |= CHIP_BUSTYPE_SPI;
++	switch (ich_generation) {
++	case 7:
++		spi_controller = SPI_CONTROLLER_ICH7;
++		spibar_offset = 0x3020;
++		break;
++	case 8:
++		spi_controller = SPI_CONTROLLER_ICH9;
++		spibar_offset = 0x3020;
++		break;
++	case 9:
++	case 10:
++	default:		/* Future version might behave the same */
++		spi_controller = SPI_CONTROLLER_ICH9;
++		spibar_offset = 0x3800;
++		break;
++	}
++
++	/* SPIBAR is at RCRB+0x3020 for ICH[78] and RCRB+0x3800 for ICH9. */
++	msg_pdbg("SPIBAR = 0x%x + 0x%04x\n", base, spibar_offset);
++
++	/* Assign Virtual Address */
++	ich_spibar = rcrb + spibar_offset;
++
++	switch (spi_controller) {
++	case SPI_CONTROLLER_ICH7:
++		msg_pdbg("0x00: 0x%04x     (SPIS)\n",
++			     mmio_readw(ich_spibar + 0));
++		msg_pdbg("0x02: 0x%04x     (SPIC)\n",
++			     mmio_readw(ich_spibar + 2));
++		msg_pdbg("0x04: 0x%08x (SPIA)\n",
++			     mmio_readl(ich_spibar + 4));
++		for (i = 0; i < 8; i++) {
++			int offs;
++			offs = 8 + (i * 8);
++			msg_pdbg("0x%02x: 0x%08x (SPID%d)\n", offs,
++				     mmio_readl(ich_spibar + offs), i);
++			msg_pdbg("0x%02x: 0x%08x (SPID%d+4)\n", offs + 4,
++				     mmio_readl(ich_spibar + offs + 4), i);
++		}
++		ichspi_bbar = mmio_readl(ich_spibar + 0x50);
++		msg_pdbg("0x50: 0x%08x (BBAR)\n",
++			     ichspi_bbar);
++		msg_pdbg("0x54: 0x%04x     (PREOP)\n",
++			     mmio_readw(ich_spibar + 0x54));
++		msg_pdbg("0x56: 0x%04x     (OPTYPE)\n",
++			     mmio_readw(ich_spibar + 0x56));
++		msg_pdbg("0x58: 0x%08x (OPMENU)\n",
++			     mmio_readl(ich_spibar + 0x58));
++		msg_pdbg("0x5c: 0x%08x (OPMENU+4)\n",
++			     mmio_readl(ich_spibar + 0x5c));
++		for (i = 0; i < 4; i++) {
++			int offs;
++			offs = 0x60 + (i * 4);
++			msg_pdbg("0x%02x: 0x%08x (PBR%d)\n", offs,
++				     mmio_readl(ich_spibar + offs), i);
++		}
++		msg_pdbg("\n");
++		if (mmio_readw(ich_spibar) & (1 << 15)) {
++			msg_pinfo("WARNING: SPI Configuration Lockdown activated.\n");
++			ichspi_lock = 1;
++		}
++		ich_init_opcodes();
++		break;
++	case SPI_CONTROLLER_ICH9:
++		tmp2 = mmio_readw(ich_spibar + 4);
++		msg_pdbg("0x04: 0x%04x (HSFS)\n", tmp2);
++		msg_pdbg("FLOCKDN %i, ", (tmp2 >> 15 & 1));
++		msg_pdbg("FDV %i, ", (tmp2 >> 14) & 1);
++		msg_pdbg("FDOPSS %i, ", (tmp2 >> 13) & 1);
++		msg_pdbg("SCIP %i, ", (tmp2 >> 5) & 1);
++		msg_pdbg("BERASE %i, ", (tmp2 >> 3) & 3);
++		msg_pdbg("AEL %i, ", (tmp2 >> 2) & 1);
++		msg_pdbg("FCERR %i, ", (tmp2 >> 1) & 1);
++		msg_pdbg("FDONE %i\n", (tmp2 >> 0) & 1);
++
++		tmp = mmio_readl(ich_spibar + 0x50);
++		msg_pdbg("0x50: 0x%08x (FRAP)\n", tmp);
++		msg_pdbg("BMWAG 0x%02x, ", ICH_BMWAG(tmp));
++		msg_pdbg("BMRAG 0x%02x, ", ICH_BMRAG(tmp));
++		msg_pdbg("BRWA 0x%02x, ", ICH_BRWA(tmp));
++		msg_pdbg("BRRA 0x%02x\n", ICH_BRRA(tmp));
++
++		/* print out the FREGx registers along with FRAP access bits */
++		for(i = 0; i < 5; i++)
++			do_ich9_spi_frap(tmp, i);
++
++		msg_pdbg("0x74: 0x%08x (PR0)\n",
++			     mmio_readl(ich_spibar + 0x74));
++		msg_pdbg("0x78: 0x%08x (PR1)\n",
++			     mmio_readl(ich_spibar + 0x78));
++		msg_pdbg("0x7C: 0x%08x (PR2)\n",
++			     mmio_readl(ich_spibar + 0x7C));
++		msg_pdbg("0x80: 0x%08x (PR3)\n",
++			     mmio_readl(ich_spibar + 0x80));
++		msg_pdbg("0x84: 0x%08x (PR4)\n",
++			     mmio_readl(ich_spibar + 0x84));
++		msg_pdbg("0x90: 0x%08x (SSFS, SSFC)\n",
++			     mmio_readl(ich_spibar + 0x90));
++		msg_pdbg("0x94: 0x%04x     (PREOP)\n",
++			     mmio_readw(ich_spibar + 0x94));
++		msg_pdbg("0x96: 0x%04x     (OPTYPE)\n",
++			     mmio_readw(ich_spibar + 0x96));
++		msg_pdbg("0x98: 0x%08x (OPMENU)\n",
++			     mmio_readl(ich_spibar + 0x98));
++		msg_pdbg("0x9C: 0x%08x (OPMENU+4)\n",
++			     mmio_readl(ich_spibar + 0x9C));
++		ichspi_bbar = mmio_readl(ich_spibar + 0xA0);
++		msg_pdbg("0xA0: 0x%08x (BBAR)\n",
++			     ichspi_bbar);
++		msg_pdbg("0xB0: 0x%08x (FDOC)\n",
++			     mmio_readl(ich_spibar + 0xB0));
++		if (tmp2 & (1 << 15)) {
++			msg_pinfo("WARNING: SPI Configuration Lockdown activated.\n");
++			ichspi_lock = 1;
++		}
++		ich_init_opcodes();
++		break;
++	default:
++		/* Nothing */
++		break;
++	}
++
++	old = pci_read_byte(dev, 0xdc);
++	msg_pdbg("SPI Read Configuration: ");
++	new = (old >> 2) & 0x3;
++	switch (new) {
++	case 0:
++	case 1:
++	case 2:
++		msg_pdbg("prefetching %sabled, caching %sabled, ",
++			     (new & 0x2) ? "en" : "dis",
++			     (new & 0x1) ? "dis" : "en");
++		break;
++	default:
++		msg_pdbg("invalid prefetching/caching settings, ");
++		break;
++	}
++	return 0;
++}
++
++int via_init_spi(struct pci_dev *dev)
++{
++	uint32_t mmio_base;
++
++	mmio_base = (pci_read_long(dev, 0xbc)) << 8;
++	msg_pdbg("MMIO base at = 0x%x\n", mmio_base);
++	ich_spibar = physmap("VT8237S MMIO registers", mmio_base, 0x70);
++
++	msg_pdbg("0x6c: 0x%04x     (CLOCK/DEBUG)\n",
++		     mmio_readw(ich_spibar + 0x6c));
++
++	/* Not sure if it speaks all these bus protocols. */
++	buses_supported = CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI;
++	spi_controller = SPI_CONTROLLER_VIA;
++	ich_init_opcodes();
++
++	return 0;
++}
++
++#endif
+diff --git a/internal.c b/internal.c
+index 174370c..8b19692 100644
+--- a/internal.c
++++ b/internal.c
+@@ -21,11 +21,9 @@
+ #include <stdint.h>
+ #include <string.h>
+ #include <stdlib.h>
+-#include <fcntl.h>
+ #include <sys/types.h>
+-#include <sys/stat.h>
+-#include <errno.h>
+ #include "flash.h"
++#include "programmer.h"
+ 
+ #if NEED_PCI == 1
+ struct pci_dev *pci_dev_find_filter(struct pci_filter filter)
+@@ -98,11 +96,13 @@ struct pci_dev *pci_card_find(uint16_t vendor, uint16_t device,
+ }
+ #endif
+ 
+-#if INTERNAL_SUPPORT == 1
+-struct superio superio = {};
++#if CONFIG_INTERNAL == 1
+ int force_boardenable = 0;
+ int force_boardmismatch = 0;
+ 
++#if defined(__i386__) || defined(__x86_64__)
++struct superio superio = {};
++
+ void probe_superio(void)
+ {
+ 	superio = probe_superio_ite();
+@@ -112,8 +112,9 @@ void probe_superio(void)
+ 		superio = probe_superio_winbond();
+ #endif
+ }
++#endif
+ 
+-int is_laptop;
++int is_laptop = 0;
+ 
+ int internal_init(void)
+ {
+@@ -121,36 +122,45 @@ int internal_init(void)
+ 	int force_laptop = 0;
+ 	char *arg;
+ 
+-	arg = extract_param(&programmer_param, "boardenable=", ",:");
++	arg = extract_programmer_param("boardenable");
+ 	if (arg && !strcmp(arg,"force")) {
+ 		force_boardenable = 1;
+ 	} else if (arg && !strlen(arg)) {
+ 		msg_perr("Missing argument for boardenable.\n");
++		free(arg);
++		return 1;
+ 	} else if (arg) {
+ 		msg_perr("Unknown argument for boardenable: %s\n", arg);
+-		exit(1);
++		free(arg);
++		return 1;
+ 	}
+ 	free(arg);
+ 
+-	arg = extract_param(&programmer_param, "boardmismatch=", ",:");
++	arg = extract_programmer_param("boardmismatch");
+ 	if (arg && !strcmp(arg,"force")) {
+ 		force_boardmismatch = 1;
+ 	} else if (arg && !strlen(arg)) {
+ 		msg_perr("Missing argument for boardmismatch.\n");
++		free(arg);
++		return 1;
+ 	} else if (arg) {
+ 		msg_perr("Unknown argument for boardmismatch: %s\n", arg);
+-		exit(1);
++		free(arg);
++		return 1;
+ 	}
+ 	free(arg);
+ 
+-	arg = extract_param(&programmer_param, "laptop=", ",:");
++	arg = extract_programmer_param("laptop");
+ 	if (arg && !strcmp(arg,"force_I_want_a_brick")) {
+ 		force_laptop = 1;
+ 	} else if (arg && !strlen(arg)) {
+ 		msg_perr("Missing argument for laptop.\n");
++		free(arg);
++		return 1;
+ 	} else if (arg) {
+ 		msg_perr("Unknown argument for laptop: %s\n", arg);
+-		exit(1);
++		free(arg);
++		return 1;
+ 	}
+ 	free(arg);
+ 
+@@ -162,14 +172,29 @@ int internal_init(void)
+ 	pci_init(pacc);		/* Initialize the PCI library */
+ 	pci_scan_bus(pacc);	/* We want to get the list of devices */
+ 
+-	/* We look at the lbtable first to see if we need a
++	if (processor_flash_enable()) {
++		msg_perr("Processor detection/init failed.\n"
++			 "Aborting.\n");
++		return 1;
++	}
++
++#if defined(__i386__) || defined(__x86_64__)
++	/* We look at the cbtable first to see if we need a
+ 	 * mainboard specific flash enable sequence.
+ 	 */
+ 	coreboot_init();
++
+ 	dmi_init();
+ 
+ 	/* Probe for the Super I/O chip and fill global struct superio. */
+ 	probe_superio();
++#else
++	/* FIXME: Enable cbtable searching on all non-x86 platforms supported
++	 *        by coreboot.
++	 * FIXME: Find a replacement for DMI on non-x86.
++	 * FIXME: Enable Super I/O probing once port I/O is possible.
++	 */
++#endif
+ 
+ 	/* Warn if a laptop is detected. */
+ 	if (is_laptop) {
+@@ -194,6 +219,7 @@ int internal_init(void)
+ 		}
+ 	}
+ 
++#if __FLASHROM_LITTLE_ENDIAN__
+ 	/* try to enable it. Failure IS an option, since not all motherboards
+ 	 * really need this to be done, etc., etc.
+ 	 */
+@@ -203,8 +229,12 @@ int internal_init(void)
+ 			 "will most likely fail.\n");
+ 	}
+ 
+-	/* Probe for IT87* LPC->SPI translation unconditionally. */
+-	it87xx_probe_spi_flash(NULL);
++#if defined(__i386__) || defined(__x86_64__)
++	/* Probe unconditionally for IT87* LPC->SPI translation and for
++	 * IT87* Parallel write enable.
++	 */
++	init_superio_ite();
++#endif
+ 
+ 	board_flash_enable(lb_vendor, lb_part);
+ 
+@@ -212,7 +242,26 @@ int internal_init(void)
+ 	 * The error code might have been a warning only.
+ 	 * Besides that, we don't check the board enable return code either.
+ 	 */
++#if defined(__i386__) || defined(__x86_64__)
+ 	return 0;
++#else
++	msg_perr("Your platform is not supported yet for the internal "
++		 "programmer due to missing\n"
++		 "flash_base and top/bottom alignment information.\n"
++		 "Aborting.\n");
++	return 1;
++#endif
++#else
++	/* FIXME: Remove this unconditional abort once all PCI drivers are
++	 * converted to use little-endian accesses for memory BARs.
++	 */
++	msg_perr("Your platform is not supported yet for the internal "
++		 "programmer because it has\n"
++		 "not been converted from native endian to little endian "
++		 "access yet.\n"
++		 "Aborting.\n");
++	return 1;
++#endif
+ }
+ 
+ int internal_shutdown(void)
+diff --git a/it87spi.c b/it87spi.c
+index 0ee7d12..9ecc414 100644
+--- a/it87spi.c
++++ b/it87spi.c
+@@ -23,10 +23,13 @@
+  * Contains the ITE IT87* SPI specific routines
+  */
+ 
++#if defined(__i386__) || defined(__x86_64__)
++
+ #include <string.h>
+ #include <stdlib.h>
+ #include "flash.h"
+ #include "chipdrivers.h"
++#include "programmer.h"
+ #include "spi.h"
+ 
+ #define ITE_SUPERIO_PORT1	0x2e
+@@ -34,7 +37,7 @@
+ 
+ uint16_t it8716f_flashport = 0;
+ /* use fast 33MHz SPI (<>0) or slow 16MHz (0) */
+-int fast_spi = 1;
++static int fast_spi = 1;
+ 
+ /* Helper functions for most recent ITE IT87xx Super I/O chips */
+ #define CHIP_ID_BYTE1_REG	0x20
+@@ -81,8 +84,8 @@ struct superio probe_superio_ite(void)
+ 		case 0x82:
+ 		case 0x86:
+ 		case 0x87:
+-			msg_pinfo("Found ITE Super I/O, id %04hx\n",
+-				     ret.model);
++			msg_pinfo("Found ITE Super I/O, ID 0x%04hx.\n",
++				  ret.model);
+ 			return ret;
+ 		}
+ 	}
+@@ -94,88 +97,109 @@ struct superio probe_superio_ite(void)
+ 	return ret;
+ }
+ 
+-static uint16_t find_ite_spi_flash_port(uint16_t port, uint16_t id)
++static uint16_t it87spi_probe(uint16_t port)
+ {
+ 	uint8_t tmp = 0;
+ 	char *portpos = NULL;
+ 	uint16_t flashport = 0;
+ 
+-	switch (id) {
+-	case 0x8716:
+-	case 0x8718:
+-	case 0x8720:
+-		enter_conf_mode_ite(port);
+-		/* NOLDN, reg 0x24, mask out lowest bit (suspend) */
+-		tmp = sio_read(port, 0x24) & 0xFE;
+-		/* If IT87SPI was not explicitly selected, we want to check
+-		 * quickly if LPC->SPI translation is active.
+-		 */
+-		if ((programmer == PROGRAMMER_INTERNAL) && !(tmp & (0x0E))) {
+-			msg_pdbg("No IT87* serial flash segment enabled.\n");
+-			exit_conf_mode_ite(port);
+-			break;
+-		}
+-		msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
+-		       0xFFFE0000, 0xFFFFFFFF, (tmp & 1 << 1) ? "en" : "dis");
+-		msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
+-		       0x000E0000, 0x000FFFFF, (tmp & 1 << 1) ? "en" : "dis");
+-		msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
+-		       0xFFEE0000, 0xFFEFFFFF, (tmp & 1 << 2) ? "en" : "dis");
+-		msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
+-		       0xFFF80000, 0xFFFEFFFF, (tmp & 1 << 3) ? "en" : "dis");
+-		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.
++	enter_conf_mode_ite(port);
++	/* NOLDN, reg 0x24, mask out lowest bit (suspend) */
++	tmp = sio_read(port, 0x24) & 0xFE;
++	/* If IT87SPI was not explicitly selected, we want to check
++	 * quickly if LPC->SPI translation is active.
++	 */
++	if ((programmer == PROGRAMMER_INTERNAL) && !(tmp & (0x0E))) {
++		msg_pdbg("No IT87* serial flash segment enabled.\n");
++		exit_conf_mode_ite(port);
++		/* Nothing to do. */
++		return 1;
++	}
++	msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
++		 0xFFFE0000, 0xFFFFFFFF, (tmp & 1 << 1) ? "en" : "dis");
++	msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
++		 0x000E0000, 0x000FFFFF, (tmp & 1 << 1) ? "en" : "dis");
++	msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
++		 0xFFEE0000, 0xFFEFFFFF, (tmp & 1 << 2) ? "en" : "dis");
++	msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
++		 0xFFF80000, 0xFFFEFFFF, (tmp & 1 << 3) ? "en" : "dis");
++	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))) {
++		msg_pdbg("Enabling LPC write to serial flash\n");
++		tmp |= 1 << 4;
++		sio_write(port, 0x24, tmp);
++	}
++	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);
++	msg_pdbg("Serial flash port 0x%04x\n", flashport);
++	/* Non-default port requested? */
++	portpos = extract_programmer_param("it87spiport");
++	if (portpos) {
++		char *endptr = NULL;
++		unsigned long forced_flashport;
++		forced_flashport = strtoul(portpos, &endptr, 0);
++		/* Port 0, port >0x1000, unaligned ports and garbage strings
++		 * are rejected.
+ 		 */
+-		/* If any serial flash segment is enabled, enable writing. */
+-		if ((tmp & 0xe) && (!(tmp & 1 << 4))) {
+-			msg_pdbg("Enabling LPC write to serial flash\n");
+-			tmp |= 1 << 4;
+-			sio_write(port, 0x24, tmp);
+-		}
+-		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);
+-		msg_pdbg("Serial flash port 0x%04x\n", flashport);
+-		if (programmer_param && !strlen(programmer_param)) {
+-			free(programmer_param);
+-			programmer_param = NULL;
++		if (!forced_flashport || (forced_flashport >= 0x1000) ||
++		    (forced_flashport & 0x7) || (*endptr != '\0')) {
++			/* Using ports below 0x100 is a really bad idea, and
++			 * should only be done if no port between 0x100 and
++			 * 0xff8 works due to routing issues.
++			 */
++			msg_perr("Error: it87spiport specified, but no valid "
++				 "port specified.\nPort must be a multiple of "
++				 "0x8 and lie between 0x100 and 0xff8.\n");
++			free(portpos);
++			/* FIXME: Return failure here once it87spi_common_init()
++			 * can handle the return value sanely.
++			 */
++			exit(1);
++		} else {
++			flashport = (uint16_t)forced_flashport;
++			msg_pinfo("Forcing serial flash port 0x%04x\n",
++				  flashport);
++			sio_write(port, 0x64, (flashport >> 8));
++			sio_write(port, 0x65, (flashport & 0xff));
+ 		}
+-		if (programmer_param) {
+-			portpos = extract_param(&programmer_param,
+-						"it87spiport=", ",:");
+-			if (portpos) {
+-				flashport = strtol(portpos, (char **)NULL, 0);
+-				msg_pinfo("Forcing serial flash port 0x%04x\n",
+-					  flashport);
+-				sio_write(port, 0x64, (flashport >> 8));
+-				sio_write(port, 0x65, (flashport & 0xff));
+-				free(portpos);
+-			}
+-		}
+-		exit_conf_mode_ite(port);
+-		break;
+-	/* TODO: Handle more IT87xx if they support flash translation */
+-	default:
+-		msg_pdbg("SuperI/O ID %04hx is not on the controller list.\n", id);
+ 	}
+-	return flashport;
++	free(portpos);
++	exit_conf_mode_ite(port);
++	it8716f_flashport = flashport;
++	if (buses_supported & CHIP_BUSTYPE_SPI)
++		msg_pdbg("Overriding chipset SPI with IT87 SPI.\n");
++	spi_controller = SPI_CONTROLLER_IT87XX;
++	buses_supported |= CHIP_BUSTYPE_SPI;
++	return 0;
+ }
+ 
+-int it87spi_common_init(void)
++int init_superio_ite(void)
+ {
+ 	if (superio.vendor != SUPERIO_VENDOR_ITE)
+ 		return 1;
+ 
+-	it8716f_flashport = find_ite_spi_flash_port(superio.port, superio.model);
+-
+-	if (it8716f_flashport)
+-		spi_controller = SPI_CONTROLLER_IT87XX;
+-
+-	return (!it8716f_flashport);
++	switch (superio.model) {
++	case 0x8705:
++		return it8705f_write_enable(superio.port);
++		break;
++	case 0x8716:
++	case 0x8718:
++	case 0x8720:
++		return it87spi_probe(superio.port);
++		break;
++	default:
++		msg_pdbg("Super I/O ID 0x%04hx is not on the list of flash "
++			 "capable controllers.\n", superio.model);
++	}
++	return 1;
+ }
+ 
+ 
+@@ -186,7 +210,7 @@ int it87spi_init(void)
+ 	get_io_perms();
+ 	/* Probe for the Super I/O chip and fill global struct superio. */
+ 	probe_superio();
+-	ret = it87spi_common_init();
++	ret = init_superio_ite();
+ 	if (!ret) {
+ 		buses_supported = CHIP_BUSTYPE_SPI;
+ 	} else {
+@@ -195,19 +219,6 @@ int it87spi_init(void)
+ 	return ret;
+ }
+ 
+-int it87xx_probe_spi_flash(const char *name)
+-{
+-	int ret;
+-
+-	ret = it87spi_common_init();
+-	if (!ret) {
+-		if (buses_supported & CHIP_BUSTYPE_SPI)
+-			msg_pdbg("Overriding chipset SPI with IT87 SPI.\n");
+-		buses_supported |= CHIP_BUSTYPE_SPI;
+-	}
+-	return ret;
+-}
+-
+ /*
+  * The IT8716F only supports commands with length 1,2,4,5 bytes including
+  * command byte and can not read more than 3 bytes from the device.
+@@ -282,7 +293,7 @@ int it8716f_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ }
+ 
+ /* Page size is usually 256 bytes */
+-static int it8716f_spi_page_program(struct flashchip *flash, int block, uint8_t *buf)
++static int it8716f_spi_page_program(struct flashchip *flash, uint8_t *buf, int start)
+ {
+ 	int i;
+ 	int result;
+@@ -295,7 +306,7 @@ static int it8716f_spi_page_program(struct flashchip *flash, int block, uint8_t
+ 	OUTB(0x06, it8716f_flashport + 1);
+ 	OUTB(((2 + (fast_spi ? 1 : 0)) << 4), it8716f_flashport);
+ 	for (i = 0; i < 256; i++) {
+-		chip_writeb(buf[256 * block + i], bios + 256 * block + i);
++		chip_writeb(buf[i], bios + start + i);
+ 	}
+ 	OUTB(0, it8716f_flashport);
+ 	/* Wait until the Write-In-Progress bit is cleared.
+@@ -324,30 +335,41 @@ int it8716f_spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int
+ 	return 0;
+ }
+ 
+-int it8716f_spi_chip_write_256(struct flashchip *flash, uint8_t *buf)
++int it8716f_spi_chip_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
+ {
+-	int total_size = 1024 * flash->total_size;
+-	int i;
+-
+ 	/*
+ 	 * IT8716F only allows maximum of 512 kb SPI chip size for memory
+ 	 * mapped access.
+ 	 */
+-	if ((programmer == PROGRAMMER_IT87SPI) || (total_size > 512 * 1024)) {
+-		spi_chip_write_1(flash, buf);
++	if ((programmer == PROGRAMMER_IT87SPI) || (flash->total_size * 1024 > 512 * 1024)) {
++		spi_chip_write_1_new(flash, buf, start, len);
+ 	} else {
+-		spi_disable_blockprotect();
+-		/* Erase first */
+-		msg_pinfo("Erasing flash before programming... ");
+-		if (erase_flash(flash)) {
+-			msg_perr("ERASE FAILED!\n");
+-			return -1;
++		int lenhere;
++
++		if (start % 256) {
++			/* start to the end of the page or start + len,
++			 * whichever is smaller. Page length is hardcoded to
++			 * 256 bytes (IT87 SPI hardware limitation).
++			 */
++			lenhere = min(len, (start | 0xff) - start + 1);
++			spi_chip_write_1_new(flash, buf, start, lenhere);
++			start += lenhere;
++			len -= lenhere;
++			buf += lenhere;
+ 		}
+-		msg_pinfo("done.\n");
+-		for (i = 0; i < total_size / 256; i++) {
+-			it8716f_spi_page_program(flash, i, buf);
++
++		/* FIXME: Handle chips which have max writechunk size >1 and <256. */
++		while (len >= 256) {
++			it8716f_spi_page_program(flash, buf, start);
++			start += 256;
++			len -= 256;
++			buf += 256;
+ 		}
++		if (len)
++			spi_chip_write_1_new(flash, buf, start, len);
+ 	}
+ 
+ 	return 0;
+ }
++
++#endif
+diff --git a/jedec.c b/jedec.c
+index 30c343f..05cba79 100644
+--- a/jedec.c
++++ b/jedec.c
+@@ -38,7 +38,7 @@ uint8_t oddparity(uint8_t val)
+ 	return (val ^ (val >> 1)) & 0x1;
+ }
+ 
+-void toggle_ready_jedec_common(chipaddr dst, int delay)
++static void toggle_ready_jedec_common(chipaddr dst, int delay)
+ {
+ 	unsigned int i = 0;
+ 	uint8_t tmp1, tmp2;
+@@ -70,7 +70,7 @@ void toggle_ready_jedec(chipaddr dst)
+  * 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)
++static void toggle_ready_jedec_slow(chipaddr dst)
+ {
+ 	toggle_ready_jedec_common(dst, 8 * 1000);
+ }
+@@ -92,7 +92,7 @@ void data_polling_jedec(chipaddr dst, uint8_t data)
+ 		msg_cdbg("%s: excessive loops, i=0x%x\n", __func__, i);
+ }
+ 
+-void start_program_jedec_common(struct flashchip *flash, unsigned int mask)
++static void start_program_jedec_common(struct flashchip *flash, unsigned int mask)
+ {
+ 	chipaddr bios = flash->virtual_memory;
+ 	chip_writeb(0xAA, bios + (0x5555 & mask));
+@@ -100,7 +100,7 @@ void start_program_jedec_common(struct flashchip *flash, unsigned int mask)
+ 	chip_writeb(0xA0, bios + (0x5555 & mask));
+ }
+ 
+-int probe_jedec_common(struct flashchip *flash, unsigned int mask)
++static int probe_jedec_common(struct flashchip *flash, unsigned int mask)
+ {
+ 	chipaddr bios = flash->virtual_memory;
+ 	uint8_t id1, id2;
+@@ -199,7 +199,7 @@ int probe_jedec_common(struct flashchip *flash, unsigned int mask)
+ 	return 1;
+ }
+ 
+-int erase_sector_jedec_common(struct flashchip *flash, unsigned int page,
++static int erase_sector_jedec_common(struct flashchip *flash, unsigned int page,
+ 			      unsigned int pagesize, unsigned int mask)
+ {
+ 	chipaddr bios = flash->virtual_memory;
+@@ -229,7 +229,7 @@ int erase_sector_jedec_common(struct flashchip *flash, unsigned int page,
+ 	return 0;
+ }
+ 
+-int erase_block_jedec_common(struct flashchip *flash, unsigned int block,
++static int erase_block_jedec_common(struct flashchip *flash, unsigned int block,
+ 			     unsigned int blocksize, unsigned int mask)
+ {
+ 	chipaddr bios = flash->virtual_memory;
+@@ -259,7 +259,7 @@ int erase_block_jedec_common(struct flashchip *flash, unsigned int block,
+ 	return 0;
+ }
+ 
+-int erase_chip_jedec_common(struct flashchip *flash, unsigned int mask)
++static int erase_chip_jedec_common(struct flashchip *flash, unsigned int mask)
+ {
+ 	int total_size = flash->total_size * 1024;
+ 	chipaddr bios = flash->virtual_memory;
+@@ -288,7 +288,7 @@ int erase_chip_jedec_common(struct flashchip *flash, unsigned int mask)
+ 	return 0;
+ }
+ 
+-int write_byte_program_jedec_common(struct flashchip *flash, uint8_t *src,
++static int write_byte_program_jedec_common(struct flashchip *flash, uint8_t *src,
+ 			     chipaddr dst, unsigned int mask)
+ {
+ 	int tried = 0, failed = 0;
+@@ -335,7 +335,7 @@ int write_sector_jedec_common(struct flashchip *flash, uint8_t *src,
+ 	return failed;
+ }
+ 
+-int write_page_write_jedec_common(struct flashchip *flash, uint8_t *src,
++static 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;
+@@ -374,7 +374,7 @@ retry:
+ 	return failed;
+ }
+ 
+-int getaddrmask(struct flashchip *flash)
++static int getaddrmask(struct flashchip *flash)
+ {
+ 	switch (flash->feature_bits & FEATURE_ADDR_MASK) {
+ 	case FEATURE_ADDR_FULL:
+diff --git a/layout.c b/layout.c
+index df77c2f..d65e370 100644
+--- a/layout.c
++++ b/layout.c
+@@ -18,16 +18,18 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
++#include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <ctype.h>
+ #include "flash.h"
++#include "programmer.h"
+ 
+-#if INTERNAL_SUPPORT == 1
++#if CONFIG_INTERNAL == 1
+ char *mainboard_vendor = NULL;
+ char *mainboard_part = NULL;
+ #endif
+-int romimages = 0;
++static int romimages = 0;
+ 
+ #define MAX_ROMLAYOUT	16
+ 
+@@ -38,9 +40,9 @@ typedef struct {
+ 	char name[256];
+ } romlayout_t;
+ 
+-romlayout_t rom_entries[MAX_ROMLAYOUT];
++static romlayout_t rom_entries[MAX_ROMLAYOUT];
+ 
+-#if INTERNAL_SUPPORT == 1 /* FIXME: Move the whole block to cbtable.c? */
++#if CONFIG_INTERNAL == 1 /* FIXME: Move the whole block to cbtable.c? */
+ static char *def_name = "DEFAULT";
+ 
+ int show_id(uint8_t *bios, int size, int force)
+@@ -73,7 +75,7 @@ int show_id(uint8_t *bios, int size, int force)
+ 	mb_vendor_offset = *(walk - 2);
+ 	if ((*walk) == 0 || ((*walk) & 0x3ff) != 0 || (*walk) > size ||
+ 	    mb_part_offset > size || mb_vendor_offset > size) {
+-		printf("Flash image seems to be a legacy BIOS. Disabling checks.\n");
++		msg_pinfo("Flash image seems to be a legacy BIOS. Disabling checks.\n");
+ 		return 0;
+ 	}
+ 
+@@ -81,25 +83,25 @@ int show_id(uint8_t *bios, int size, int force)
+ 	mb_vendor = (char *)(bios + size - mb_vendor_offset);
+ 	if (!isprint((unsigned char)*mb_part) ||
+ 	    !isprint((unsigned char)*mb_vendor)) {
+-		printf("Flash image seems to have garbage in the ID location."
++		msg_pinfo("Flash image seems to have garbage in the ID location."
+ 		       " Disabling checks.\n");
+ 		return 0;
+ 	}
+ 
+-	printf_debug("coreboot last image size "
++	msg_pdbg("coreboot last image size "
+ 		     "(not ROM size) is %d bytes.\n", *walk);
+ 
+ 	mainboard_part = strdup(mb_part);
+ 	mainboard_vendor = strdup(mb_vendor);
+-	printf_debug("Manufacturer: %s\n", mainboard_vendor);
+-	printf_debug("Mainboard ID: %s\n", mainboard_part);
++	msg_pdbg("Manufacturer: %s\n", mainboard_vendor);
++	msg_pdbg("Mainboard ID: %s\n", mainboard_part);
+ 
+ 	/*
+ 	 * If lb_vendor is not set, the coreboot table was
+ 	 * not found. Nor was -m VENDOR:PART specified.
+ 	 */
+ 	if (!lb_vendor || !lb_part) {
+-		printf("Note: If the following flash access fails, "
++		msg_pinfo("Note: If the following flash access fails, "
+ 		       "try -m <vendor>:<mainboard>.\n");
+ 		return 0;
+ 	}
+@@ -109,13 +111,13 @@ int show_id(uint8_t *bios, int size, int force)
+ 	 */
+ 	if (!strcasecmp(mainboard_vendor, lb_vendor) &&
+ 	    !strcasecmp(mainboard_part, lb_part)) {
+-		printf_debug("This firmware image matches this mainboard.\n");
++		msg_pdbg("This firmware image matches this mainboard.\n");
+ 	} else {
+ 		if (force_boardmismatch) {
+-			printf("WARNING: This firmware image does not "
++			msg_pinfo("WARNING: This firmware image does not "
+ 			       "seem to fit to this machine - forcing it.\n");
+ 		} else {
+-			printf("ERROR: Your firmware image (%s:%s) does not "
++			msg_pinfo("ERROR: Your firmware image (%s:%s) does not "
+ 			       "appear to\n       be correct for the detected "
+ 			       "mainboard (%s:%s)\n\nOverride with -p internal:"
+ 			       "boardmismatch=force if you are absolutely sure "
+@@ -141,7 +143,7 @@ int read_romlayout(char *name)
+ 	romlayout = fopen(name, "r");
+ 
+ 	if (!romlayout) {
+-		fprintf(stderr, "ERROR: Could not open ROM layout (%s).\n",
++		msg_gerr("ERROR: Could not open ROM layout (%s).\n",
+ 			name);
+ 		return -1;
+ 	}
+@@ -159,7 +161,7 @@ int read_romlayout(char *name)
+ 		tstr1 = strtok(tempstr, ":");
+ 		tstr2 = strtok(NULL, ":");
+ 		if (!tstr1 || !tstr2) {
+-			fprintf(stderr, "Error parsing layout file.\n");
++			msg_gerr("Error parsing layout file.\n");
+ 			fclose(romlayout);
+ 			return 1;
+ 		}
+@@ -170,7 +172,7 @@ int read_romlayout(char *name)
+ 	}
+ 
+ 	for (i = 0; i < romimages; i++) {
+-		printf_debug("romlayout %08x - %08x named %s\n",
++		msg_gdbg("romlayout %08x - %08x named %s\n",
+ 			     rom_entries[i].start,
+ 			     rom_entries[i].end, rom_entries[i].name);
+ 	}
+@@ -187,16 +189,16 @@ int find_romentry(char *name)
+ 	if (!romimages)
+ 		return -1;
+ 
+-	printf("Looking for \"%s\"... ", name);
++	msg_ginfo("Looking for \"%s\"... ", name);
+ 
+ 	for (i = 0; i < romimages; i++) {
+ 		if (!strcmp(rom_entries[i].name, name)) {
+ 			rom_entries[i].included = 1;
+-			printf("found.\n");
++			msg_ginfo("found.\n");
+ 			return i;
+ 		}
+ 	}
+-	printf("not found.\n");	// Not found. Error.
++	msg_ginfo("not found.\n");	// Not found. Error.
+ 
+ 	return -1;
+ }
+diff --git a/m29f400bt.c b/m29f400bt.c
+index c8ed5d8..2a770ba 100644
+--- a/m29f400bt.c
++++ b/m29f400bt.c
+@@ -204,56 +204,3 @@ int write_m29f400bt(struct flashchip *flash, uint8_t *buf)
+ 
+ 	return 0;
+ }
+-
+-int write_coreboot_m29f400bt(struct flashchip *flash, uint8_t *buf)
+-{
+-	chipaddr bios = flash->virtual_memory;
+-
+-	msg_cinfo("Programming page:\n ");
+-	/*********************************
+-	*Pages for M29F400BT:
+-	* 16	0x7c000		0x7ffff		TOP
+-	*  8 	0x7a000		0x7bfff
+-	*  8 	0x78000		0x79fff
+-	* 32	0x70000		0x77fff
+-	* 64	0x60000		0x6ffff
+-	* 64	0x50000		0x5ffff
+-	* 64	0x40000		0x4ffff
+-	*---------------------------------
+-	* 64	0x30000		0x3ffff
+-	* 64	0x20000		0x2ffff
+-	* 64	0x10000		0x1ffff
+-	* 64	0x00000		0x0ffff		BOTTOM
+-	*********************************/
+-	msg_cinfo("%04d at address: 0x%08x\n", 7, 0x00000);
+-	if (block_erase_m29f400bt(flash, 0x00000, 64 * 1024)) {
+-		msg_cerr("ERASE FAILED!\n");
+-		return -1;
+-	}
+-	write_page_m29f400bt(bios, buf + 0x00000, bios + 0x00000, 64 * 1024);
+-
+-	msg_cinfo("%04d at address: 0x%08x\n", 7, 0x10000);
+-	if (block_erase_m29f400bt(flash, 0x10000, 64 * 1024)) {
+-		msg_cerr("ERASE FAILED!\n");
+-		return -1;
+-	}
+-	write_page_m29f400bt(bios, buf + 0x10000, bios + 0x10000, 64 * 1024);
+-
+-	msg_cinfo("%04d at address: 0x%08x\n", 7, 0x20000);
+-	if (block_erase_m29f400bt(flash, 0x20000, 64 * 1024)) {
+-		msg_cerr("ERASE FAILED!\n");
+-		return -1;
+-	}
+-	write_page_m29f400bt(bios, buf + 0x20000, bios + 0x20000, 64 * 1024);
+-
+-	msg_cinfo("%04d at address: 0x%08x\n", 7, 0x30000);
+-	if (block_erase_m29f400bt(flash, 0x30000, 64 * 1024)) {
+-		msg_cerr("ERASE FAILED!\n");
+-		return -1;
+-	}
+-	write_page_m29f400bt(bios, buf + 0x30000, bios + 0x30000, 64 * 1024);
+-
+-	msg_cinfo("\n");
+-
+-	return 0;
+-}
+diff --git a/mcp6x_spi.c b/mcp6x_spi.c
+new file mode 100644
+index 0000000..bfdef2d
+--- /dev/null
++++ b/mcp6x_spi.c
+@@ -0,0 +1,193 @@
++/*
++ * 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
++ */
++
++/* Driver for the NVIDIA MCP6x/MCP7x MCP6X_SPI controller.
++ * Based on clean room reverse engineered docs from
++ * http://www.flashrom.org/pipermail/flashrom/2009-December/001180.html
++ * created by Michael Karcher.
++ */
++
++#if defined(__i386__) || defined(__x86_64__)
++
++#include <stdint.h>
++#include <stdlib.h>
++#include <ctype.h>
++#include "flash.h"
++#include "programmer.h"
++
++/* Bit positions for each pin. */
++
++#define MCP6X_SPI_CS		1
++#define MCP6X_SPI_SCK		2
++#define MCP6X_SPI_MOSI		3
++#define MCP6X_SPI_MISO		4
++#define MCP6X_SPI_REQUEST	0
++#define MCP6X_SPI_GRANT		8
++
++void *mcp6x_spibar = NULL;
++
++static void mcp6x_request_spibus(void)
++{
++	uint8_t tmp;
++
++	tmp = mmio_readb(mcp6x_spibar + 0x530);
++	tmp |= 1 << MCP6X_SPI_REQUEST;
++	mmio_writeb(tmp, mcp6x_spibar + 0x530);
++
++	/* Wait until we are allowed to use the SPI bus. */
++	while (!(mmio_readw(mcp6x_spibar + 0x530) & (1 << MCP6X_SPI_GRANT))) ;
++}
++
++static void mcp6x_release_spibus(void)
++{
++	uint8_t tmp;
++
++	tmp = mmio_readb(mcp6x_spibar + 0x530);
++	tmp &= ~(1 << MCP6X_SPI_REQUEST);
++	mmio_writeb(tmp, mcp6x_spibar + 0x530);
++}
++
++static void mcp6x_bitbang_set_cs(int val)
++{
++	uint8_t tmp;
++
++	/* Requesting and releasing the SPI bus is handled in here to allow the
++	 * chipset to use its own SPI engine for native reads.
++	 */
++	if (val == 0)
++		mcp6x_request_spibus();
++
++	tmp = mmio_readb(mcp6x_spibar + 0x530);
++	tmp &= ~(1 << MCP6X_SPI_CS);
++	tmp |= (val << MCP6X_SPI_CS);
++	mmio_writeb(tmp, mcp6x_spibar + 0x530);
++
++	if (val == 1)
++		mcp6x_release_spibus();
++}
++
++static void mcp6x_bitbang_set_sck(int val)
++{
++	uint8_t tmp;
++
++	tmp = mmio_readb(mcp6x_spibar + 0x530);
++	tmp &= ~(1 << MCP6X_SPI_SCK);
++	tmp |= (val << MCP6X_SPI_SCK);
++	mmio_writeb(tmp, mcp6x_spibar + 0x530);
++}
++
++static void mcp6x_bitbang_set_mosi(int val)
++{
++	uint8_t tmp;
++
++	tmp = mmio_readb(mcp6x_spibar + 0x530);
++	tmp &= ~(1 << MCP6X_SPI_MOSI);
++	tmp |= (val << MCP6X_SPI_MOSI);
++	mmio_writeb(tmp, mcp6x_spibar + 0x530);
++}
++
++static int mcp6x_bitbang_get_miso(void)
++{
++	uint8_t tmp;
++
++	tmp = mmio_readb(mcp6x_spibar + 0x530);
++	tmp = (tmp >> MCP6X_SPI_MISO) & 0x1;
++	return tmp;
++}
++
++static const struct bitbang_spi_master bitbang_spi_master_mcp6x = {
++	.type = BITBANG_SPI_MASTER_MCP,
++	.set_cs = mcp6x_bitbang_set_cs,
++	.set_sck = mcp6x_bitbang_set_sck,
++	.set_mosi = mcp6x_bitbang_set_mosi,
++	.get_miso = mcp6x_bitbang_get_miso,
++};
++
++int mcp6x_spi_init(int want_spi)
++{
++	uint16_t status;
++	uint32_t mcp6x_spibaraddr;
++	struct pci_dev *smbusdev;
++
++	/* Look for the SMBus device (SMBus PCI class) */
++	smbusdev = pci_dev_find_vendorclass(0x10de, 0x0c05);
++	if (!smbusdev) {
++		if (want_spi) {
++			msg_perr("ERROR: SMBus device not found. Not enabling "
++				 "SPI.\n");
++			return 1;
++		} else {
++			msg_pinfo("Odd. SMBus device not found.\n");
++			return 0;
++		}
++	}
++	msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n",
++		smbusdev->vendor_id, smbusdev->device_id,
++		smbusdev->bus, smbusdev->dev, smbusdev->func);
++
++
++	/* Locate the BAR where the SPI interface lives. */
++	mcp6x_spibaraddr = pci_read_long(smbusdev, 0x74);
++	/* BAR size is 64k, bits 15..4 are zero, bit 3..0 declare a
++	 * 32-bit non-prefetchable memory BAR.
++	 */
++	mcp6x_spibaraddr &= ~0xffff;
++	msg_pdbg("MCP SPI BAR is at 0x%08x\n", mcp6x_spibaraddr);
++
++	/* Accessing a NULL pointer BAR is evil. Don't do it. */
++	if (!mcp6x_spibaraddr && want_spi) {
++		msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR "
++			 "is invalid.\n");
++		return 1;
++	} else if (!mcp6x_spibaraddr && !want_spi) {
++		msg_pdbg("MCP SPI is not used.\n");
++		return 0;
++	} else if (mcp6x_spibaraddr && !want_spi) {
++		msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently"
++			 " doesn't have SPI enabled.\n");
++		/* FIXME: Should we enable SPI anyway? */
++		return 0;
++	}
++	/* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
++	mcp6x_spibar = physmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544);
++
++#if 0
++	/* FIXME: Run the physunmap in a shutdown function. */
++	physunmap(mcp6x_spibar, 0x544);
++#endif
++
++	status = mmio_readw(mcp6x_spibar + 0x530);
++	msg_pdbg("SPI control is 0x%04x, req=%i, gnt=%i\n",
++		 status, (status >> MCP6X_SPI_REQUEST) & 0x1,
++		 (status >> MCP6X_SPI_GRANT) & 0x1);
++
++	/* 1 usec halfperiod delay for now. */
++	if (bitbang_spi_init(&bitbang_spi_master_mcp6x, 1)) {
++		/* This should never happen. */
++		msg_perr("MCP6X bitbang SPI master init failed!\n");
++		return 1;
++	}
++
++	buses_supported |= CHIP_BUSTYPE_SPI;
++	spi_controller = SPI_CONTROLLER_MCP6X_BITBANG;
++
++	return 0;
++}
++
++#endif
+diff --git a/nic3com.c b/nic3com.c
+index d3dcce5..0eb781a 100644
+--- a/nic3com.c
++++ b/nic3com.c
+@@ -18,10 +18,11 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
++#if defined(__i386__) || defined(__x86_64__)
++
+ #include <stdlib.h>
+-#include <string.h>
+-#include <sys/types.h>
+ #include "flash.h"
++#include "programmer.h"
+ 
+ #define BIOS_ROM_ADDR		0x04
+ #define BIOS_ROM_DATA		0x08
+@@ -31,10 +32,10 @@
+ 
+ #define PCI_VENDOR_ID_3COM	0x10b7
+ 
+-uint32_t internal_conf;
+-uint16_t id;
++static uint32_t internal_conf;
++static uint16_t id;
+ 
+-struct pcidev_status nics_3com[] = {
++const struct pcidev_status nics_3com[] = {
+ 	/* 3C90xB */
+ 	{0x10b7, 0x9055, OK, "3COM", "3C90xB: PCI 10/100 Mbps; shared 10BASE-T/100BASE-TX"},
+ 	{0x10b7, 0x9001, NT, "3COM", "3C90xB: PCI 10/100 Mbps; shared 10BASE-T/100BASE-T4" },
+@@ -59,7 +60,8 @@ int nic3com_init(void)
+ 	get_io_perms();
+ 
+ 	io_base_addr = pcidev_init(PCI_VENDOR_ID_3COM, PCI_BASE_ADDRESS_0,
+-				   nics_3com, programmer_param);
++				   nics_3com);
++
+ 	id = pcidev_dev->device_id;
+ 
+ 	/* 3COM 3C90xB cards need a special fixup. */
+@@ -81,6 +83,7 @@ int nic3com_init(void)
+ 	OUTW(SELECT_REG_WINDOW + 0, io_base_addr + INT_STATUS);
+ 
+ 	buses_supported = CHIP_BUSTYPE_PARALLEL;
++	max_rom_decode.parallel = 128 * 1024;
+ 
+ 	return 0;
+ }
+@@ -95,7 +98,6 @@ int nic3com_shutdown(void)
+ 		OUTL(internal_conf, io_base_addr + INTERNAL_CONFIG);
+ 	}
+ 
+-	free(programmer_param);
+ 	pci_cleanup(pacc);
+ 	release_io_perms();
+ 	return 0;
+@@ -112,3 +114,7 @@ uint8_t nic3com_chip_readb(const chipaddr addr)
+ 	OUTL((uint32_t)addr, io_base_addr + BIOS_ROM_ADDR);
+ 	return INB(io_base_addr + BIOS_ROM_DATA);
+ }
++
++#else
++#error PCI port I/O access is not supported on this architecture yet.
++#endif
+diff --git a/nicintel_spi.c b/nicintel_spi.c
+new file mode 100644
+index 0000000..d09facc
+--- /dev/null
++++ b/nicintel_spi.c
+@@ -0,0 +1,187 @@
++/*
++ * This file is part of the flashrom project.
++ *
++ * Copyright (C) 2010 Carl-Daniel Hailfinger
++ * Copyright (C) 2010 Idwer Vollering
++ *
++ * 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
++ */
++
++/*
++ * Datasheet:
++ * PCI/PCI-X Family of Gigabit Ethernet Controllers Software Developer's Manual
++ * 82540EP/EM, 82541xx, 82544GC/EI, 82545GM/EM, 82546GB/EB, and 82547xx
++ * http://download.intel.com/design/network/manuals/8254x_GBe_SDM.pdf
++ */
++
++#include <stdlib.h>
++#include "flash.h"
++#include "programmer.h"
++
++#define PCI_VENDOR_ID_INTEL 0x8086
++
++#define EECD	0x10
++#define FLA	0x1c
++
++/*
++ * Register bits of EECD.
++ * 
++ * Bit 04, 05: FWE (Flash Write Enable Control)
++ * 00b = not allowed
++ * 01b = flash writes disabled
++ * 10b = flash writes enabled
++ * 11b = not allowed
++ */
++#define FLASH_WRITES_DISABLED	0x10 /* FWE: 10000b */
++#define FLASH_WRITES_ENABLED	0x20 /* FWE: 100000b */
++
++/* Flash Access register bits */
++/* Table 13-9 */
++#define FL_SCK	0
++#define FL_CS	1
++#define FL_SI	2
++#define FL_SO	3
++#define FL_REQ	4
++#define FL_GNT	5
++/* Currently unused */
++// #define FL_BUSY	30
++// #define FL_ER	31
++
++uint8_t *nicintel_spibar;
++
++const struct pcidev_status nics_intel_spi[] = {
++	{PCI_VENDOR_ID_INTEL, 0x107c, OK, "Intel", "82541PI Gigabit Ethernet Controller"},
++
++	{},
++};
++
++static void nicintel_request_spibus(void)
++{
++	uint32_t tmp;
++
++	tmp = pci_mmio_readl(nicintel_spibar + FLA);
++	tmp |= 1 << FL_REQ;
++	pci_mmio_writel(tmp, nicintel_spibar + FLA);
++
++	/* Wait until we are allowed to use the SPI bus. */
++	while (!(pci_mmio_readl(nicintel_spibar + FLA) & (1 << FL_GNT))) ;
++}
++
++static void nicintel_release_spibus(void)
++{
++	uint32_t tmp;
++
++	tmp = pci_mmio_readl(nicintel_spibar + FLA);
++	tmp &= ~(1 << FL_REQ);
++	pci_mmio_writel(tmp, nicintel_spibar + FLA);
++}
++
++static void nicintel_bitbang_set_cs(int val)
++{
++	uint32_t tmp;
++
++	/*
++	 * Requesting and releasing the SPI bus is handled in here to allow
++	 * the chipset to use its own SPI engine for native reads.
++	 */
++	if (val == 0)
++		nicintel_request_spibus();
++
++	tmp = pci_mmio_readl(nicintel_spibar + FLA);
++	tmp &= ~(1 << FL_CS);
++	tmp |= (val << FL_CS);
++	pci_mmio_writel(tmp,  nicintel_spibar + FLA);
++
++	if (val == 1)
++		nicintel_release_spibus();
++}
++
++static void nicintel_bitbang_set_sck(int val)
++{
++	uint32_t tmp;
++
++	tmp = pci_mmio_readl(nicintel_spibar + FLA);
++	tmp &= ~(1 << FL_SCK);
++	tmp |= (val << FL_SCK);
++	pci_mmio_writel(tmp, nicintel_spibar + FLA);
++}
++
++static void nicintel_bitbang_set_mosi(int val)
++{
++	uint32_t tmp;
++
++	tmp = pci_mmio_readl(nicintel_spibar + FLA);
++	tmp &= ~(1 << FL_SI);
++	tmp |= (val << FL_SI);
++	pci_mmio_writel(tmp, nicintel_spibar + FLA);
++}
++
++static int nicintel_bitbang_get_miso(void)
++{
++	uint32_t tmp;
++
++	tmp = pci_mmio_readl(nicintel_spibar + FLA);
++	tmp = (tmp >> FL_SO) & 0x1;
++	return tmp;
++}
++
++static const struct bitbang_spi_master bitbang_spi_master_nicintel = {
++	.type = BITBANG_SPI_MASTER_NICINTEL,
++	.set_cs = nicintel_bitbang_set_cs,
++	.set_sck = nicintel_bitbang_set_sck,
++	.set_mosi = nicintel_bitbang_set_mosi,
++	.get_miso = nicintel_bitbang_get_miso,
++};
++
++int nicintel_spi_init(void)
++{
++	uint32_t tmp;
++
++	get_io_perms();
++
++	io_base_addr = pcidev_init(PCI_VENDOR_ID_INTEL, PCI_BASE_ADDRESS_0,
++				   nics_intel_spi);
++
++	nicintel_spibar = physmap("Intel Gigabit NIC w/ SPI flash",
++				  io_base_addr, 4096);
++	tmp = pci_mmio_readl(nicintel_spibar + EECD);
++	tmp &= ~FLASH_WRITES_DISABLED;
++	tmp |= FLASH_WRITES_ENABLED;
++	pci_mmio_writel(tmp, nicintel_spibar + EECD);
++
++	/* 1 usec halfperiod delay for now. */
++	if (bitbang_spi_init(&bitbang_spi_master_nicintel, 1))
++		return 1;
++
++	buses_supported = CHIP_BUSTYPE_SPI;
++	spi_controller = SPI_CONTROLLER_NICINTEL;
++
++	return 0;
++}
++
++int nicintel_spi_shutdown(void)
++{
++	uint32_t tmp;
++
++	tmp = pci_mmio_readl(nicintel_spibar + EECD);
++	tmp &= ~FLASH_WRITES_ENABLED;
++	tmp |= FLASH_WRITES_DISABLED;
++	pci_mmio_writel(tmp, nicintel_spibar + EECD);
++
++	physunmap(nicintel_spibar, 4096);
++	pci_cleanup(pacc);
++	release_io_perms();
++
++	return 0;
++}
+diff --git a/nicnatsemi.c b/nicnatsemi.c
+new file mode 100644
+index 0000000..1683857
+--- /dev/null
++++ b/nicnatsemi.c
+@@ -0,0 +1,95 @@
++/*
++ * This file is part of the flashrom project.
++ *
++ * Copyright (C) 2010 Andrew Morgan <ziltro at ziltro.com>
++ *
++ * 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
++ */
++
++#if defined(__i386__) || defined(__x86_64__)
++
++#include <stdlib.h>
++#include "flash.h"
++#include "programmer.h"
++
++#define PCI_VENDOR_ID_NATSEMI	0x100b
++
++#define BOOT_ROM_ADDR		0x50
++#define BOOT_ROM_DATA		0x54
++
++const struct pcidev_status nics_natsemi[] = {
++	{0x100b, 0x0020, NT, "National Semiconductor", "DP83815/DP83816"},
++	{0x100b, 0x0022, NT, "National Semiconductor", "DP83820"},
++	{},
++};
++
++int nicnatsemi_init(void)
++{
++	get_io_perms();
++
++	io_base_addr = pcidev_init(PCI_VENDOR_ID_NATSEMI, PCI_BASE_ADDRESS_0,
++				   nics_natsemi);
++
++	buses_supported = CHIP_BUSTYPE_PARALLEL;
++
++	/* The datasheet shows address lines MA0-MA16 in one place and MA0-MA15
++	 * in another. My NIC has MA16 connected to A16 on the boot ROM socket
++	 * so I'm assuming it is accessible. If not then next line wants to be
++	 * max_rom_decode.parallel = 65536; and the mask in the read/write
++	 * functions below wants to be 0x0000FFFF.
++	 */
++	max_rom_decode.parallel = 131072;
++
++	return 0;
++}
++
++int nicnatsemi_shutdown(void)
++{
++	pci_cleanup(pacc);
++	release_io_perms();
++	return 0;
++}
++
++void nicnatsemi_chip_writeb(uint8_t val, chipaddr addr)
++{
++	OUTL((uint32_t)addr & 0x0001FFFF, io_base_addr + BOOT_ROM_ADDR);
++	/*
++	 * The datasheet requires 32 bit accesses to this register, but it seems
++	 * that requirement might only apply if the register is memory mapped.
++	 * Bits 8-31 of this register are apparently don't care, and if this
++	 * register is I/O port mapped, 8 bit accesses to the lowest byte of the
++	 * register seem to work fine. Due to that, we ignore the advice in the
++	 * data sheet.
++	 */
++	OUTB(val, io_base_addr + BOOT_ROM_DATA);
++}
++
++uint8_t nicnatsemi_chip_readb(const chipaddr addr)
++{
++	OUTL(((uint32_t)addr & 0x0001FFFF), io_base_addr + BOOT_ROM_ADDR);
++	/*
++	 * The datasheet requires 32 bit accesses to this register, but it seems
++	 * that requirement might only apply if the register is memory mapped.
++	 * Bits 8-31 of this register are apparently don't care, and if this
++	 * register is I/O port mapped, 8 bit accesses to the lowest byte of the
++	 * register seem to work fine. Due to that, we ignore the advice in the
++	 * data sheet.
++	 */
++	return INB(io_base_addr + BOOT_ROM_DATA);
++}
++
++#else
++#error PCI port I/O access is not supported on this architecture yet.
++#endif
+diff --git a/nicrealtek.c b/nicrealtek.c
+new file mode 100644
+index 0000000..c32e5d6
+--- /dev/null
++++ b/nicrealtek.c
+@@ -0,0 +1,115 @@
++/*
++ * This file is part of the flashrom project.
++ *
++ * Copyright (C) 2009 Joerg Fischer <turboj at gmx.de>
++ *
++ * 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
++ */
++
++#if defined(__i386__) || defined(__x86_64__)
++
++#include <stdlib.h>
++#include "flash.h"
++#include "programmer.h"
++
++#define PCI_VENDOR_ID_REALTEK	0x10ec
++#define PCI_VENDOR_ID_SMC1211	0x1113
++
++#define BIOS_ROM_ADDR		0xD4
++#define BIOS_ROM_DATA		0xD7
++
++const struct pcidev_status nics_realtek[] = {
++	{0x10ec, 0x8139, OK, "Realtek", "RTL8139/8139C/8139C+"},
++	{},
++};
++
++const struct pcidev_status nics_realteksmc1211[] = {
++	{0x1113, 0x1211, OK, "SMC2", "1211TX"}, /* RTL8139 clone */
++	{},
++};
++
++int nicrealtek_init(void)
++{
++	get_io_perms();
++
++	io_base_addr = pcidev_init(PCI_VENDOR_ID_REALTEK, PCI_BASE_ADDRESS_0,
++				   nics_realtek);
++
++	buses_supported = CHIP_BUSTYPE_PARALLEL;
++
++	return 0;
++}
++
++int nicsmc1211_init(void)
++{
++	get_io_perms();
++
++	io_base_addr = pcidev_init(PCI_VENDOR_ID_SMC1211, PCI_BASE_ADDRESS_0,
++				   nics_realteksmc1211);
++
++	buses_supported = CHIP_BUSTYPE_PARALLEL;
++
++	return 0;
++}
++
++int nicrealtek_shutdown(void)
++{
++	/* FIXME: We forgot to disable software access again. */
++	pci_cleanup(pacc);
++	release_io_perms();
++	return 0;
++}
++
++void nicrealtek_chip_writeb(uint8_t val, chipaddr addr)
++{
++	/* Output addr and data, set WE to 0, set OE to 1, set CS to 0,
++	 * enable software access.
++	 */
++	OUTL(((uint32_t)addr & 0x01FFFF) | 0x0A0000 | (val << 24),
++	     io_base_addr + BIOS_ROM_ADDR);
++	/* Output addr and data, set WE to 1, set OE to 1, set CS to 1,
++	 * enable software access.
++	 */
++	OUTL(((uint32_t)addr & 0x01FFFF) | 0x1E0000 | (val << 24),
++	     io_base_addr + BIOS_ROM_ADDR);
++}
++
++uint8_t nicrealtek_chip_readb(const chipaddr addr)
++{
++	uint8_t val;
++
++	/* FIXME: Can we skip reading the old data and simply use 0? */
++	/* Read old data. */
++	val = INB(io_base_addr + BIOS_ROM_DATA);
++	/* Output new addr and old data, set WE to 1, set OE to 0, set CS to 0,
++	 * enable software access.
++	 */
++	OUTL(((uint32_t)addr & 0x01FFFF) | 0x060000 | (val << 24),
++	     io_base_addr + BIOS_ROM_ADDR);
++
++	/* Read new data. */
++	val = INB(io_base_addr + BIOS_ROM_DATA);
++	/* Output addr and new data, set WE to 1, set OE to 1, set CS to 1,
++	 * enable software access.
++	 */
++	OUTL(((uint32_t)addr & 0x01FFFF) | 0x1E0000 | (val << 24),
++	     io_base_addr + BIOS_ROM_ADDR);
++
++	return val;
++}
++
++#else
++#error PCI port I/O access is not supported on this architecture yet.
++#endif
+diff --git a/pcidev.c b/pcidev.c
+index add1fce..f7048e7 100644
+--- a/pcidev.c
++++ b/pcidev.c
+@@ -22,14 +22,14 @@
+ #include <string.h>
+ #include <sys/types.h>
+ #include "flash.h"
++#include "programmer.h"
+ 
+ uint32_t io_base_addr;
+ struct pci_access *pacc;
+-struct pci_filter filter;
+ struct pci_dev *pcidev_dev = NULL;
+ 
+ uint32_t pcidev_validate(struct pci_dev *dev, uint32_t bar,
+-			 struct pcidev_status *devs)
++			 const struct pcidev_status *devs)
+ {
+ 	int i;
+ 	/* FIXME: 64 bit memory BARs need a 64 bit addr. */
+@@ -79,9 +79,11 @@ uint32_t pcidev_validate(struct pci_dev *dev, uint32_t bar,
+ }
+ 
+ uint32_t pcidev_init(uint16_t vendor_id, uint32_t bar,
+-		     struct pcidev_status *devs, char *pcidev_bdf)
++		     const struct pcidev_status *devs)
+ {
+ 	struct pci_dev *dev;
++	struct pci_filter filter;
++	char *pcidev_bdf;
+ 	char *msg = NULL;
+ 	int found = 0;
+ 	uint32_t addr = 0, curaddr = 0;
+@@ -93,12 +95,14 @@ uint32_t pcidev_init(uint16_t vendor_id, uint32_t bar,
+ 
+ 	/* Filter by vendor and also bb:dd.f (if supplied by the user). */
+ 	filter.vendor = vendor_id;
++	pcidev_bdf = extract_programmer_param("pci");
+ 	if (pcidev_bdf != NULL) {
+ 		if ((msg = pci_filter_parse_slot(&filter, pcidev_bdf))) {
+ 			msg_perr("Error: %s\n", msg);
+ 			exit(1);
+ 		}
+ 	}
++	free(pcidev_bdf);
+ 
+ 	for (dev = pacc->devices; dev; dev = dev->next) {
+ 		if (pci_filter_match(&filter, dev)) {
+@@ -125,7 +129,7 @@ uint32_t pcidev_init(uint16_t vendor_id, uint32_t bar,
+ 	return curaddr;
+ }
+ 
+-void print_supported_pcidevs(struct pcidev_status *devs)
++void print_supported_pcidevs(const struct pcidev_status *devs)
+ {
+ 	int i;
+ 
+diff --git a/physmap.c b/physmap.c
+index 03fef95..ded3485 100644
+--- a/physmap.c
++++ b/physmap.c
+@@ -20,14 +20,20 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
++#include <unistd.h>
++#include <stdio.h>
+ #include <sys/types.h>
+-#include <sys/stat.h>
+-#include <fcntl.h>
+ #include <stdlib.h>
+ #include <string.h>
+-#include <errno.h>
+ #include "flash.h"
+ 
++/* Do we need any file access or ioctl for physmap or MSR? */
++#if !defined(__DJGPP__)
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <errno.h>
++#endif
++
+ #ifdef __DJGPP__
+ #include <dpmi.h>
+ #include <sys/nearptr.h>
+@@ -56,7 +62,7 @@ static void *map_first_meg(unsigned long phys_addr, size_t len)
+ 	return realmem_map + phys_addr;
+ }
+ 
+-void *sys_physmap(unsigned long phys_addr, size_t len)
++static void *sys_physmap(unsigned long phys_addr, size_t len)
+ {
+ 	int ret;
+ 	__dpmi_meminfo mi;
+@@ -104,7 +110,7 @@ void physunmap(void *virt_addr, size_t len)
+ 
+ #define MEM_DEV "DirectIO"
+ 
+-void *sys_physmap(unsigned long phys_addr, size_t len)
++static void *sys_physmap(unsigned long phys_addr, size_t len)
+ {
+ 	return map_physical(phys_addr, len);
+ }
+@@ -131,7 +137,7 @@ static int fd_mem = -1;
+ static int fd_mem_cached = -1;
+ 
+ /* 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)
++static void *sys_physmap_rw_uncached(unsigned long phys_addr, size_t len)
+ {
+ 	void *virt_addr;
+ 
+@@ -151,7 +157,7 @@ void *sys_physmap_rw_uncached(unsigned long phys_addr, size_t len)
+ /* 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)
++static void *sys_physmap_ro_cached(unsigned long phys_addr, size_t len)
+ {
+ 	void *virt_addr;
+ 
+@@ -184,7 +190,7 @@ void physunmap(void *virt_addr, size_t len)
+ #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)
++static void *physmap_common(const char *descr, unsigned long phys_addr, size_t len, int mayfail, int readonly)
+ {
+ 	void *virt_addr;
+ 
+@@ -223,6 +229,10 @@ void *physmap_common(const char *descr, unsigned long phys_addr, size_t len, int
+ 			msg_perr("You can override CONFIG_X86_PAT at boot with the nopat kernel parameter but\n");
+ 			msg_perr("disabling the other option unfortunately requires a kernel recompile. Sorry!\n");
+ 		}
++#elif defined (__OpenBSD__)
++		msg_perr("Please set securelevel=-1 in /etc/rc.securelevel "
++			   "and reboot, or reboot into \n");
++		msg_perr("single user mode.\n");
+ #endif
+ 		if (!mayfail)
+ 			exit(3);
+@@ -241,6 +251,8 @@ 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);
+ }
+ 
++#if defined(__i386__) || defined(__x86_64__)
++
+ #ifdef __linux__
+ /*
+  * Reading and writing to MSRs, however requires instructions rdmsr/wrmsr,
+@@ -308,8 +320,8 @@ int wrmsr(int addr, msr_t msr)
+ int setup_cpu_msr(int cpu)
+ {
+ 	char msrfilename[64];
+-	memset(msrfilename, 0, 64);
+-	sprintf(msrfilename, "/dev/cpu/%d/msr", cpu);
++	memset(msrfilename, 0, sizeof(msrfilename));
++	snprintf(msrfilename, sizeof(msrfilename), "/dev/cpu/%d/msr", cpu);
+ 
+ 	if (fd_msr != -1) {
+ 		msg_pinfo("MSR was already initialized\n");
+@@ -391,8 +403,8 @@ int wrmsr(int addr, msr_t msr)
+ int setup_cpu_msr(int cpu)
+ {
+ 	char msrfilename[64];
+-	memset(msrfilename, 0, 64);
+-	sprintf(msrfilename, "/dev/cpu%d", cpu);
++	memset(msrfilename, 0, sizeof(msrfilename));
++	snprintf(msrfilename, sizeof(msrfilename), "/dev/cpu%d", cpu);
+ 
+ 	if (fd_msr != -1) {
+ 		msg_pinfo("MSR was already initialized\n");
+@@ -462,4 +474,6 @@ void cleanup_cpu_msr(void)
+ #endif
+ #endif
+ #endif
+-
++#else
++/* Does MSR exist on non-x86 architectures? */
++#endif
+diff --git a/pm49fl00x.c b/pm49fl00x.c
+index fd93ef6..4136b17 100644
+--- a/pm49fl00x.c
++++ b/pm49fl00x.c
+@@ -23,7 +23,7 @@
+ #include "flash.h"
+ #include "chipdrivers.h"
+ 
+-void write_lockbits_49fl00x(chipaddr bios, int size,
++static void write_lockbits_49fl00x(chipaddr bios, int size,
+ 			    unsigned char bits, int block_size)
+ {
+ 	int i, left = size;
+diff --git a/print.c b/print.c
+index abc1209..52b8a9f 100644
+--- a/print.c
++++ b/print.c
+@@ -19,10 +19,12 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
++#include <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+ #include "flash.h"
+ #include "flashchips.h"
++#include "programmer.h"
+ 
+ /*
+  * Return a string corresponding to the bustype parameter.
+@@ -72,29 +74,36 @@ static int digits(int n)
+ 	return i;
+ }
+ 
+-void print_supported_chips(void)
++static void print_supported_chips(void)
+ {
+ 	int okcol = 0, pos = 0, i, chipcount = 0;
++	int maxchiplen = 0, maxvendorlen = 0;
+ 	struct flashchip *f;
+ 
+ 	for (f = flashchips; f->name != NULL; f++) {
+-		if (GENERIC_DEVICE_ID == f->model_id)
++		/* Ignore "unknown XXXX SPI chip" entries. */
++		if (!strncmp(f->name, "unknown", 7))
+ 			continue;
+-		okcol = max(okcol, strlen(f->vendor) + 1 + strlen(f->name));
+-	}
+-	okcol = (okcol + 7) & ~7;
+-
+-	for (f = flashchips; f->name != NULL; f++)
+ 		chipcount++;
+-
+-	printf("\nSupported flash chips (total: %d):\n\n", chipcount);
+-	POS_PRINT("Vendor:   Device:");
+-	while (pos < okcol) {
+-		printf("\t");
+-		pos += 8 - (pos % 8);
++		maxvendorlen = max(maxvendorlen, strlen(f->vendor));
++		maxchiplen = max(maxchiplen, strlen(f->name));
+ 	}
++	maxvendorlen++;
++	maxchiplen++;
++	okcol = maxvendorlen + maxchiplen;
+ 
+-	printf("Tested OK:\tKnown BAD:  Size/KB:  Type:\n\n");
++	printf("\nSupported flash chips (total: %d):\n\n", chipcount);
++	printf("Vendor");
++	for (i = strlen("Vendor"); i < maxvendorlen; i++)
++		printf(" ");
++	printf("Device");
++	for (i = strlen("Device"); i < maxchiplen; i++)
++		printf(" ");
++
++	printf("Tested   Known    Size/KB:  Type:\n");
++	for (i = 0; i < okcol; i++)
++		printf(" ");
++	printf("OK       Broken\n\n");
+ 	printf("(P = PROBE, R = READ, E = ERASE, W = WRITE)\n\n");
+ 
+ 	for (f = flashchips; f->name != NULL; f++) {
+@@ -103,15 +112,13 @@ void print_supported_chips(void)
+ 			continue;
+ 
+ 		printf("%s", f->vendor);
+-		for (i = 0; i < 10 - strlen(f->vendor); i++)
++		for (i = strlen(f->vendor); i < maxvendorlen; i++)
+ 			printf(" ");
+ 		printf("%s", f->name);
++		for (i = strlen(f->name); i < maxchiplen; i++)
++			printf(" ");
+ 
+-		pos = 10 + strlen(f->name);
+-		while (pos < okcol) {
+-			printf("\t");
+-			pos += 8 - (pos % 8);
+-		}
++		pos = maxvendorlen + maxchiplen;
+ 		if ((f->tested & TEST_OK_MASK)) {
+ 			if ((f->tested & TEST_OK_PROBE))
+ 				POS_PRINT("P ");
+@@ -123,29 +130,33 @@ void print_supported_chips(void)
+ 				POS_PRINT("W ");
+ 		}
+ 		while (pos < okcol + 9) {
+-			printf("\t");
+-			pos += 8 - (pos % 8);
++			printf(" ");
++			pos++;
+ 		}
+ 		if ((f->tested & TEST_BAD_MASK)) {
+ 			if ((f->tested & TEST_BAD_PROBE))
+-				printf("P ");
++				POS_PRINT("P ");
+ 			if ((f->tested & TEST_BAD_READ))
+-				printf("R ");
++				POS_PRINT("R ");
+ 			if ((f->tested & TEST_BAD_ERASE))
+-				printf("E ");
++				POS_PRINT("E ");
+ 			if ((f->tested & TEST_BAD_WRITE))
+-				printf("W ");
++				POS_PRINT("W ");
+ 		}
+ 
+-		printf("\t    %d", f->total_size);
++		while (pos < okcol + 18) {
++			printf(" ");
++			pos++;
++		}
++		printf("%d", f->total_size);
+ 		for (i = 0; i < 10 - digits(f->total_size); i++)
+ 			printf(" ");
+ 		printf("%s\n", flashbuses_to_text(f->bustype));
+ 	}
+ }
+ 
+-#if INTERNAL_SUPPORT == 1
+-void print_supported_chipsets(void)
++#if CONFIG_INTERNAL == 1
++static void print_supported_chipsets(void)
+ {
+ 	int i, j, chipsetcount = 0;
+ 	const struct penable *c = chipset_enables;
+@@ -168,263 +179,388 @@ void print_supported_chipsets(void)
+ 	}
+ }
+ 
+-void print_supported_boards_helper(const struct board_info *b, const char *msg)
++static void print_supported_boards_helper(const struct board_info *boards,
++				   const char *devicetype)
+ {
+-	int i, j, boardcount = 0;
++	int i, j, boardcount_good = 0, boardcount_bad = 0;
++	const struct board_pciid_enable *b = board_pciid_enables;
+ 
+-	for (i = 0; b[i].vendor != NULL; i++)
+-		boardcount++;
+-
+-	printf("\n%s (total: %d):\n\n", msg, boardcount);
+-
+-	for (i = 0; b[i].vendor != NULL; i++) {
+-		printf("%s", b[i].vendor);
+-		for (j = 0; j < 25 - strlen(b[i].vendor); j++)
+-			printf(" ");
+-		printf("%s", b[i].name);
+-		for (j = 0; j < 28 - strlen(b[i].name); j++)
+-			printf(" ");
+-		printf("\n");
++	for (i = 0; boards[i].vendor != NULL; i++) {
++		if (boards[i].working)
++			boardcount_good++;
++		else
++			boardcount_bad++;
+ 	}
+-}
+-
+-void print_supported_boards(void)
+-{
+-	int i, j, boardcount = 0;
+-	struct board_pciid_enable *b = board_pciid_enables;
+ 
+-	for (i = 0; b[i].vendor_name != NULL; i++)
+-		boardcount++;
++	printf("\nKnown %s (good: %d, bad: %d):"
++	       "\n\nVendor:                  Board:                      "
++	       "Status: Required option:"
++	       "\n\n", devicetype, boardcount_good, boardcount_bad);
+ 
+-	printf("\nSupported boards which need write-enable code (total: %d):"
+-	       "\n\nVendor:                  Board:                        "
+-	       "Required option:\n\n", boardcount);
+-
+-	for (i = 0; b[i].vendor_name != NULL; i++) {
+-		printf("%s", b[i].vendor_name);
+-		for (j = 0; j < 25 - strlen(b[i].vendor_name); j++)
++	for (i = 0; boards[i].vendor != NULL; i++) {
++		printf("%s", boards[i].vendor);
++		for (j = 0; j < 25 - strlen(boards[i].vendor); j++)
+ 			printf(" ");
+-		printf("%s", b[i].board_name);
+-		for (j = 0; j < 30 - strlen(b[i].board_name); j++)
++		printf("%s", boards[i].name);
++		for (j = 0; j < 28 - strlen(boards[i].name); j++)
+ 			printf(" ");
+-		if (b[i].lb_vendor != NULL)
+-			printf("-m %s:%s\n", b[i].lb_vendor, b[i].lb_part);
+-		else
+-			printf("(none, board is autodetected)\n");
++		printf((boards[i].working) ? "OK      " : "BAD     ");
++
++		for (j = 0; b[j].vendor_name != NULL; j++) {
++			if (strcmp(b[j].vendor_name, boards[i].vendor)
++			    || strcmp(b[j].board_name, boards[i].name))
++				continue;
++			if (b[j].lb_vendor == NULL)
++				printf("(autodetected)");
++			else
++				printf("-m %s:%s", b[j].lb_vendor,
++						   b[j].lb_part);
++		}
++		printf("\n");
+ 	}
+-
+-	print_supported_boards_helper(boards_ok,
+-		"Supported boards which don't need write-enable code");
+-	print_supported_boards_helper(boards_bad,
+-		"Boards which have been verified to NOT work yet");
+-	print_supported_boards_helper(laptops_ok,
+-		"Laptops which have been verified to work");
+-	print_supported_boards_helper(laptops_bad,
+-		"Laptops which have been verified to NOT work yet");
+ }
+ #endif
+ 
+ void print_supported(void)
+ {
+ 		print_supported_chips();
+-#if INTERNAL_SUPPORT == 1
++#if CONFIG_INTERNAL == 1
+ 		print_supported_chipsets();
+-		print_supported_boards();
++		print_supported_boards_helper(boards_known, "boards");
++		print_supported_boards_helper(laptops_known, "laptops");
+ #endif
+-#if (NIC3COM_SUPPORT == 1) || (GFXNVIDIA_SUPPORT == 1) || (DRKAISER_SUPPORT == 1) || (SATASII_SUPPORT == 1)
++#if CONFIG_NIC3COM+CONFIG_NICREALTEK+CONFIG_NICNATSEMI+CONFIG_GFXNVIDIA+CONFIG_DRKAISER+CONFIG_SATASII+CONFIG_ATAHPT >= 1
+ 		printf("\nSupported PCI devices flashrom can use "
+ 		       "as programmer:\n\n");
+ #endif
+-#if NIC3COM_SUPPORT == 1
++#if CONFIG_NIC3COM == 1
+ 		print_supported_pcidevs(nics_3com);
+ #endif
+-#if GFXNVIDIA_SUPPORT == 1
++#if CONFIG_NICREALTEK == 1
++		print_supported_pcidevs(nics_realtek);
++		print_supported_pcidevs(nics_realteksmc1211);
++#endif
++#if CONFIG_NICNATSEMI == 1
++		print_supported_pcidevs(nics_natsemi);
++#endif
++#if CONFIG_GFXNVIDIA == 1
+ 		print_supported_pcidevs(gfx_nvidia);
+ #endif
+-#if DRKAISER_SUPPORT == 1
++#if CONFIG_DRKAISER == 1
+ 		print_supported_pcidevs(drkaiser_pcidev);
+ #endif
+-#if SATASII_SUPPORT == 1
++#if CONFIG_SATASII == 1
+ 		print_supported_pcidevs(satas_sii);
+ #endif
+-#if ATAHPT_SUPPORT == 1
++#if CONFIG_ATAHPT == 1
+ 		print_supported_pcidevs(ata_hpt);
+ #endif
+-}
+-
++#if CONFIG_NICINTEL_SPI == 1
++		print_supported_pcidevs(nics_intel_spi);
++#endif
+ 
+-#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. */
+-	{ "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",		"A7V133", },
+-	{ "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", },
+-	{ "ASUS",		"P5B-Deluxe", },
+-	{ "ASUS",		"P5KC", },
+-	{ "ASUS",		"P5L-MX", },
+-	{ "ASUS",		"P6T Deluxe", },
+-	{ "ASUS",		"P6T Deluxe V2", },
+-	{ "A-Trend",		"ATC-6220", },
+-	{ "BCOM",		"WinNET100", },
+-	{ "DFI",		"Blood-Iron P35 T2RL", },
+-	{ "Elitegroup",		"K7S5A", },
+-	{ "Elitegroup",		"P6VAP-A+", },
+-	{ "GIGABYTE",		"GA-2761GXDK", },
+-	{ "GIGABYTE",		"GA-6BXC", },
+-	{ "GIGABYTE",		"GA-6BXDU", },
+-	{ "GIGABYTE",		"GA-6ZMA", },
+-	{ "GIGABYTE",		"GA-7ZM", },
+-	{ "GIGABYTE",		"GA-965P-DS4", },
+-	{ "GIGABYTE",		"GA-EP35-DS3L", },
+-	{ "GIGABYTE",		"GA-EX58-UD4P", },
+-	{ "GIGABYTE",		"GA-M57SLI-S4", },
+-	{ "GIGABYTE",		"GA-M61P-S3", },
+-	{ "GIGABYTE",		"GA-MA69VM-S2", },
+-	{ "GIGABYTE",		"GA-MA770T-UD3P", },
+-	{ "GIGABYTE",		"GA-MA78G-DS3H", },
+-	{ "GIGABYTE",		"GA-MA78GM-S2H", },
+-	{ "GIGABYTE",		"GA-MA78GPM-DS2H", },
+-	{ "GIGABYTE",		"GA-MA790FX-DQ6", },
+-	{ "GIGABYTE",		"GA-MA790GP-DS4H", },
+-	{ "Intel",		"EP80759", },
+-	{ "Jetway",		"J7F4K1G5D-PB", },
+-	{ "MSI",                "MS-6153", },
+-	{ "MSI",                "MS-6156", },
+-	{ "MSI",                "MS-6330 (K7T Turbo)", },
+-	{ "MSI",		"MS-6570 (K7N2)", },
+-	{ "MSI",		"MS-7065", },
+-	{ "MSI",		"MS-7168 (Orion)", },
+-	{ "MSI",		"MS-7236 (945PL Neo3)", },
+-	{ "MSI",		"MS-7255 (P4M890M)", },
+-	{ "MSI",		"MS-7312 (K9MM-V)", },
+-	{ "MSI",		"MS-7345 (P35 Neo2-FIR)", },
+-	{ "MSI",		"MS-7368 (K9AG Neo2-Digital)", },
+-	{ "MSI",                "MS-7376 (K9A2 Platinum)", },
+-	{ "NEC",		"PowerMate 2000", },
+-	{ "PC Engines",		"Alix.1c", },
+-	{ "PC Engines",		"Alix.2c2", },
+-	{ "PC Engines",		"Alix.2c3", },
+-	{ "PC Engines",		"Alix.3c3", },
+-	{ "PC Engines",		"Alix.3d3", },
+-	{ "PC Engines",		"WRAP.2E", },
+-	{ "RCA",		"RM4100", },
+-	{ "Shuttle",            "FD37", },
+-	{ "Sun",		"Blade x6250", },
+-	{ "Supermicro",		"H8QC8", },
+-	{ "Supermicro",		"X8DTT-F", },
+-	{ "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",		"S5382 (Tempest i5000PW)", },
+-	{ "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",		"PC3500G", },
+-	{ "VIA",		"VB700X", },
++#if CONFIG_FT2232_SPI+CONFIG_DEDIPROG >= 1
++		printf("\nSupported USB devices flashrom can use "
++		       "as programmer:\n\n");
++#endif
+ 
+-	{},
+-};
++#if CONFIG_FT2232_SPI == 1
++		print_supported_usbdevs(devs_ft2232spi);
++#endif
++}
+ 
+-/* 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",		"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", },
++#if CONFIG_INTERNAL == 1
+ 
+-	{},
+-};
++#ifdef CONFIG_PRINT_WIKI
++#define B(vendor, name, status, url, note) { vendor, name, status, url, note }
++#else
++#define B(vendor, name, status, url, note) { vendor, name, status }
++#endif
+ 
+ /* Please keep this list alphabetically ordered by vendor/board. */
+-const struct board_info laptops_ok[] = {
+-	/* Verified working laptops. */
+-	{ "Lenovo",		"3000 V100 TF05Cxx", },
+-	{ "Acer",               "Aspire 1520", },
++const struct board_info boards_known[] = {
++#if defined(__i386__) || defined(__x86_64__)
++	B("A-Trend",	"ATC-6220",		1, "http://www.motherboard.cz/mb/atrend/atc6220.htm", NULL),
++	B("abit",	"AN-M2",		1, "http://www.abit.com.tw/page/de/motherboard/motherboard_detail.php?DEFTITLE=Y&fMTYPE=Socket%20AM2&pMODEL_NAME=AN-M2", NULL),
++	B("abit",	"AX8",			1, "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?DEFTITLE=Y&fMTYPE=Socket%20939&pMODEL_NAME=AX8", NULL),
++	B("abit",	"Fatal1ty F-I90HD",	1, "http://www.abit.com.tw/page/de/motherboard/motherboard_detail.php?pMODEL_NAME=Fatal1ty+F-I90HD&fMTYPE=LGA775", NULL),
++	B("abit",	"IC7",			1, "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?pMODEL_NAME=IC7&fMTYPE=Socket%20478", NULL),
++	B("abit",	"IP35",			1, "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?fMTYPE=LGA775&pMODEL_NAME=IP35", NULL),
++	B("abit",	"IP35 Pro",		1, "http://www.abit.com.tw/page/de/motherboard/motherboard_detail.php?fMTYPE=LGA775&pMODEL_NAME=IP35%20Pro", NULL),
++	B("abit",	"IS-10",		0, "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?pMODEL_NAME=IS-10&fMTYPE=Socket+478", "Reported by deejkuba at aol.com to flashrom at coreboot.org, no public archive. Missing board enable and/or M50FW040 unlocking. May work now."),
++	B("abit",	"KN8 Ultra",		1, "http://www.abit.com.tw/page/de/motherboard/motherboard_detail.php?DEFTITLE=Y&fMTYPE=Socket%20939&pMODEL_NAME=KN8%20Ultra", NULL),
++	B("abit",	"NF-M2 nView",		1, "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?fMTYPE=Socket%20AM2&pMODEL_NAME=NF-M2%20nView", NULL),
++	B("abit",	"NF7-S",		1, "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?fMTYPE=Socket%20A&pMODEL_NAME=NF7-S", NULL),
++	B("abit",	"VA6",			1, "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?fMTYPE=Slot%201&pMODEL_NAME=VA6", NULL),
++	B("abit",	"VT6X4",		1, "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?fMTYPE=Slot%201&pMODEL_NAME=VT6X4", NULL),
++	B("Acorp",	"6A815EPD",		1, "http://web.archive.org/web/20021206163652/www.acorp.com.tw/English/default.asp", NULL),
++	B("Advantech",	"PCM-5820",		1, "http://www.emacinc.com/sbc_pc_compatible/pcm_5820.htm", NULL),
++	B("agami",	"Aruma",		1, "http://web.archive.org/web/20080212111524/http://www.agami.com/site/ais-6000-series", NULL),
++	B("Albatron",	"PM266A Pro",		1, "http://www.albatron.com.tw/English/Product/MB/pro_detail.asp?rlink=Overview&no=56", NULL), /* FIXME */
++	B("AOpen",	"vKM400Am-S",		1, "http://usa.aopen.com/products_detail.aspx?Auno=824", NULL),
++	B("Artec Group","DBE61",		1, "http://wiki.thincan.org/DBE61", NULL),
++	B("Artec Group","DBE62",		1, "http://wiki.thincan.org/DBE62", NULL),
++	B("ASI",	"MB-5BLMP",		1, "http://www.hojerteknik.com/winnet.htm", "Used in the IGEL WinNET III thin client."),
++	B("ASRock",	"775i65G",		1, "http://www.asrock.com/mb/overview.asp?Model=775i65G", NULL),
++	B("ASRock",	"939A785GMH/128M",	1, "http://www.asrock.com/mb/overview.asp?Model=939A785GMH/128M&s=939", NULL),
++	B("ASRock",	"A330GC",		1, "http://www.asrock.com/mb/overview.asp?Model=A330GC", NULL),
++	B("ASRock",	"A770CrossFire",	1, "http://www.asrock.com/mb/overview.asp?Model=A770CrossFire&s=AM2\%2b", NULL),
++	B("ASRock",	"ALiveNF6G-DVI",	1, "http://www.asrock.com/mb/overview.asp?Model=ALiveNF6G-DVI", NULL),
++	B("ASRock",	"K7VT4A+",		0, "http://www.asrock.com/mb/overview.asp?Model=K7VT4A%%2b&s=", "No chip found, probably due to flash translation. http://www.flashrom.org/pipermail/flashrom/2009-August/000393.html"),
++	B("ASRock",	"K8S8X",		1, "http://www.asrock.com/mb/overview.asp?Model=K8S8X", NULL),
++	B("ASRock",	"M3A790GXH/128M",	1, "http://www.asrock.com/MB/overview.asp?Model=M3A790GXH/128M", NULL),
++	B("ASRock",	"P4i65GV",		1, NULL, NULL),
++	B("ASUS",	"A7N8X Deluxe",		1, "http://www.asus.com/product.aspx?P_ID=wAsRYm41KTp78MFC", NULL),
++	B("ASUS",	"A7N8X-E Deluxe",	1, "http://www.asus.com/product.aspx?P_ID=TmQtPJv4jIxmL9C2", NULL),
++	B("ASUS",	"A7V133",		1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/socka/kt133a/a7v133/", NULL),
++	B("ASUS",	"A7V400-MX",		1, "http://www.asus.com/product.aspx?P_ID=hORgEHRBDLMfwAwx", NULL),
++	B("ASUS",	"A7V600-X",		1, "http://www.asus.com/product.aspx?P_ID=L2XYS0rmtCjeOr4k", NULL),
++	B("ASUS",	"A7V8X",		1, "http://www.asus.com/product.aspx?P_ID=qfpaGrAy2kLVo0f2", NULL),
++	B("ASUS",	"A7V8X-MX",		1, "http://www.asus.com/product.aspx?P_ID=SEJOOYqfuQPitx2H", NULL),
++	B("ASUS",	"A7V8X-MX SE",		1, "http://www.asus.com/product.aspx?P_ID=1guVBT1qV5oqhHyZ", NULL),
++	B("ASUS",	"A7V8X-X",		1, "http://www.asus.com/product.aspx?P_ID=YcXfRrWHZ9RKoVmw", NULL),
++	B("ASUS",	"A8Jm",			1, "http://www.asus.com/product.aspx?P_ID=VztICtOgiU6drx4m", NULL),
++	B("ASUS",	"A8N",			1, NULL, NULL), /* TODO: This should probably be A8N-SLI Deluxe, see http://www.coreboot.org/pipermail/flashrom/2009-November/000878.html */
++	B("ASUS",	"A8N-E",		1, "http://www.asus.com/product.aspx?P_ID=DzbA8hgqchMBOVRz", NULL),
++	B("ASUS",	"A8N-LA (Nagami-GL8E)",	1, "http://h10025.www1.hp.com/ewfrf/wc/document?lc=en&cc=us&docname=c00647121&dlc=en", "This is an OEM board from HP, the HP name is Nagami-GL8E."),
++	B("ASUS",	"A8N-SLI",		1, "http://www.asus.com/product.aspx?P_ID=J9FKa8z2xVId3pDK", NULL),
++	B("ASUS",	"A8N-SLI Premium",	1, "http://www.asus.com/product.aspx?P_ID=nbulqxniNmzf0mH1", NULL),
++	B("ASUS",	"A8N-VM CSM",		1, "http://www.asus.com/product.aspx?P_ID=JBqqlpj4cspbSa3s", NULL),
++	B("ASUS",	"A8NE-FM/S",		1, "http://www.hardwareschotte.de/hardware/preise/proid_1266090/preis_ASUS+A8NE-FM", NULL),
++	B("ASUS",	"A8V Deluxe",		1, "http://www.asus.com/product.aspx?P_ID=tvpdgPNCPaABZRVU", NULL),
++	B("ASUS",	"A8V-E Deluxe",		1, "http://www.asus.com/product.aspx?P_ID=hQBPIJWEZnnGAZEh", NULL),
++	B("ASUS",	"A8V-E SE",		1, "http://www.asus.com/product.aspx?P_ID=VMfiJJRYTHM4gXIi", "See http://www.coreboot.org/pipermail/coreboot/2007-October/026496.html"),
++	B("ASUS",	"K8V",			1, "http://www.asus.com/product.aspx?P_ID=fG2KZOWF7v6MRFRm", NULL),
++	B("ASUS",	"K8V SE Deluxe",	1, "http://www.asus.com/product.aspx?P_ID=65HeDI8XM1u6Uy6o", NULL),
++	B("ASUS",	"K8V-X SE",		1, "http://www.asus.com/product.aspx?P_ID=lzDXlbBVHkdckHVr", NULL),
++	B("ASUS",	"M2A-MX",		1, "http://www.asus.com/product.aspx?P_ID=BmaOnPewi1JgltOZ", NULL),
++	B("ASUS",	"M2A-VM",		1, "http://www.asus.com/product.aspx?P_ID=St3pWpym8xXpROQS", "See http://www.coreboot.org/pipermail/coreboot/2007-September/025281.html"),
++	B("ASUS",	"M2N32-SLI Deluxe",	1, "http://www.asus.com/product.aspx?P_ID=0jMy2X8lKstYRvev", NULL),
++	B("ASUS",	"M2N-E",		1, "http://www.asus.com/product.aspx?P_ID=NFlvt10av3F7ayQ9", "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"),
++	B("ASUS",	"M2N-SLI Deluxe",	1, "http://www.asus.com/product.aspx?P_ID=szSFtrap7crpBaQE", NULL),
++	B("ASUS",	"M2NBP-VM CSM",		1, "http://www.asus.com/product.aspx?P_ID=MnOfzTGd2KkwG0rF", NULL),
++	B("ASUS",	"M2NPV-VM",		1, "http://www.asus.com/product.aspx?P_ID=HGTVnGv5nGahCYgK", NULL),
++	B("ASUS",	"M2V",			1, "http://www.asus.com/product.aspx?P_ID=OqYlEDFfF6ZqZGvp", NULL),
++	B("ASUS",	"M2V-MX",		1, "http://www.asus.com/product.aspx?P_ID=7grf8Ci4yxnqzt3z", NULL),
++	B("ASUS",	"M3A76-CM",		1, NULL, NULL),
++	B("ASUS",	"M3A78-EM",		1, "http://www.asus.com/product.aspx?P_ID=KjpYqzmAd9vsTM2D", NULL),
++	B("ASUS",	"M4A785TD-M EVO",	1, "http://www.asus.com/product.aspx?P_ID=QHbvGVB1mXmmD8qQ", NULL),
++	B("ASUS",	"M4A79T Deluxe",	1, "http://www.asus.com/product.aspx?P_ID=lhJiLTN5huPfCVkW", NULL),
++	B("ASUS",	"M4A87TD/USB3",		1, "http://www.asus.com/product.aspx?P_ID=nlWYrI9wlNIYHAaa", NULL),
++	B("ASUS",	"MEW-AM",		0, "ftp://ftp.asus.com.tw/pub/ASUS/mb/sock370/810/mew-am/", "No public report found. Owned by Uwe Hermann <uwe at hermann-uwe.de>. May work now."),
++	B("ASUS",	"MEW-VM",		0, "http://www.elhvb.com/mboards/OEM/HP/manual/ASUS%20MEW-VM.htm", "No public report found. Owned by Uwe Hermann <uwe at hermann-uwe.de>. May work now."),
++	B("ASUS",	"P2B",			1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p2b/", NULL),
++	B("ASUS",	"P2B-D",		1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p2b-d/", NULL),
++	B("ASUS",	"P2B-DS",		1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p2b-ds/", NULL),
++	B("ASUS",	"P2B-F",		1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p2b-d/", NULL),
++	B("ASUS",	"P2B-N",		1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p2b-n/", NULL),
++	B("ASUS",	"P2E-M",		1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440ex/p2e-m/", NULL),
++	B("ASUS",	"P2L97-S",		1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440lx/p2l97-s/", NULL),
++	B("ASUS",	"P3B-F",		0, "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p3b-f/", "No public report found. Owned by Uwe Hermann <uwe at hermann-uwe.de>. May work now."),
++	B("ASUS",	"P4B266",		1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/sock478/p4b266/", NULL),
++	B("ASUS",	"P4B266-LM",		1, "http://esupport.sony.com/US/perl/swu-list.pl?mdl=PCVRX650", NULL),
++	B("ASUS",	"P4B533-E",		1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/sock478/p4b533-e/", NULL),
++	B("ASUS",	"P4C800-E Deluxe",	1, "http://www.asus.com/product.aspx?P_ID=cFuVCr9bXXCckmcK", NULL),
++	B("ASUS",	"P4P800",		1, "http://www.asus.com/product.aspx?P_ID=DYt1Et9MlBChqzLb", NULL),
++	B("ASUS",	"P4P800-E Deluxe",	1, "http://www.asus.com/product.aspx?P_ID=INIJUvLlif7LHp3g", NULL),
++	B("ASUS",	"P4SD-LA",		1, "http://h10025.www1.hp.com/ewfrf/wc/document?docname=c00022505", NULL),
++	B("ASUS",	"P4S800-MX",		1, "http://www.asus.com/product.aspx?P_ID=Bb57zoJhmO1Qkcrh", NULL),
++	B("ASUS",	"P5A",			1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/sock7/ali/p5a/", NULL),
++	B("ASUS",	"P5B",			1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/socket775/P5B/", NULL),
++	B("ASUS",	"P5B-Deluxe",		1, "http://www.asus.com/product.aspx?P_ID=bswT66IBSb2rEWNa", NULL),
++	B("ASUS",	"P5BV-M",		0, "ftp://ftp.asus.com.tw/pub/ASUS/mb/socket775/P5B-VM/", "Reported by Bernhard M. Wiedemann <bernhard at uml12d.zq1.de> to flashrom at coreboot.org, no public archive. Missing board enable and/or SST49LF008A unlocking. May work now."),
++	B("ASUS",	"P5GC-MX/1333",		1, "http://www.asus.com/product.aspx?P_ID=PYvbfOokwxUzJky3", NULL),
++	B("ASUS",	"P5KC",			1, "http://www.asus.com/product.aspx?P_ID=fFZ8oUIGmLpwNMjj", NULL),
++	B("ASUS",	"P5L-MX",		1, "http://www.asus.com/product.aspx?P_ID=X70d3NCzH2DE9vWH", NULL),
++	B("ASUS",	"P5GD1 Pro",		1, "http://www.asus.com/product.aspx?P_ID=50M49xQh71EZOeM1", NULL),
++	B("ASUS",	"P5ND2-SLI Deluxe",	1, "http://www.asus.com/product.aspx?P_ID=WY7XroDuUImVbgp5", NULL),
++	B("ASUS",	"P5PE-VM",		1, "http://www.asus.com/product.aspx?P_ID=k3h0ZFVu9Lo1dUvk", NULL),
++	B("ASUS",	"P6T SE",		1, "http://www.asus.com/product.aspx?P_ID=t4yhK6y9W9o7iQ9E", NULL),
++	B("ASUS",	"P6T Deluxe",		1, "http://www.asus.com/product.aspx?P_ID=vXixf82co6Q5v0BZ", NULL),
++	B("ASUS",	"P6T Deluxe V2",	1, "http://www.asus.com/product.aspx?P_ID=iRlP8RG9han6saZx", NULL),
++	B("BCOM",	"WinNET100",		1, "http://www.coreboot.org/BCOM_WINNET100", "Used in the IGEL-316 thin client."),
++	B("Biostar",	"M6TBA",		0, "ftp://ftp.biostar-usa.com/manuals/M6TBA/", "No public report found. Owned by Uwe Hermann <uwe at hermann-uwe.de>. May work now."),
++	B("Biostar",	"M7NCD Pro",		1, "http://www.biostar.com.tw/app/en/mb/content.php?S_ID=260", NULL),
++	B("Biostar",	"P4M80-M4",		1, "http://www.biostar-usa.com/mbdetails.asp?model=p4m80-m4", NULL),
++	B("Biostar",	"TA780G M2+",		1, "http://www.biostar.com.tw/app/en/t-series/content.php?S_ID=344", NULL),
++	B("Boser",	"HS-6637",		0, "http://www.boser.com.tw/manual/HS-62376637v3.4.pdf", "Reported by Mark Robinson <mark at zl2tod.net> to flashrom at coreboot.org, no public archive. Missing board enable and/or F29C51002T unlocking. May work now."),
++	B("Congatec",	"conga-X852",		1, "http://www.congatec.com/single_news+M57715f6263d.html?&L=1", NULL),
++	B("Dell",	"PowerEdge 1850",	1, "http://support.dell.com/support/edocs/systems/pe1850/en/index.htm", NULL),
++	B("DFI",	"855GME-MGF",		0, "http://www.dfi.com.tw/portal/CM/cmproduct/XX_cmproddetail/XX_WbProdsWindow?action=e&downloadType=&windowstate=normal&mode=view&downloadFlag=false&itemId=433", "Probably needs a board enable. http://www.coreboot.org/pipermail/coreboot/2009-May/048549.html"),
++	B("DFI",	"Blood-Iron P35 T2RL",	1, "http://lp.lanparty.com.tw/portal/CM/cmproduct/XX_cmproddetail/XX_WbProdsWindow?itemId=516&downloadFlag=false&action=1", NULL),
++	B("Elitegroup",	"K7S5A",		1, "http://www.ecs.com.tw/ECSWebSite/Products/ProductsDetail.aspx?detailid=279&CategoryID=1&DetailName=Specification&MenuID=1&LanID=0", NULL),
++	B("Elitegroup",	"K7S6A",		1, "http://www.ecs.com.tw/ECSWebSite/Products/ProductsDetail.aspx?detailid=77&CategoryID=1&DetailName=Specification&MenuID=52&LanID=0", NULL),
++	B("Elitegroup",	"K7VTA3",		1, "http://www.ecs.com.tw/ECSWebSite/Products/ProductsDetail.aspx?detailid=264&CategoryID=1&DetailName=Specification&MenuID=52&LanID=0", NULL),
++	B("Elitegroup",	"P6IWP-Fe",		1, "http://www.ecs.com.tw/ECSWebSite_2007/Products/ProductsDetail.aspx?CategoryID=1&TypeID=3&DetailID=95&DetailName=Feature&MenuID=1&LanID=0", NULL),
++	B("Elitegroup",	"P6VAP-A+",		1, "http://www.ecs.com.tw/ECSWebSite/Products/ProductsDetail.aspx?detailid=117&CategoryID=1&DetailName=Specification&MenuID=1&LanID=0", NULL),
++	B("Elitegroup", "RS485M-M",		1, "http://www.ecs.com.tw/ECSWebSite_2007/Products/ProductsDetail.aspx?CategoryID=1&DetailID=654&DetailName=Feature&MenuID=1&LanID=0", NULL),
++	B("Emerson",	"ATCA-7360",		1, "http://www.emerson.com/sites/Network_Power/en-US/Products/Product_Detail/Product1/Pages/EmbCompATCA-7360.aspx", NULL),
++	B("EPoX",	"EP-8K5A2",		1, "http://www.epox.com/product.asp?ID=EP-8K5A2", NULL),
++	B("EPoX",	"EP-8RDA3+",		1, "http://www.epox.com/product.asp?ID=EP-8RDA3plus", NULL),
++	B("EPoX",	"EP-BX3",		1, "http://www.epox.com/product.asp?ID=EP-BX3", NULL),
++	B("FIC",	"VA-502",		0, "ftp://ftp.fic.com.tw/motherboard/manual/socket7/va-502/", "No public report found. Owned by Uwe Hermann <uwe at hermann-uwe.de>. Seems the PCI subsystem IDs are identical with the Tekram P6Pro-A5. May work now."),
++	B("Foxconn",	"A6VMX",		1, "http://www.foxconnchannel.com/product/motherboards/detail_overview.aspx?id=en-us0000346", NULL),
++	B("Fujitsu-Siemens", "ESPRIMO P5915",	1, "http://uk.ts.fujitsu.com/rl/servicesupport/techsupport/professionalpc/ESPRIMO/P/EsprimoP5915-6.htm", "Mainboard model is D2312-A2."),
++	B("GIGABYTE",	"GA-2761GXDK",		1, "http://www.computerbase.de/news/hardware/mainboards/amd-systeme/2007/mai/gigabyte_dtx-mainboard/", NULL),
++	B("GIGABYTE",	"GA-6BXC",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=1445", NULL),
++	B("GIGABYTE",	"GA-6BXDU",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=1429", NULL),
++	B("GIGABYTE",	"GA-6ZMA",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=1541", NULL),
++	B("GIGABYTE",	"GA-MA785GMT-UD2H",	1, "http://www.gigabyte.de/Products/Motherboard/Products_Overview.aspx?ProductID=4525", NULL),
++	B("GIGABYTE",	"GA-770TA-UD3",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=3272#ov", NULL),
++	B("GIGABYTE",	"GA-7VT600",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=1666", NULL),
++	B("GIGABYTE",	"GA-7ZM",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=1366", "Works fine if you remove jumper JP9 on the board and disable the flash protection BIOS option."),
++	B("GIGABYTE",	"GA-8IRML",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=1343", NULL),
++	B("GIGABYTE",	"GA-8PE667 Ultra 2",	1, "http://www.gigabyte.com/products/product-page.aspx?pid=1607", NULL),
++	B("GIGABYTE",	"GA-965P-DS4",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=2288", NULL),
++	B("GIGABYTE",	"GA-EP35-DS3L",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=2778", NULL),
++	B("GIGABYTE",	"GA-EX58-UD4P",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=2986", NULL),
++	B("GIGABYTE",	"GA-K8N-SLI",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=1928", NULL),
++	B("GIGABYTE",	"GA-K8N51GMF-9",	1, "http://www.gigabyte.com/products/product-page.aspx?pid=1939", NULL),
++	B("GIGABYTE",	"GA-M57SLI-S4",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=2287", NULL),
++	B("GIGABYTE",	"GA-M61P-S3",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=2434", NULL),
++	B("GIGABYTE",	"GA-MA69VM-S2",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=2500", NULL),
++	B("GIGABYTE",	"GA-MA74GM-S2H (rev. 3.0)", 1, "http://www.gigabyte.com/products/product-page.aspx?pid=3152", NULL),
++	B("GIGABYTE",	"GA-MA770T-UD3P",	1, "http://www.gigabyte.com/products/product-page.aspx?pid=3096", NULL),
++	B("GIGABYTE",	"GA-MA78G-DS3H",	1, "http://www.gigabyte.com/products/product-page.aspx?pid=2800", NULL), /* TODO: Rev 1.x or 2.x? */
++	B("GIGABYTE",	"GA-MA78GM-S2H",	1, "http://www.gigabyte.com/products/product-page.aspx?pid=2758", NULL), /* TODO: Rev. 1.0, 1.1, or 2.x? */
++	B("GIGABYTE",	"GA-MA78GPM-DS2H",	1, "http://www.gigabyte.com/products/product-page.aspx?pid=2859", NULL),
++	B("GIGABYTE",	"GA-MA790FX-DQ6",	1, "http://www.gigabyte.com/products/product-page.aspx?pid=2690", NULL),
++	B("GIGABYTE",	"GA-MA790GP-DS4H",	1, "http://www.gigabyte.com/products/product-page.aspx?pid=2887", NULL),
++	B("HP",		"ProLiant DL145 G3",	1, "http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c00816835&lang=en&cc=us&taskId=101&prodSeriesId=3219755&prodTypeId=15351", NULL),
++	B("HP",		"ProLiant DL165 G6",	1, "http://h10010.www1.hp.com/wwpc/us/en/sm/WF05a/15351-15351-3328412-241644-3328421-3955644.html", NULL),
++	B("HP",		"Puffer2-UL8E",		1, "http://h10025.www1.hp.com/ewfrf/wc/document?docname=c00300023", NULL),
++	B("HP",		"Vectra VL400",		1, "http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c00060658&lang=en&cc=us", NULL),
++	B("HP",		"Vectra VL420 SFF",	1, "http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c00060661&lang=en&cc=us", NULL),
++	B("HP",		"xw9400",		1, "http://h20000.www2.hp.com/bizsupport/TechSupport/Home.jsp?lang=en&cc=us&prodSeriesId=3211286&prodTypeId=12454", "Boot block is write protected unless the solder points next to F2 are shorted." ),
++	B("IBASE",	"MB899",		1, "http://www.ibase-i.com.tw/2009/mb899.html", NULL),
++	B("IBM",	"x3455",		1, "http://www-03.ibm.com/systems/x/hardware/rack/x3455/index.html", NULL),
++	B("IEI",	"PICOe-9452",		1, "http://www.ieiworld.com/product_groups/industrial/content.aspx?keyword=WSB&gid=00001000010000000001&cid=08125380291060861658&id=08142308605814597144", NULL),
++	B("Intel",	"D201GLY",		1, "http://www.intel.com/support/motherboards/desktop/d201gly/index.htm", NULL),
++	B("Intel",	"EP80759",		1, NULL, NULL),
++	B("Intel",	"SE440BX-2",		0, "http://downloadcenter.intel.com/SearchResult.aspx?lang=eng&ProductFamily=Desktop+Boards&ProductLine=Discontinued+Motherboards&ProductProduct=Intel%C2%AE+SE440BX-2+Motherboard", "Probably won't work, see http://www.coreboot.org/pipermail/flashrom/2010-July/003952.html"),
++	B("IWILL",	"DK8-HTX",		1, "http://web.archive.org/web/20060507170150/http://www.iwill.net/product_2.asp?p_id=98", NULL),
++	B("Jetway",	"J7F4K1G5D-PB",		1, "http://www.jetway.com.tw/jetway/system/productshow2.asp?id=389&proname=J7F4K1G5D-P", NULL),
++	B("Kontron",	"986LCD-M",		1, "http://de.kontron.com/products/boards+and+mezzanines/embedded+motherboards/miniitx+motherboards/986lcdmmitx.html", NULL),
++	B("Lex",	"CV700A",		1, "http://www.lex.com.tw/product/CV700A-spec.htm", NULL),
++	B("Mitac",	"6513WU",		1, "http://web.archive.org/web/20050313054828/http://www.mitac.com/micweb/products/tyan/6513wu/6513wu.htm", NULL),
++	B("MSI",	"MS-6153",		1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=336", NULL),
++	B("MSI",	"MS-6156",		1, "http://uk.ts.fujitsu.com/rl/servicesupport/techsupport/boards/Motherboards/MicroStar/Ms6156/MS6156.htm", NULL),
++	B("MSI",	"MS-6178",		0, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=343", "Immediately powers off if you try to hot-plug the chip. However, this does '''not''' happen if you use coreboot. Owned by Uwe Hermann <uwe at hermann-uwe.de>."),
++	B("MSI",	"MS-6330 (K7T Turbo)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=327", NULL),
++	B("MSI",	"MS-6561 (745 Ultra)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=274", NULL),
++	B("MSI",	"MS-6570 (K7N2)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=519", NULL),
++	B("MSI",	"MS-6577 (Xenon)",	1, "http://h10025.www1.hp.com/ewfrf/wc/document?product=90390&lc=en&cc=us&dlc=en&docname=bph07843", "This is an OEM board from HP, the HP name is Xenon."),
++	B("MSI",	"MS-6590 (KT4 Ultra)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=502", NULL),
++	B("MSI",	"MS-6702E (K8T Neo2-F)",1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=588", NULL),
++	B("MSI",	"MS-6712 (KT4V)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=505", NULL),
++	B("MSI",	"MS-6787 (P4MAM-V/P4MAM-L)", 1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=577", NULL),
++	B("MSI",	"MS-7005 (651M-L)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=559", NULL),
++	B("MSI",	"MS-7025 (K8N Neo2 Platinum)", 1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=587", NULL),
++	B("MSI",	"MS-7046",		1, "http://www.heimir.de/ms7046/", NULL),
++	B("MSI",	"MS-7061 (KM4M-V/KM4AM-V)", 1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=594", NULL),
++	B("MSI",	"MS-7065",		1, "http://browse.geekbench.ca/geekbench2/view/53114", NULL),
++	B("MSI",	"MS-7135 (K8N Neo3)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=170", NULL),
++	B("MSI",	"MS-7168 (Orion)",	1, "http://support.packardbell.co.uk/uk/item/index.php?i=spec_orion&pi=platform_honeymoon_istart", NULL),
++	B("MSI",	"MS-7207 (K8NGM2-L)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=224", NULL),
++	B("MSI",	"MS-7236 (945PL Neo3)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1173", NULL),
++	B("MSI",	"MS-7253 (K9VGM-V)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=260", NULL),
++	B("MSI",	"MS-7255 (P4M890M)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1082", NULL),
++	B("MSI",	"MS-7260, (K9N Neo)",	0, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=255", "Interestingly flashrom does not work when the vendor BIOS is booted, but it ''does'' work flawlessly when the machine is booted with coreboot. Owned by Uwe Hermann <uwe at hermann-uwe.de>."),
++	B("MSI",	"MS-7312 (K9MM-V)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1104", NULL),
++	B("MSI",	"MS-7345 (P35 Neo2-FIR)", 1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1261", NULL),
++	B("MSI",	"MS-7368 (K9AG Neo2-Digital)", 1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1241", NULL),
++	B("MSI",	"MS-7376 (K9A2 Platinum)", 1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1332", NULL),
++	B("MSI",	"MS-7642 (890GXM-G65)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=2012", NULL),
++	B("NEC",	"PowerMate 2000",	1, "http://support.necam.com/mobilesolutions/hardware/Desktops/pm2000/celeron/", NULL),
++	B("Nokia",	"IP530",		1, NULL, NULL),
++	B("PC Engines",	"Alix.1c",		1, "http://pcengines.ch/alix1c.htm", NULL),
++	B("PC Engines",	"Alix.2c2",		1, "http://pcengines.ch/alix2c2.htm", NULL),
++	B("PC Engines",	"Alix.2c3",		1, "http://pcengines.ch/alix2c3.htm", NULL),
++	B("PC Engines",	"Alix.3c3",		1, "http://pcengines.ch/alix3c3.htm", NULL),
++	B("PC Engines",	"Alix.3d3",		1, "http://pcengines.ch/alix3d3.htm", NULL),
++	B("PC Engines",	"WRAP.2E",		1, "http://pcengines.ch/wrap2e1.htm", NULL),
++	B("Portwell",	"PEB-4700VLA",		1, "http://www.portwell.com/products/detail.asp?CUSTCHAR1=PEB-4700VLA", NULL),
++	B("RCA",	"RM4100",		1, "http://www.settoplinux.org/index.php?title=RCA_RM4100", NULL),
++	B("Samsung",	"Polaris 32",		1, NULL, NULL),
++	B("Shuttle",	"AK31",			1, "http://www.motherboard.cz/mb/shuttle/AK31.htm", NULL),
++	B("Shuttle",	"AK38N",		1, "http://eu.shuttle.com/en/desktopdefault.aspx/tabid-36/558_read-9889/", NULL),
++	B("Shuttle",	"AV11V30",		1, NULL, NULL),
++	B("Shuttle",	"FD37",			1, "http://www.shuttle.eu/products/discontinued/barebones/sd37p2/", NULL),
++	B("Shuttle",	"FN25",			1, "http://www.shuttle.eu/products/discontinued/barebones/sn25p/?0=", NULL),
++	B("Shuttle",	"X50/X50(B)",		1, "http://au.shuttle.com/product_detail_spec.jsp?PI=1241", NULL),
++	B("Soyo",	"SY-5VD",		0, "http://www.soyo.com/content/Downloads/163/&c=80&p=464&l=English", "No public report found. Owned by Uwe Hermann <uwe at hermann-uwe.de>. May work now."),
++	B("Soyo",	"SY-6BA+ III",		1, "http://www.motherboard.cz/mb/soyo/SY-6BA+III.htm", NULL),
++	B("Soyo",	"SY-7VCA",		1, "http://www.tomshardware.com/reviews/12-socket-370-motherboards,196-15.html", NULL),
++	B("Sun",	"Blade x6250",		1, "http://www.sun.com/servers/blades/x6250/", NULL),
++	B("Sun",	"Fire x4150",		0, "http://www.sun.com/servers/x64/x4150/", "No public report found. May work now."),
++	B("Sun",	"Fire x4200",		0, "http://www.sun.com/servers/entry/x4200/", "No public report found. May work now."),
++	B("Sun",	"Fire x4540",		0, "http://www.sun.com/servers/x64/x4540/", "No public report found. May work now."),
++	B("Sun",	"Fire x4600",		0, "http://www.sun.com/servers/x64/x4600/", "No public report found. May work now."),
++	B("Supermicro",	"H8QC8",		1, "http://www.supermicro.com/Aplus/motherboard/Opteron/nforce/H8QC8.cfm", NULL),
++	B("Supermicro",	"X8DTT-F",		1, "http://www.supermicro.com/products/motherboard/QPI/5500/X8DTT-F.cfm", NULL),
++	B("T-Online",	"S-100",		1, "http://wiki.freifunk-hannover.de/T-Online_S_100", NULL),
++	B("Tekram",	"P6Pro-A5",		1, "http://www.motherboard.cz/mb/tekram/P6Pro-A5.htm", NULL),
++	B("Termtek",	"TK-3370 (Rev:2.5B)",	1, NULL, NULL),
++	B("Thomson",	"IP1000",		1, "http://www.settoplinux.org/index.php?title=Thomson_IP1000", NULL),
++	B("TriGem",	"Lomita",		1, "http://www.e4allupgraders.info/dir1/motherboards/socket370/lomita.shtml", NULL),
++	B("Tyan",	"S5375-1U (Tempest i5100X)", 1, "http://www.tyan.com/product_board_detail.aspx?pid=610", NULL),
++	B("Tyan",	"S1846 (Tsunami ATX)",	1, "http://www.tyan.com/archive/products/html/tsunamiatx.html", NULL),
++	B("Tyan",	"S2466 (Tiger MPX)",	1, "http://www.tyan.com/product_board_detail.aspx?pid=461", NULL),
++	B("Tyan",	"S2498 (Tomcat K7M)",	1, "http://www.tyan.com/archive/products/html/tomcatk7m.html", NULL),
++	B("Tyan",	"S2881 (Thunder K8SR)",	1, "http://www.tyan.com/product_board_detail.aspx?pid=115", NULL),
++	B("Tyan",	"S2882 (Thunder K8S Pro)", 1, "http://www.tyan.com/product_board_detail.aspx?pid=121", NULL),
++	B("Tyan",	"S2882-D (Thunder K8SD Pro)", 1, "http://www.tyan.com/product_board_detail.aspx?pid=127", NULL),
++	B("Tyan",	"S2891 (Thunder K8SRE)", 1, "http://www.tyan.com/product_board_detail.aspx?pid=144", NULL),
++	B("Tyan",	"S2892 (Thunder K8SE)",	1, "http://www.tyan.com/product_board_detail.aspx?pid=145", NULL),
++	B("Tyan",	"S2895 (Thunder K8WE)",	1, "http://www.tyan.com/archive/products/html/thunderk8we.html", NULL),
++	B("Tyan",	"S2915 (Thunder n6650W)", 1, "http://tyan.com/product_board_detail.aspx?pid=163", NULL),
++	B("Tyan",	"S2933 (Thunder n3600S)", 1, "http://tyan.com/product_SKU_spec.aspx?ProductType=MB&pid=478&SKU=600000063", NULL),
++	B("Tyan",	"S3095 (Tomcat i945GM)", 1, "http://www.tyan.com/product_board_detail.aspx?pid=181", NULL),
++	B("Tyan",	"S3992 (Thunder h2000M)", 1, "http://tyan.com/product_board_detail.aspx?pid=235", NULL),
++	B("Tyan",	"S5180 (Toledo i965R)",	1, "http://www.tyan.com/product_board_detail.aspx?pid=456", NULL),
++	B("Tyan",	"S5191 (Toledo i3000R)", 1, "http://www.tyan.com/product_board_detail.aspx?pid=343", NULL),
++	B("Tyan",	"S5197 (Toledo i3010W)", 1, "http://www.tyan.com/product_board_detail.aspx?pid=349", NULL),
++	B("Tyan",	"S5211 (Toledo i3210W)", 1, "http://www.tyan.com/product_board_detail.aspx?pid=591", NULL),
++	B("Tyan",	"S5211-1U (Toledo i3200R)", 1, "http://www.tyan.com/product_board_detail.aspx?pid=593", NULL),
++	B("Tyan",	"S5220 (Toledo q35T)",	1, "http://www.tyan.com/product_board_detail.aspx?pid=597", NULL),
++	B("Tyan",	"S5375 (Tempest i5100X)", 1, "http://www.tyan.com/product_board_detail.aspx?pid=566", NULL),
++	B("Tyan",	"S5376 (Tempest i5100W)", 1, "http://www.tyan.com/product_board_detail.aspx?pid=605", "Both S5376G2NR and S5376WAG2NR should work."),
++	B("Tyan",	"S5377 (Tempest i5100T)", 1, "http://www.tyan.com/product_SKU_spec.aspx?ProductType=MB&pid=642&SKU=600000017", NULL),
++	B("Tyan",	"S5382 (Tempest i5000PW)", 1, "http://www.tyan.com/product_board_detail.aspx?pid=439", NULL),
++	B("Tyan",	"S5397 (Tempest i5400PW)", 1, "http://www.tyan.com/product_board_detail.aspx?pid=560", NULL),
++	B("VIA",	"EPIA M/MII/...",	1, "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=202", NULL), /* EPIA-MII link for now */
++	B("VIA",	"EPIA SP",		1, "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=261", NULL),
++	B("VIA",	"EPIA-CN",		1, "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=400", NULL),
++	B("VIA",	"EPIA EK",		1, "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?motherboard_id=420", NULL),
++	B("VIA",	"EPIA-EX15000G",	1, "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=450", NULL),
++	B("VIA",	"EPIA-LN",		1, "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=473", NULL),
++	B("VIA",	"EPIA-M700",		1, "http://via.com.tw/servlet/downloadSvl?motherboard_id=670&download_file_id=3700", NULL),
++	B("VIA",	"EPIA-N/NL",		1, "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=221", NULL), /* EPIA-N link for now */
++	B("VIA",	"EPIA-NX15000G",	1, "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=470", NULL),
++	B("VIA",	"NAB74X0",		1, "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=590", NULL),
++	B("VIA",	"pc2500e",		1, "http://www.via.com.tw/en/initiatives/empowered/pc2500_mainboard/index.jsp", NULL),
++	B("VIA",	"PC3500G",		1, "http://www.via.com.tw/en/initiatives/empowered/pc3500_mainboard/index.jsp", NULL),
++	B("VIA",	"VB700X",		1, "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=490", NULL),
++	B("ZOTAC",	"ZBOX HD-ID11",		1, "http://pdde.zotac.com/index.php?page=shop.product_details&product_id=240&category_id=75", NULL),
++#endif
+ 
+ 	{},
+ };
+ 
+ /* 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", },
++const struct board_info laptops_known[] = {
++#if defined(__i386__) || defined(__x86_64__)
++	B("Acer",	"Aspire 1520",		1, "http://support.acer.com/us/en/acerpanam/notebook/0000/Acer/Aspire1520/Aspire1520nv.shtml", NULL),
++	B("Acer",	"Aspire One",		0, NULL, "http://www.coreboot.org/pipermail/coreboot/2009-May/048041.html"),
++	B("ASUS",	"Eee PC 701 4G",	0, "http://www.asus.com/product.aspx?P_ID=h6SPd3tEzLEsrEiS", "It seems the chip (25X40VSIG) is behind some SPI flash translation layer (likely in the EC, the ENE KB3310)."),
++	B("Dell",	"Latitude CPi A366XT",	0, "http://www.coreboot.org/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."),
++	B("HP/Compaq",	"nx9005",		0, "http://h18000.www1.hp.com/products/quickspecs/11602_na/11602_na.HTML", "Shuts down when probing for a chip. http://www.flashrom.org/pipermail/flashrom/2010-May/003321.html"),
++	B("HP/Compaq",	"nx9010",		0, "http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?lang=en&cc=us&objectID=c00348514", "Hangs upon '''flashrom -V''' (needs hard power-cycle then)."),
++	B("IBM/Lenovo",	"Thinkpad T40p",	0, "http://www.thinkwiki.org/wiki/Category:T40p", NULL),
++	B("IBM/Lenovo",	"240",			0, "http://www.stanford.edu/~bresnan//tp240.html", "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."),
++	B("Lenovo",	"3000 V100 TF05Cxx",	1, "http://www5.pc.ibm.com/europe/products.nsf/products?openagent&brand=Lenovo3000Notebook&series=Lenovo+3000+V+Series#viewallmodelstop", NULL),
++#endif
+ 
+ 	{},
+ };
+ #endif
+-
+diff --git a/print_wiki.c b/print_wiki.c
+index 38505ed..313fdea 100644
+--- a/print_wiki.c
++++ b/print_wiki.c
+@@ -19,49 +19,32 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
++#include <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+ #include <time.h>
+ #include "flash.h"
+ #include "flashchips.h"
++#include "programmer.h"
+ 
+-#if INTERNAL_SUPPORT == 1
+-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;
+-};
+-#endif
+-
+-const char *wiki_header = "= Supported devices =\n\n\
++static const char * const wiki_header = "= Supported devices =\n\n\
+ <div style=\"margin-top:0.5em; padding:0.5em 0.5em 0.5em 0.5em; \
+ background-color:#eeeeee; align:right; border:1px solid #aabbcc;\"><small>\n\
+ Please do '''not''' edit these tables in the wiki directly, they are \
+ generated by pasting '''flashrom -z''' output.<br />\
+ '''Last update:''' %s(generated by flashrom %s)\n</small></div>\n";
+ 
+-#if INTERNAL_SUPPORT == 1
+-const char *chipset_th = "{| border=\"0\" style=\"font-size: smaller\"\n\
++#if CONFIG_INTERNAL == 1
++static const char * const 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\" \
++static const char * const 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";
++! align=\"left\" | Mainboard\n! align=\"left\" | Required option\n! align=\"left\" | Status\n\n";
+ 
+-const char *board_intro = "\
++static const char * const 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 \
+@@ -74,14 +57,14 @@ know, someone has to give it a try). Please report any further verified \
+ mainboards on the [[Mailinglist|mailing list]].\n";
+ #endif
+ 
+-const char *chip_th = "{| border=\"0\" style=\"font-size: smaller\" \
++static const char * const 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\" | &nbsp;\n\
+ | Probe\n| Read\n| Erase\n| Write\n\n";
+ 
+-const char *programmer_section = "\
++static const char * const 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: \
+@@ -89,8 +72,8 @@ 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";
+ 
+-#if INTERNAL_SUPPORT == 1
+-const char *laptop_intro = "\n== Supported laptops/notebooks ==\n\n\
++#if CONFIG_INTERNAL == 1
++static const char * const 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\
+@@ -101,277 +84,7 @@ background-color:#ff6666; align:right; border:1px solid #000000;\">\n\
+ 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</div>\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/product.aspx?P_ID=TmQtPJv4jIxmL9C2" },
+-	{ "ASUS",		"A7V133",		"ftp://ftp.asus.com.tw/pub/ASUS/mb/socka/kt133a/a7v133/" },
+-	{ "ASUS",		"A7V400-MX",		"http://www.asus.com/product.aspx?P_ID=hORgEHRBDLMfwAwx" },
+-	{ "ASUS",		"A7V8X-MX",		"http://www.asus.com/product.aspx?P_ID=SEJOOYqfuQPitx2H" },
+-	{ "ASUS",		"A8N-E",		"http://www.asus.com/product.aspx?P_ID=DzbA8hgqchMBOVRz" },
+-	{ "ASUS",		"A8NE-FM/S",		"http://www.hardwareschotte.de/hardware/preise/proid_1266090/preis_ASUS+A8NE-FM" },
+-	{ "ASUS",		"A8N-SLI",		"http://www.asus.com/product.aspx?P_ID=J9FKa8z2xVId3pDK" },
+-	{ "ASUS",		"A8N-SLI Premium",	"http://www.asus.com/product.aspx?P_ID=nbulqxniNmzf0mH1" },
+-	{ "ASUS",		"A8V Deluxe",		"http://www.asus.com/product.aspx?P_ID=tvpdgPNCPaABZRVU" },
+-	{ "ASUS",		"A8V-E Deluxe",		"http://www.asus.com/product.aspx?P_ID=hQBPIJWEZnnGAZEh" },
+-	{ "ASUS",		"A8V-E SE",		"http://www.asus.com/product.aspx?P_ID=VMfiJJRYTHM4gXIi" },
+-	{ "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://www.asus.com/product.aspx?P_ID=lzDXlbBVHkdckHVr" },
+-	{ "ASUS",		"M2A-MX",		"http://www.asus.com/product.aspx?P_ID=BmaOnPewi1JgltOZ" },
+-	{ "ASUS",		"M2A-VM",		"http://www.asus.com/product.aspx?P_ID=St3pWpym8xXpROQS" },
+-	{ "ASUS",		"M2N-E",		"http://www.asus.com/product.aspx?P_ID=NFlvt10av3F7ayQ9" },
+-	{ "ASUS",		"M2V",			"http://www.asus.com/product.aspx?P_ID=OqYlEDFfF6ZqZGvp" },
+-	{ "ASUS",		"M3A78-EM",		"http://www.asus.com/product.aspx?P_ID=KjpYqzmAd9vsTM2D" },
+-	{ "ASUS",		"P2B",			"ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p2b/" },
+-	{ "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",		"ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p2b-d/" },
+-	{ "ASUS",		"P2L97-S",		"ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440lx/p2l97-s/" },
+-	{ "ASUS",		"P5B",			"ftp://ftp.asus.com.tw/pub/ASUS/mb/socket775/P5B/" },
+-	{ "ASUS",		"P5B-Deluxe",		"http://www.asus.com/product.aspx?P_ID=bswT66IBSb2rEWNa" },
+-	{ "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",		"http://www.asus.com/product.aspx?P_ID=vXixf82co6Q5v0BZ" },
+-	{ "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?ProductID=1445" },
+-	{ "GIGABYTE",		"GA-6BXDU",		"http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=1429" },
+-	{ "GIGABYTE",		"GA-6ZMA",		"http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=1541" },
+-	{ "GIGABYTE",		"GA-965P-DS4",		"http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2288" },
+-	{ "GIGABYTE",		"GA-EX58-UD4P",		"http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2986" },
+-	{ "GIGABYTE",		"GA-EP35-DS3L",		"http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2778" },
+-	{ "GIGABYTE",		"GA-MA69VM-S2",		"http://www.gigabyte.com.tw/Products/Motherboard/Products_Spec.aspx?ProductID=2500" },
+-	{ "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&prod_no=336" },
+-	{ "MSI",		"MS-6156",		"http://uk.ts.fujitsu.com/rl/servicesupport/techsupport/boards/Motherboards/MicroStar/Ms6156/MS6156.htm" },
+-	{ "MSI",		"MS-6330 (K7T Turbo)",	"http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=327" },
+-	{ "MSI",		"MS-6570 (K7N2)",	"http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=519" },
+-	{ "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.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1082" },
+-	{ "MSI",		"MS-7345 (P35 Neo2-FIR)","http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1261" },
+-	{ "MSI",		"MS-7312 (K9MM-V)",	"http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1104" },
+-	{ "MSI",		"MS-7368 (K9AG Neo2-Digital)", "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1241" },
+-	{ "MSI",		"MS-7376 (K9A2 Platinum)", "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1332" },
+-	{ "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" },
+-	{ "PC Engines",		"WRAP.2E",		"http://pcengines.ch/wrap2e1.htm" },
+-	{ "RCA",		"RM4100",		"http://www.settoplinux.org/index.php?title=RCA_RM4100" },
+-	{ "Shuttle",		"FD37",			"http://www.shuttle.eu/products/discontinued/barebones/sd37p2/" },
+-	{ "Sun",		"Blade x6250",		"http://www.sun.com/servers/blades/x6250/" },
+-	{ "Supermicro",		"H8QC8",		"http://www.supermicro.com/Aplus/motherboard/Opteron/nforce/H8QC8.cfm" },
+-	{ "Supermicro",		"X8DTT-F",		"http://www.supermicro.com/products/motherboard/QPI/5500/X8DTT-F.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",		"S5382 (Tempest i5000PW)", "http://www.tyan.com/product_board_detail.aspx?pid=439" },
+-	{ "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",		"VT6X4",		"http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?fMTYPE=Slot%201&pMODEL_NAME=VT6X4" },
+-	{ "Abit",		"IP35",			"http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?fMTYPE=LGA775&pMODEL_NAME=IP35" },
+-	{ "Abit",		"IP35 Pro",		"http://www.abit.com.tw/page/de/motherboard/motherboard_detail.php?fMTYPE=LGA775&pMODEL_NAME=IP35%20Pro" },
+-	{ "Abit",               "NF7-S",                "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?fMTYPE=Socket%20A&pMODEL_NAME=NF7-S"},
+-	{ "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",		"A7V8X-X",		"http://www.asus.com/product.aspx?P_ID=YcXfRrWHZ9RKoVmw" },
+-	{ "ASUS",		"M2NBP-VM CSM",		"http://www.asus.com/product.aspx?P_ID=MnOfzTGd2KkwG0rF" },
+-	{ "ASUS",		"M2V-MX",		"http://www.asus.com/product.aspx?P_ID=7grf8Ci4yxnqzt3z" },
+-	{ "ASUS",		"P4B266",		"ftp://ftp.asus.com.tw/pub/ASUS/mb/sock478/p4b266/" },
+-	{ "ASUS",		"P4C800-E Deluxe",	"http://www.asus.com/product.aspx?P_ID=cFuVCr9bXXCckmcK" },
+-	{ "ASUS",		"P4B266-LM",		"http://esupport.sony.com/US/perl/swu-list.pl?mdl=PCVRX650" },
+-	{ "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",		"K7S6A",		"http://www.ecs.com.tw/ECSWebSite/Products/ProductsDetail.aspx?detailid=77&CategoryID=1&DetailName=Specification&MenuID=52&LanID=0" },
+-	{ "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_Spec.aspx?ProductID=2287" },
+-	{ "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" },
+-	{ "HP",			"Vectra VL400 PC",	"http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c00060658&lang=en&cc=us" },
+-	{ "HP",			"Vectra VL420 SFF PC",	"http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c00060661&lang=en&cc=us" },
+-	{ "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.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=502" },
+-	{ "MSI",		"MS-6702E (K8T Neo2-F)","http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=588" },
+-	{ "MSI",		"MS-6712 (KT4V)",	"http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=505" },
+-	{ "MSI",		"MS-7005 (651M-L)",	"http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=559" },
+-	{ "MSI",		"MS-7046",		"http://www.heimir.de/ms7046/" },
+-	{ "MSI",		"MS-7135 (K8N Neo3)",	"http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=170" },
+-	{ "Shuttle",		"AK31",			"http://www.motherboard.cz/mb/shuttle/AK31.htm" },
+-	{ "Shuttle",		"AK38N",		"http://eu.shuttle.com/en/desktopdefault.aspx/tabid-36/558_read-9889/" },
+-	{ "Shuttle",		"FN25",			"http://www.shuttle.eu/products/discontinued/barebones/sn25p/?0=" },
+-	{ "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",		"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.com/index.php?func=proddesc&maincat_no=1&prod_no=343" },
+-	{ "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. */
+-	{ "Acer",		"Aspire 1520",		"http://support.acer.com/us/en/acerpanam/notebook/0000/Acer/Aspire1520/Aspire1520nv.shtml" },
+-	{ "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)
++static void print_supported_chipsets_wiki(int cols)
+ {
+ 	int i, j, enablescount = 0, color = 1;
+ 	const struct penable *e;
+@@ -389,14 +102,14 @@ void print_supported_chipsets_wiki(void)
+ 		if (i > 0 && strcmp(e[i].vendor_name, e[i - 1].vendor_name))
+ 			color = !color;
+ 
+-		printf("|- bgcolor=\"#%s\" valign=\"top\"\n| %s || %s "
++		printf("|- bgcolor=\"#%s\"\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}}" : "?");
++		       (e[i].status == OK) ? "{{OK}}" : "{{?3}}");
+ 
+-		/* Split table in three columns. */
+-		if (j >= (enablescount / 3 + 1)) {
++		/* Split table in 'cols' columns. */
++		if (j >= (enablescount / cols + 1)) {
+ 			printf("\n|}\n\n| valign=\"top\"|\n\n%s", chipset_th);
+ 			j = 0;
+ 		}
+@@ -405,47 +118,64 @@ void print_supported_chipsets_wiki(void)
+ 	printf("\n|}\n\n|}\n");
+ }
+ 
+-static void wiki_helper(const char *heading, const char *status,
+-			int cols, const struct board_info boards[])
++static void wiki_helper(const char *devicetype, 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;
++	int i, j, k = 0, boardcount_good = 0, boardcount_bad = 0, color = 1;
++	int num_notes = 0;
+ 	char *notes = calloc(1, 1);
+ 	char tmp[900 + 1];
++	const struct board_pciid_enable *b = board_pciid_enables;
+ 
+-	for (b = boards; b->vendor != NULL; b++)
+-		boardcount++;
++	for (i = 0; boards[i].vendor != NULL; i++) {
++		if (boards[i].working)
++			boardcount_good++;
++		else
++			boardcount_bad++;
++	}
+ 
+-	printf("\n'''%s'''\n\nTotal amount of boards: '''%d'''\n\n"
++	printf("\n\nTotal amount of supported %s: '''%d'''. "
++	       "Not yet supported (i.e., known-bad): '''%d'''.\n\n"
+ 	       "{| border=\"0\" valign=\"top\"\n| valign=\"top\"|\n\n%s",
+-	       heading, boardcount, board_th);
++	       devicetype, boardcount_good, boardcount_bad, board_th);
++
++	for (i = 0, j = 0; boards[i].vendor != NULL; i++, j++) {
+ 
+-        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))
++		if (i > 0 && strcmp(boards[i].vendor, boards[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);
++		k = 0;
++		while ((b[k].vendor_name != NULL) &&
++			(strcmp(b[k].vendor_name, boards[i].vendor) ||
++			 strcmp(b[k].board_name, boards[i].name))) {
++			k++;
++		}
+ 
+-		if (c != -1) {
++		printf("|- bgcolor=\"#%s\"\n| %s || %s%s %s%s || %s%s%s%s "
++		       "|| {{%s}}", (color) ? "eeeeee" : "dddddd",
++		       boards[i].vendor,
++		       boards[i].url ? "[" : "",
++		       boards[i].url ? boards[i].url : "",
++		       boards[i].name,
++		       boards[i].url ? "]" : "",
++		       b[k].lb_vendor ? "-m " : "&mdash;",
++		       b[k].lb_vendor ? b[k].lb_vendor : "",
++		       b[k].lb_vendor ? ":" : "",
++		       b[k].lb_vendor ? b[k].lb_part : "",
++		       (boards[i].working) ? "OK" : "No");
++
++		if (boards[i].note) {
+ 			printf("<sup>%d</sup>\n", num_notes + 1);
+ 			snprintf((char *)&tmp, 900, "<sup>%d</sup> %s<br />\n",
+-				 1 + num_notes++, boards_notes[c].note);
++				 1 + num_notes++, boards[i].note);
+ 			notes = strcat_realloc(notes, (char *)&tmp);
+ 		} else {
+ 			printf("\n");
+ 		}
+ 
+ 		/* Split table in 'cols' columns. */
+-		if (j >= (boardcount / cols + 1)) {
++		if (j >= ((boardcount_good + boardcount_bad) / cols + 1)) {
+ 			printf("\n|}\n\n| valign=\"top\"|\n\n%s", board_th);
+ 			j = 0;
+ 		}
+@@ -458,61 +188,17 @@ static void wiki_helper(const char *heading, const char *status,
+ 	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 " : "&mdash;",
+-		       (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)
++static 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);
++	wiki_helper("boards", 2, boards_known);
+ 
+ 	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);
++	wiki_helper("laptops", 1, laptops_known);
+ }
+ #endif
+ 
+-void print_supported_chips_wiki(void)
++static void print_supported_chips_wiki(int cols)
+ {
+ 	int i = 0, c = 1, chipcount = 0;
+ 	struct flashchip *f, *old = NULL;
+@@ -535,21 +221,21 @@ void print_supported_chips_wiki(void)
+ 			c = !c;
+ 
+ 		t = f->tested;
+-		printf("|- bgcolor=\"#%s\" valign=\"top\"\n| %s || %s || %d "
++		printf("|- bgcolor=\"#%s\"\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_BAD_PROBE) ? "No" : "?3",
+ 		       (t & TEST_OK_READ) ? "OK" :
+-		       (t & TEST_BAD_READ) ? "No" : ((c) ? "?2" : "?"),
++		       (t & TEST_BAD_READ) ? "No" : "?3",
+ 		       (t & TEST_OK_ERASE) ? "OK" :
+-		       (t & TEST_BAD_ERASE) ? "No" : ((c) ? "?2" : "?"),
++		       (t & TEST_BAD_ERASE) ? "No" : "?3",
+ 		       (t & TEST_OK_WRITE) ? "OK" :
+-		       (t & TEST_BAD_WRITE) ? "No" : ((c) ? "?2" : "?"));
++		       (t & TEST_BAD_WRITE) ? "No" : "?3");
+ 
+-		/* Split table into three columns. */
+-		if (i >= (chipcount / 3 + 1)) {
++		/* Split table into 'cols' columns. */
++		if (i >= (chipcount / cols + 1)) {
+ 			printf("\n|}\n\n| valign=\"top\"|\n\n%s", chip_th);
+ 			i = 0;
+ 		}
+@@ -560,7 +246,7 @@ void print_supported_chips_wiki(void)
+ 	printf("\n|}\n\n|}\n");
+ }
+ 
+-void print_supported_pcidevs_wiki(struct pcidev_status *devs)
++static void print_supported_pcidevs_wiki(const struct pcidev_status *devs)
+ {
+ 	int i = 0;
+ 	static int c = 0;
+@@ -569,11 +255,11 @@ void print_supported_pcidevs_wiki(struct pcidev_status *devs)
+ 	c = !c;
+ 
+ 	for (i = 0; devs[i].vendor_name != NULL; i++) {
+-		printf("|- bgcolor=\"#%s\" valign=\"top\"\n| %s || %s || "
++		printf("|- bgcolor=\"#%s\"\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 == NT) ? (c) ? "?2" : "?" : "OK");
++		       (devs[i].status == NT) ? "?3" : "OK");
+ 	}
+ }
+ 
+@@ -582,27 +268,37 @@ void print_supported_wiki(void)
+ 	time_t t = time(NULL);
+ 
+ 	printf(wiki_header, ctime(&t), flashrom_version);
+-#if INTERNAL_SUPPORT == 1
+-	print_supported_chips_wiki();
+-	print_supported_chipsets_wiki();
++#if CONFIG_INTERNAL == 1
++	print_supported_chips_wiki(2);
++	print_supported_chipsets_wiki(3);
+ 	print_supported_boards_wiki();
+ #endif
+ 	printf("%s", programmer_section);
+-#if NIC3COM_SUPPORT == 1
++#if CONFIG_NIC3COM == 1
+ 	print_supported_pcidevs_wiki(nics_3com);
+ #endif
+-#if GFXNVIDIA_SUPPORT == 1
++#if CONFIG_NICREALTEK == 1
++	print_supported_pcidevs_wiki(nics_realtek);
++	print_supported_pcidevs_wiki(nics_realteksmc1211);
++#endif
++#if CONFIG_NICNATSEMI == 1
++	print_supported_pcidevs_wiki(nics_natsemi);
++#endif
++#if CONFIG_GFXNVIDIA == 1
+ 	print_supported_pcidevs_wiki(gfx_nvidia);
+ #endif
+-#if DRKAISER_SUPPORT == 1
++#if CONFIG_DRKAISER == 1
+ 	print_supported_pcidevs_wiki(drkaiser_pcidev);
+ #endif
+-#if SATASII_SUPPORT == 1
++#if CONFIG_SATASII == 1
+ 	print_supported_pcidevs_wiki(satas_sii);
+ #endif
+-#if ATAHPT_SUPPORT == 1
++#if CONFIG_ATAHPT == 1
+ 	print_supported_pcidevs_wiki(ata_hpt);
+ #endif
++#if CONFIG_NICINTEL_SPI == 1
++	print_supported_pcidevs_wiki(nics_intel_spi);
++#endif
+ 	printf("\n|}\n");
+ }
+ 
+diff --git a/processor_enable.c b/processor_enable.c
+new file mode 100644
+index 0000000..245975e
+--- /dev/null
++++ b/processor_enable.c
+@@ -0,0 +1,46 @@
++/*
++ * 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
++ */
++
++/*
++ * Contains the processor specific flash enables and system settings.
++ */
++
++#include "flash.h"
++#include "programmer.h"
++
++#if defined(__i386__) || defined(__x86_64__)
++
++int processor_flash_enable(void)
++{
++	/* On x86, flash access is not processor specific except on
++	 * AMD Elan SC520, AMD Geode and maybe other SoC-style CPUs.
++	 * FIXME: Move enable_flash_cs5536 and get_flashbase_sc520 here.
++	 */
++	return 0;
++}
++
++#else
++
++int processor_flash_enable(void)
++{
++	/* Not implemented yet. Oh well. */
++	return 1;
++}
++
++#endif
+diff --git a/programmer.c b/programmer.c
+index ca641e1..0160867 100644
+--- a/programmer.c
++++ b/programmer.c
+@@ -18,7 +18,6 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
+-#include <stdlib.h>
+ #include "flash.h"
+ 
+ /* No-op shutdown() for programmers which don't need special handling */
+diff --git a/programmer.h b/programmer.h
+new file mode 100644
+index 0000000..0d8e161
+--- /dev/null
++++ b/programmer.h
+@@ -0,0 +1,599 @@
++/*
++ * This file is part of the flashrom project.
++ *
++ * Copyright (C) 2000 Silicon Integrated System Corporation
++ * Copyright (C) 2000 Ronald G. Minnich <rminnich at gmail.com>
++ * Copyright (C) 2005-2009 coresystems GmbH
++ * Copyright (C) 2006-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
++ */
++
++#ifndef __PROGRAMMER_H__
++#define __PROGRAMMER_H__ 1
++
++enum programmer {
++#if CONFIG_INTERNAL == 1
++	PROGRAMMER_INTERNAL,
++#endif
++#if CONFIG_DUMMY == 1
++	PROGRAMMER_DUMMY,
++#endif
++#if CONFIG_NIC3COM == 1
++	PROGRAMMER_NIC3COM,
++#endif
++#if CONFIG_NICREALTEK == 1
++	PROGRAMMER_NICREALTEK,
++	PROGRAMMER_NICREALTEK2,
++#endif
++#if CONFIG_NICNATSEMI == 1
++	PROGRAMMER_NICNATSEMI,
++#endif
++#if CONFIG_GFXNVIDIA == 1
++	PROGRAMMER_GFXNVIDIA,
++#endif
++#if CONFIG_DRKAISER == 1
++	PROGRAMMER_DRKAISER,
++#endif
++#if CONFIG_SATASII == 1
++	PROGRAMMER_SATASII,
++#endif
++#if CONFIG_ATAHPT == 1
++	PROGRAMMER_ATAHPT,
++#endif
++#if CONFIG_INTERNAL == 1
++#if defined(__i386__) || defined(__x86_64__)
++	PROGRAMMER_IT87SPI,
++#endif
++#endif
++#if CONFIG_FT2232_SPI == 1
++	PROGRAMMER_FT2232_SPI,
++#endif
++#if CONFIG_SERPROG == 1
++	PROGRAMMER_SERPROG,
++#endif
++#if CONFIG_BUSPIRATE_SPI == 1
++	PROGRAMMER_BUSPIRATE_SPI,
++#endif
++#if CONFIG_DEDIPROG == 1
++	PROGRAMMER_DEDIPROG,
++#endif
++#if CONFIG_RAYER_SPI == 1
++	PROGRAMMER_RAYER_SPI,
++#endif
++#if CONFIG_NICINTEL_SPI == 1
++	PROGRAMMER_NICINTEL_SPI,
++#endif
++	PROGRAMMER_INVALID /* This must always be the last entry. */
++};
++
++extern enum programmer programmer;
++
++struct programmer_entry {
++	const char *vendor;
++	const char *name;
++
++	int (*init) (void);
++	int (*shutdown) (void);
++
++	void * (*map_flash_region) (const char *descr, unsigned long phys_addr,
++				    size_t len);
++	void (*unmap_flash_region) (void *virt_addr, size_t len);
++
++	void (*chip_writeb) (uint8_t val, chipaddr addr);
++	void (*chip_writew) (uint16_t val, chipaddr addr);
++	void (*chip_writel) (uint32_t val, chipaddr addr);
++	void (*chip_writen) (uint8_t *buf, chipaddr addr, size_t len);
++	uint8_t (*chip_readb) (const chipaddr addr);
++	uint16_t (*chip_readw) (const chipaddr addr);
++	uint32_t (*chip_readl) (const chipaddr addr);
++	void (*chip_readn) (uint8_t *buf, const chipaddr addr, size_t len);
++	void (*delay) (int usecs);
++};
++
++extern const struct programmer_entry programmer_table[];
++
++int programmer_init(char *param);
++int programmer_shutdown(void);
++
++enum bitbang_spi_master_type {
++	BITBANG_SPI_INVALID	= 0, /* This must always be the first entry. */
++#if CONFIG_RAYER_SPI == 1
++	BITBANG_SPI_MASTER_RAYER,
++#endif
++#if CONFIG_NICINTEL_SPI == 1
++	BITBANG_SPI_MASTER_NICINTEL,
++#endif
++#if CONFIG_INTERNAL == 1
++#if defined(__i386__) || defined(__x86_64__)
++	BITBANG_SPI_MASTER_MCP,
++#endif
++#endif
++};
++
++struct bitbang_spi_master {
++	enum bitbang_spi_master_type type;
++
++	/* Note that CS# is active low, so val=0 means the chip is active. */
++	void (*set_cs) (int val);
++	void (*set_sck) (int val);
++	void (*set_mosi) (int val);
++	int (*get_miso) (void);
++};
++
++#if CONFIG_INTERNAL == 1
++struct penable {
++	uint16_t vendor_id;
++	uint16_t device_id;
++	int status;
++	const char *vendor_name;
++	const char *device_name;
++	int (*doit) (struct pci_dev *dev, const char *name);
++};
++
++extern const struct penable chipset_enables[];
++
++struct board_pciid_enable {
++	/* Any device, but make it sensible, like the ISA bridge. */
++	uint16_t first_vendor;
++	uint16_t first_device;
++	uint16_t first_card_vendor;
++	uint16_t first_card_device;
++
++	/* Any device, but make it sensible, like
++	 * the host bridge. May be NULL.
++	 */
++	uint16_t second_vendor;
++	uint16_t second_device;
++	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;
++
++	const char *vendor_name;
++	const char *board_name;
++
++	int max_rom_decode_parallel;
++	int status;
++	int (*enable) (void);
++};
++
++extern const struct board_pciid_enable board_pciid_enables[];
++
++struct board_info {
++	const char *vendor;
++	const char *name;
++	const int working;
++#ifdef CONFIG_PRINT_WIKI
++	const char *url;
++	const char *note;
++#endif
++};
++
++extern const struct board_info boards_known[];
++extern const struct board_info laptops_known[];
++#endif
++
++/* udelay.c */
++void myusec_delay(int usecs);
++void myusec_calibrate_delay(void);
++void internal_delay(int usecs);
++
++#if NEED_PCI == 1
++/* pcidev.c */
++extern uint32_t io_base_addr;
++extern struct pci_access *pacc;
++extern struct pci_dev *pcidev_dev;
++struct pcidev_status {
++	uint16_t vendor_id;
++	uint16_t device_id;
++	int status;
++	const char *vendor_name;
++	const char *device_name;
++};
++uint32_t pcidev_validate(struct pci_dev *dev, uint32_t bar, const struct pcidev_status *devs);
++uint32_t pcidev_init(uint16_t vendor_id, uint32_t bar, const struct pcidev_status *devs);
++#endif
++
++/* print.c */
++#if CONFIG_NIC3COM+CONFIG_NICREALTEK+CONFIG_NICNATSEMI+CONFIG_GFXNVIDIA+CONFIG_DRKAISER+CONFIG_SATASII+CONFIG_ATAHPT+CONFIG_NICINTEL_SPI >= 1
++void print_supported_pcidevs(const struct pcidev_status *devs);
++#endif
++
++/* board_enable.c */
++void w836xx_ext_enter(uint16_t port);
++void w836xx_ext_leave(uint16_t port);
++int it8705f_write_enable(uint8_t port);
++uint8_t sio_read(uint16_t port, uint8_t reg);
++void sio_write(uint16_t port, uint8_t reg, uint8_t data);
++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 */
++int chipset_flash_enable(void);
++
++/* processor_enable.c */
++int processor_flash_enable(void);
++
++/* 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);
++
++/* 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 CONFIG_INTERNAL == 1
++extern int is_laptop;
++extern int force_boardenable;
++extern int force_boardmismatch;
++void probe_superio(void);
++int internal_init(void);
++int internal_shutdown(void);
++void internal_chip_writeb(uint8_t val, chipaddr addr);
++void internal_chip_writew(uint16_t val, chipaddr addr);
++void internal_chip_writel(uint32_t val, chipaddr addr);
++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
++
++/* hwaccess.c */
++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 mmio_le_writeb(uint8_t val, void *addr);
++void mmio_le_writew(uint16_t val, void *addr);
++void mmio_le_writel(uint32_t val, void *addr);
++uint8_t mmio_le_readb(void *addr);
++uint16_t mmio_le_readw(void *addr);
++uint32_t mmio_le_readl(void *addr);
++#define pci_mmio_writeb mmio_le_writeb
++#define pci_mmio_writew mmio_le_writew
++#define pci_mmio_writel mmio_le_writel
++#define pci_mmio_readb mmio_le_readb
++#define pci_mmio_readw mmio_le_readw
++#define pci_mmio_readl mmio_le_readl
++
++/* 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);
++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);
++
++/* dummyflasher.c */
++#if CONFIG_DUMMY == 1
++int dummy_init(void);
++int dummy_shutdown(void);
++void *dummy_map(const char *descr, unsigned long phys_addr, size_t len);
++void dummy_unmap(void *virt_addr, size_t len);
++void dummy_chip_writeb(uint8_t val, chipaddr addr);
++void dummy_chip_writew(uint16_t val, chipaddr addr);
++void dummy_chip_writel(uint32_t val, chipaddr addr);
++void dummy_chip_writen(uint8_t *buf, chipaddr addr, size_t len);
++uint8_t dummy_chip_readb(const chipaddr addr);
++uint16_t dummy_chip_readw(const chipaddr addr);
++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);
++int dummy_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len);
++int dummy_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len);
++#endif
++
++/* nic3com.c */
++#if CONFIG_NIC3COM == 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 const struct pcidev_status nics_3com[];
++#endif
++
++/* gfxnvidia.c */
++#if CONFIG_GFXNVIDIA == 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 const struct pcidev_status gfx_nvidia[];
++#endif
++
++/* drkaiser.c */
++#if CONFIG_DRKAISER == 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 const struct pcidev_status drkaiser_pcidev[];
++#endif
++
++/* nicrealtek.c */
++#if CONFIG_NICREALTEK == 1
++int nicrealtek_init(void);
++int nicsmc1211_init(void);
++int nicrealtek_shutdown(void);
++void nicrealtek_chip_writeb(uint8_t val, chipaddr addr);
++uint8_t nicrealtek_chip_readb(const chipaddr addr);
++extern const struct pcidev_status nics_realtek[];
++extern const struct pcidev_status nics_realteksmc1211[];
++#endif
++
++/* nicnatsemi.c */
++#if CONFIG_NICNATSEMI == 1
++int nicnatsemi_init(void);
++int nicnatsemi_shutdown(void);
++void nicnatsemi_chip_writeb(uint8_t val, chipaddr addr);
++uint8_t nicnatsemi_chip_readb(const chipaddr addr);
++extern const struct pcidev_status nics_natsemi[];
++#endif
++
++/* nicintel_spi.c */
++#if CONFIG_NICINTEL_SPI == 1
++int nicintel_spi_init(void);
++int nicintel_spi_shutdown(void);
++int nicintel_spi_send_command(unsigned int writecnt, unsigned int readcnt,
++	const unsigned char *writearr, unsigned char *readarr);
++void nicintel_spi_chip_writeb(uint8_t val, chipaddr addr);
++extern const struct pcidev_status nics_intel_spi[];
++#endif
++
++/* satasii.c */
++#if CONFIG_SATASII == 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 const struct pcidev_status satas_sii[];
++#endif
++
++/* atahpt.c */
++#if CONFIG_ATAHPT == 1
++int atahpt_init(void);
++int atahpt_shutdown(void);
++void atahpt_chip_writeb(uint8_t val, chipaddr addr);
++uint8_t atahpt_chip_readb(const chipaddr addr);
++extern const struct pcidev_status ata_hpt[];
++#endif
++
++/* ft2232_spi.c */
++#if CONFIG_FT2232_SPI == 1
++struct usbdev_status {
++	uint16_t vendor_id;
++	uint16_t device_id;
++	int status;
++	const char *vendor_name;
++	const char *device_name;
++};
++int ft2232_spi_init(void);
++int ft2232_spi_send_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr);
++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, int start, int len);
++extern const struct usbdev_status devs_ft2232spi[];
++void print_supported_usbdevs(const struct usbdev_status *devs);
++#endif
++
++/* rayer_spi.c */
++#if CONFIG_RAYER_SPI == 1
++int rayer_spi_init(void);
++#endif
++
++/* mcp6x_spi.c */
++#if CONFIG_INTERNAL == 1
++#if defined(__i386__) || defined(__x86_64__)
++int mcp6x_spi_init(int want_spi);
++#endif
++#endif
++
++/* bitbang_spi.c */
++int bitbang_spi_init(const struct bitbang_spi_master *master, int halfperiod);
++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, int start, int len);
++
++/* 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);
++int buspirate_spi_write_256(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 */
++struct decode_sizes {
++	uint32_t parallel;
++	uint32_t lpc;
++	uint32_t fwh;
++	uint32_t spi;
++};
++extern struct decode_sizes max_rom_decode;
++extern int programmer_may_write;
++extern unsigned long flashbase;
++void check_chip_supported(struct flashchip *flash);
++int check_max_decode(enum chipbustype buses, uint32_t size);
++char *extract_programmer_param(char *param_name);
++
++/* layout.c */
++int show_id(uint8_t *bios, int size, int force);
++
++/* spi.c */
++enum spi_controller {
++	SPI_CONTROLLER_NONE,
++#if CONFIG_INTERNAL == 1
++#if defined(__i386__) || defined(__x86_64__)
++	SPI_CONTROLLER_ICH7,
++	SPI_CONTROLLER_ICH9,
++	SPI_CONTROLLER_IT87XX,
++	SPI_CONTROLLER_SB600,
++	SPI_CONTROLLER_VIA,
++	SPI_CONTROLLER_WBSIO,
++	SPI_CONTROLLER_MCP6X_BITBANG,
++#endif
++#endif
++#if CONFIG_FT2232_SPI == 1
++	SPI_CONTROLLER_FT2232,
++#endif
++#if CONFIG_DUMMY == 1
++	SPI_CONTROLLER_DUMMY,
++#endif
++#if CONFIG_BUSPIRATE_SPI == 1
++	SPI_CONTROLLER_BUSPIRATE,
++#endif
++#if CONFIG_DEDIPROG == 1
++	SPI_CONTROLLER_DEDIPROG,
++#endif
++#if CONFIG_RAYER_SPI == 1
++	SPI_CONTROLLER_RAYER,
++#endif
++#if CONFIG_NICINTEL_SPI == 1
++	SPI_CONTROLLER_NICINTEL,
++#endif
++	SPI_CONTROLLER_INVALID /* This must always be the last entry. */
++};
++extern const int spi_programmer_count;
++struct spi_programmer {
++	int (*command)(unsigned int writecnt, unsigned int readcnt,
++		   const unsigned char *writearr, unsigned char *readarr);
++	int (*multicommand)(struct spi_command *cmds);
++
++	/* Optimized functions for this programmer */
++	int (*read)(struct flashchip *flash, uint8_t *buf, int start, int len);
++	int (*write_256)(struct flashchip *flash, uint8_t *buf, int start, int len);
++};
++
++extern enum spi_controller spi_controller;
++extern const struct spi_programmer spi_programmer[];
++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 *cmds);
++
++/* ichspi.c */
++#if CONFIG_INTERNAL == 1
++extern uint32_t ichspi_bbar;
++int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
++		    int ich_generation);
++int via_init_spi(struct pci_dev *dev);
++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 start, int len);
++int ich_spi_send_multicommand(struct spi_command *cmds);
++#endif
++
++/* it87spi.c */
++void enter_conf_mode_ite(uint16_t port);
++void exit_conf_mode_ite(uint16_t port);
++struct superio probe_superio_ite(void);
++int init_superio_ite(void);
++int it87spi_init(void);
++int it8716f_spi_send_command(unsigned int writecnt, unsigned int readcnt,
++			const unsigned char *writearr, unsigned char *readarr);
++int it8716f_spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len);
++int it8716f_spi_chip_write_256(struct flashchip *flash, uint8_t *buf, int start, int len);
++
++/* sb600spi.c */
++#if CONFIG_INTERNAL == 1
++int sb600_probe_spi(struct pci_dev *dev);
++int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt,
++		      const unsigned char *writearr, unsigned char *readarr);
++int sb600_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len);
++int sb600_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len);
++#endif
++
++/* wbsio_spi.c */
++#if CONFIG_INTERNAL == 1
++int wbsio_check_for_spi(void);
++int wbsio_spi_send_command(unsigned int writecnt, unsigned int readcnt,
++		      const unsigned char *writearr, unsigned char *readarr);
++int wbsio_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len);
++#endif
++
++/* serprog.c */
++int serprog_init(void);
++int serprog_shutdown(void);
++void serprog_chip_writeb(uint8_t val, chipaddr addr);
++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);
++
++#endif				/* !__PROGRAMMER_H__ */
+diff --git a/rayer_spi.c b/rayer_spi.c
+new file mode 100644
+index 0000000..722ad14
+--- /dev/null
++++ b/rayer_spi.c
+@@ -0,0 +1,123 @@
++/*
++ * 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; 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
++ */
++
++/* Driver for the SPIPGM hardware by "RayeR" Martin Rehak.
++ * See http://rayer.ic.cz/elektro/spipgm.htm for schematics and instructions.
++ */
++
++/* This driver uses non-portable direct I/O port accesses which won't work on
++ * any non-x86 platform, and even on x86 there is a high chance there will be
++ * collisions with any loaded parallel port drivers.
++ * The big advantage of direct port I/O is OS independence and speed because
++ * most OS parport drivers will perform many unnecessary accesses although
++ * this driver just treats the parallel port as a GPIO set.
++ */
++#if defined(__i386__) || defined(__x86_64__)
++
++#include "flash.h"
++#include "programmer.h"
++
++/* We have two sets of pins, out and in. The numbers for both sets are
++ * independent and are bitshift values, not real pin numbers.
++ */
++/* Pins for master->slave direction */
++#define SPI_CS_PIN 5
++#define SPI_SCK_PIN 6
++#define SPI_MOSI_PIN 7
++/* Pins for slave->master direction */
++#define SPI_MISO_PIN 6
++
++static int lpt_iobase;
++
++/* FIXME: All rayer_bitbang_set_* functions could use caching of the value
++ * stored at port lpt_iobase to avoid unnecessary INB. In theory, only one
++ * INB(lpt_iobase) would be needed on programmer init to get the initial
++ * value.
++ */
++
++void rayer_bitbang_set_cs(int val)
++{
++	uint8_t tmp;
++
++	tmp = INB(lpt_iobase);
++	tmp &= ~(1 << SPI_CS_PIN);
++	tmp |= (val << SPI_CS_PIN);
++	OUTB(tmp, lpt_iobase);
++}
++
++void rayer_bitbang_set_sck(int val)
++{
++	uint8_t tmp;
++
++	tmp = INB(lpt_iobase);
++	tmp &= ~(1 << SPI_SCK_PIN);
++	tmp |= (val << SPI_SCK_PIN);
++	OUTB(tmp, lpt_iobase);
++}
++
++void rayer_bitbang_set_mosi(int val)
++{
++	uint8_t tmp;
++
++	tmp = INB(lpt_iobase);
++	tmp &= ~(1 << SPI_MOSI_PIN);
++	tmp |= (val << SPI_MOSI_PIN);
++	OUTB(tmp, lpt_iobase);
++}
++
++int rayer_bitbang_get_miso(void)
++{
++	uint8_t tmp;
++
++	tmp = INB(lpt_iobase + 1);
++	tmp = (tmp >> SPI_MISO_PIN) & 0x1;
++	return tmp;
++}
++
++static const struct bitbang_spi_master bitbang_spi_master_rayer = {
++	.type = BITBANG_SPI_MASTER_RAYER,
++	.set_cs = rayer_bitbang_set_cs,
++	.set_sck = rayer_bitbang_set_sck,
++	.set_mosi = rayer_bitbang_set_mosi,
++	.get_miso = rayer_bitbang_get_miso,
++};
++
++int rayer_spi_init(void)
++{
++	/* Pick a default value for now. */
++	lpt_iobase = 0x378;
++
++	msg_pdbg("Using port 0x%x as I/O base for parallel port access.\n",
++		 lpt_iobase);
++
++	get_io_perms();
++
++	/* 1 usec halfperiod delay for now. */
++	if (bitbang_spi_init(&bitbang_spi_master_rayer, 1))
++		return 1;
++
++	buses_supported = CHIP_BUSTYPE_SPI;
++	spi_controller = SPI_CONTROLLER_RAYER;
++
++	return 0;
++}
++
++#else
++#error PCI port I/O access is not supported on this architecture yet.
++#endif
+diff --git a/satasii.c b/satasii.c
+index 90d38bd..89f45b9 100644
+--- a/satasii.c
++++ b/satasii.c
+@@ -21,21 +21,21 @@
+ /* Datasheets can be found on http://www.siliconimage.com. Great thanks! */
+ 
+ #include <stdlib.h>
+-#include <string.h>
+ #include "flash.h"
++#include "programmer.h"
+ 
+ #define PCI_VENDOR_ID_SII	0x1095
+ 
+ uint8_t *sii_bar;
+-uint16_t id;
++static uint16_t id;
+ 
+-struct pcidev_status satas_sii[] = {
++const struct pcidev_status satas_sii[] = {
+ 	{0x1095, 0x0680, OK, "Silicon Image", "PCI0680 Ultra ATA-133 Host Ctrl"},
+ 	{0x1095, 0x3112, OK, "Silicon Image", "SiI 3112 [SATALink/SATARaid] SATA Ctrl"},
+ 	{0x1095, 0x3114, OK, "Silicon Image", "SiI 3114 [SATALink/SATARaid] SATA Ctrl"},
+-	{0x1095, 0x3124, NT, "Silicon Image", "SiI 3124 PCI-X SATA Ctrl"},
++	{0x1095, 0x3124, OK, "Silicon Image", "SiI 3124 PCI-X SATA Ctrl"},
+ 	{0x1095, 0x3132, OK, "Silicon Image", "SiI 3132 SATA Raid II Ctrl"},
+-	{0x1095, 0x3512, NT, "Silicon Image", "SiI 3512 [SATALink/SATARaid] SATA Ctrl"},
++	{0x1095, 0x3512, OK, "Silicon Image", "SiI 3512 [SATALink/SATARaid] SATA Ctrl"},
+ 
+ 	{},
+ };
+@@ -47,8 +47,8 @@ int satasii_init(void)
+ 
+ 	get_io_perms();
+ 
+-	pcidev_init(PCI_VENDOR_ID_SII, PCI_BASE_ADDRESS_0, satas_sii,
+-		    programmer_param);
++	pcidev_init(PCI_VENDOR_ID_SII, PCI_BASE_ADDRESS_0, satas_sii);
++
+ 	id = pcidev_dev->device_id;
+ 
+ 	if ((id == 0x3132) || (id == 0x3124)) {
+@@ -62,7 +62,7 @@ int satasii_init(void)
+ 	sii_bar = physmap("SATA SIL registers", addr, 0x100) + reg_offset;
+ 
+ 	/* Check if ROM cycle are OK. */
+-	if ((id != 0x0680) && (!(mmio_readl(sii_bar) & (1 << 26))))
++	if ((id != 0x0680) && (!(pci_mmio_readl(sii_bar) & (1 << 26))))
+ 		msg_pinfo("Warning: Flash seems unconnected.\n");
+ 
+ 	buses_supported = CHIP_BUSTYPE_PARALLEL;
+@@ -72,7 +72,6 @@ int satasii_init(void)
+ 
+ int satasii_shutdown(void)
+ {
+-	free(programmer_param);
+ 	pci_cleanup(pacc);
+ 	release_io_perms();
+ 	return 0;
+@@ -82,32 +81,32 @@ void satasii_chip_writeb(uint8_t val, chipaddr addr)
+ {
+ 	uint32_t ctrl_reg, data_reg;
+ 
+-	while ((ctrl_reg = mmio_readl(sii_bar)) & (1 << 25)) ;
++	while ((ctrl_reg = pci_mmio_readl(sii_bar)) & (1 << 25)) ;
+ 
+ 	/* Mask out unused/reserved bits, set writes and start transaction. */
+ 	ctrl_reg &= 0xfcf80000;
+ 	ctrl_reg |= (1 << 25) | (0 << 24) | ((uint32_t) addr & 0x7ffff);
+ 
+-	data_reg = (mmio_readl((sii_bar + 4)) & ~0xff) | val;
+-	mmio_writel(data_reg, (sii_bar + 4));
+-	mmio_writel(ctrl_reg, sii_bar);
++	data_reg = (pci_mmio_readl((sii_bar + 4)) & ~0xff) | val;
++	pci_mmio_writel(data_reg, (sii_bar + 4));
++	pci_mmio_writel(ctrl_reg, sii_bar);
+ 
+-	while (mmio_readl(sii_bar) & (1 << 25)) ;
++	while (pci_mmio_readl(sii_bar) & (1 << 25)) ;
+ }
+ 
+ uint8_t satasii_chip_readb(const chipaddr addr)
+ {
+ 	uint32_t ctrl_reg;
+ 
+-	while ((ctrl_reg = mmio_readl(sii_bar)) & (1 << 25)) ;
++	while ((ctrl_reg = pci_mmio_readl(sii_bar)) & (1 << 25)) ;
+ 
+ 	/* Mask out unused/reserved bits, set reads and start transaction. */
+ 	ctrl_reg &= 0xfcf80000;
+ 	ctrl_reg |= (1 << 25) | (1 << 24) | ((uint32_t) addr & 0x7ffff);
+ 
+-	mmio_writel(ctrl_reg, sii_bar);
++	pci_mmio_writel(ctrl_reg, sii_bar);
+ 
+-	while (mmio_readl(sii_bar) & (1 << 25)) ;
++	while (pci_mmio_readl(sii_bar) & (1 << 25)) ;
+ 
+-	return (mmio_readl(sii_bar + 4)) & 0xff;
++	return (pci_mmio_readl(sii_bar + 4)) & 0xff;
+ }
+diff --git a/sb600spi.c b/sb600spi.c
+index 0cf797c..c20d5e6 100644
+--- a/sb600spi.c
++++ b/sb600spi.c
+@@ -4,6 +4,7 @@
+  * Copyright (C) 2008 Wang Qingpei <Qingpei.Wang at amd.com>
+  * Copyright (C) 2008 Joe Bao <Zheng.Bao at amd.com>
+  * Copyright (C) 2008 Advanced Micro Devices, Inc.
++ * 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
+@@ -20,9 +21,11 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
+-#include <string.h>
++#if defined(__i386__) || defined(__x86_64__)
++
+ #include "flash.h"
+ #include "chipdrivers.h"
++#include "programmer.h"
+ #include "spi.h"
+ 
+ /* This struct is unused, but helps visualize the SB600 SPI BAR layout.
+@@ -38,7 +41,7 @@
+  *};
+  */
+ 
+-uint8_t *sb600_spibar = NULL;
++static uint8_t *sb600_spibar = NULL;
+ 
+ int sb600_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+ {
+@@ -46,48 +49,49 @@ int sb600_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+ 	return spi_read_chunked(flash, buf, start, len, 8);
+ }
+ 
+-/* FIXME: SB600 can write 5 bytes per transaction. */
+-int sb600_spi_write_1(struct flashchip *flash, uint8_t *buf)
++int sb600_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
+ {
+-	int i;
+-	int total_size = flash->total_size * 1024;
+-	int result = 0;
+-
+-	spi_disable_blockprotect();
+-	/* Erase first */
+-	msg_pinfo("Erasing flash before programming... ");
+-	if (erase_flash(flash)) {
+-		msg_perr("ERASE FAILED!\n");
+-		return -1;
+-	}
+-	msg_pinfo("done.\n");
+-
+-	msg_pinfo("Programming flash");
+-	for (i = 0; i < total_size; 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)
+-			msg_pspew(".");
+-		while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP)
+-			;
+-	}
+-	msg_pinfo(" done.\n");
+-	return result;
++	return spi_write_chunked(flash, buf, start, len, 5);
+ }
+ 
+ static void reset_internal_fifo_pointer(void)
+ {
+ 	mmio_writeb(mmio_readb(sb600_spibar + 2) | 0x10, sb600_spibar + 2);
+ 
++	/* FIXME: This loop makes no sense at all. */
+ 	while (mmio_readb(sb600_spibar + 0xD) & 0x7)
+ 		msg_pspew("reset\n");
+ }
+ 
++static int compare_internal_fifo_pointer(uint8_t want)
++{
++	uint8_t tmp;
++
++	tmp = mmio_readb(sb600_spibar + 0xd) & 0x07;
++	want &= 0x7;
++	if (want != tmp) {
++		msg_perr("SB600 FIFO pointer corruption! Pointer is %d, wanted "
++			 "%d\n", tmp, want);
++		msg_perr("Something else is accessing the flash chip and "
++			 "causes random corruption.\nPlease stop all "
++			 "applications and drivers and IPMI which access the "
++			 "flash chip.\n");
++		return 1;
++	} else {
++		msg_pspew("SB600 FIFO pointer is %d, wanted %d\n", tmp, want);
++		return 0;
++	}
++}
++
++static int reset_compare_internal_fifo_pointer(uint8_t want)
++{
++	int ret;
++
++	ret = compare_internal_fifo_pointer(want);
++	reset_internal_fifo_pointer();
++	return ret;
++}
++
+ static void execute_command(void)
+ {
+ 	mmio_writeb(mmio_readb(sb600_spibar + 2) | 1, sb600_spibar + 2);
+@@ -103,6 +107,7 @@ int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ 	/* First byte is cmd which can not being sent through FIFO. */
+ 	unsigned char cmd = *writearr++;
+ 	unsigned int readoffby1;
++	unsigned char readwrite;
+ 
+ 	writecnt--;
+ 
+@@ -128,15 +133,17 @@ int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ 	 * It is unclear if the CS# line is set high too early as well.
+ 	 */
+ 	readoffby1 = (writecnt) ? 0 : 1;
+-	mmio_writeb((readcnt + readoffby1) << 4 | (writecnt), sb600_spibar + 1);
++	readwrite = (readcnt + readoffby1) << 4 | (writecnt);
++	mmio_writeb(readwrite, sb600_spibar + 1);
+ 	mmio_writeb(cmd, sb600_spibar + 0);
+ 
+ 	/* Before we use the FIFO, reset it first. */
+ 	reset_internal_fifo_pointer();
+ 
+ 	/* Send the write byte to FIFO. */
++	msg_pspew("Writing: ");
+ 	for (count = 0; count < writecnt; count++, writearr++) {
+-		msg_pspew(" [%x]", *writearr);
++		msg_pspew("[%02x]", *writearr);
+ 		mmio_writeb(*writearr, sb600_spibar + 0xC);
+ 	}
+ 	msg_pspew("\n");
+@@ -145,8 +152,10 @@ int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ 	 * We should send the data by sequence, which means we need to reset
+ 	 * the FIFO pointer to the first byte we want to send.
+ 	 */
+-	reset_internal_fifo_pointer();
++	if (reset_compare_internal_fifo_pointer(writecnt))
++		return SPI_PROGRAMMER_ERROR;
+ 
++	msg_pspew("Executing: \n");
+ 	execute_command();
+ 
+ 	/*
+@@ -160,21 +169,137 @@ int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ 	 * the opcode, the FIFO already stores the response from the chip.
+ 	 * Usually, the chip will respond with 0x00 or 0xff.
+ 	 */
+-	reset_internal_fifo_pointer();
++	if (reset_compare_internal_fifo_pointer(writecnt + readcnt))
++		return SPI_PROGRAMMER_ERROR;
+ 
+ 	/* Skip the bytes we sent. */
++	msg_pspew("Skipping: ");
+ 	for (count = 0; count < writecnt; count++) {
+ 		cmd = mmio_readb(sb600_spibar + 0xC);
+-		msg_pspew("[ %2x]", cmd);
++		msg_pspew("[%02x]", cmd);
+ 	}
++	msg_pspew("\n");
++	if (compare_internal_fifo_pointer(writecnt))
++		return SPI_PROGRAMMER_ERROR;
+ 
+-	msg_pspew("The FIFO pointer after skipping is %d.\n",
+-		  mmio_readb(sb600_spibar + 0xd) & 0x07);
++	msg_pspew("Reading: ");
+ 	for (count = 0; count < readcnt; count++, readarr++) {
+ 		*readarr = mmio_readb(sb600_spibar + 0xC);
+ 		msg_pspew("[%02x]", *readarr);
+ 	}
+ 	msg_pspew("\n");
++	if (reset_compare_internal_fifo_pointer(readcnt + writecnt))
++		return SPI_PROGRAMMER_ERROR;
++
++	if (mmio_readb(sb600_spibar + 1) != readwrite) {
++		msg_perr("Unexpected change in SB600 read/write count!\n");
++		msg_perr("Something else is accessing the flash chip and "
++			 "causes random corruption.\nPlease stop all "
++			 "applications and drivers and IPMI which access the "
++			 "flash chip.\n");
++		return SPI_PROGRAMMER_ERROR;
++	}
+ 
+ 	return 0;
+ }
++
++int sb600_probe_spi(struct pci_dev *dev)
++{
++	struct pci_dev *smbus_dev;
++	uint32_t tmp;
++	uint8_t reg;
++	const char *speed_names[4] = {
++		"Reserved", "33", "22", "16.5"
++	};
++
++	/* Read SPI_BaseAddr */
++	tmp = pci_read_long(dev, 0xa0);
++	tmp &= 0xffffffe0;	/* remove bits 4-0 (reserved) */
++	msg_pdbg("SPI base address is at 0x%x\n", tmp);
++
++	/* If the BAR has address 0, it is unlikely SPI is used. */
++	if (!tmp)
++		return 0;
++
++	/* Physical memory has to be mapped at page (4k) boundaries. */
++	sb600_spibar = physmap("SB600 SPI registers", tmp & 0xfffff000,
++			       0x1000);
++	/* The low bits of the SPI base address are used as offset into
++	 * the mapped page.
++	 */
++	sb600_spibar += tmp & 0xfff;
++
++	tmp = pci_read_long(dev, 0xa0);
++	msg_pdbg("AltSpiCSEnable=%i, SpiRomEnable=%i, "
++		     "AbortEnable=%i\n", tmp & 0x1, (tmp & 0x2) >> 1,
++		     (tmp & 0x4) >> 2);
++	tmp = (pci_read_byte(dev, 0xba) & 0x4) >> 2;
++	msg_pdbg("PrefetchEnSPIFromIMC=%i, ", tmp);
++
++	tmp = pci_read_byte(dev, 0xbb);
++	/* FIXME: Set bit 3,6,7 if not already set.
++	 * Set bit 5, otherwise SPI accesses are pointless in LPC mode.
++	 * See doc 42413 AMD SB700/710/750 RPR.
++	 */
++	msg_pdbg("PrefetchEnSPIFromHost=%i, SpiOpEnInLpcMode=%i\n",
++		     tmp & 0x1, (tmp & 0x20) >> 5);
++	tmp = mmio_readl(sb600_spibar);
++	/* FIXME: If SpiAccessMacRomEn or SpiHostAccessRomEn are zero on
++	 * SB700 or later, reads and writes will be corrupted. Abort in this
++	 * case. Make sure to avoid this check on SB600.
++	 */
++	msg_pdbg("SpiArbEnable=%i, SpiAccessMacRomEn=%i, "
++		     "SpiHostAccessRomEn=%i, ArbWaitCount=%i, "
++		     "SpiBridgeDisable=%i, DropOneClkOnRd=%i\n",
++		     (tmp >> 19) & 0x1, (tmp >> 22) & 0x1,
++		     (tmp >> 23) & 0x1, (tmp >> 24) & 0x7,
++		     (tmp >> 27) & 0x1, (tmp >> 28) & 0x1);
++	tmp = (mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3;
++	msg_pdbg("NormSpeed is %s MHz\n", speed_names[tmp]);
++
++	/* Look for the SMBus device. */
++	smbus_dev = pci_dev_find(0x1002, 0x4385);
++
++	if (!smbus_dev) {
++		msg_perr("ERROR: SMBus device not found. Not enabling SPI.\n");
++		return ERROR_NONFATAL;
++	}
++
++	/* Note about the bit tests below: If a bit is zero, the GPIO is SPI. */
++	/* GPIO11/SPI_DO and GPIO12/SPI_DI status */
++	reg = pci_read_byte(smbus_dev, 0xAB);
++	reg &= 0xC0;
++	msg_pdbg("GPIO11 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_DO");
++	msg_pdbg("GPIO12 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_DI");
++	if (reg != 0x00) {
++		msg_pdbg("Not enabling SPI");
++		return 0;
++	}
++	/* GPIO31/SPI_HOLD and GPIO32/SPI_CS status */
++	reg = pci_read_byte(smbus_dev, 0x83);
++	reg &= 0xC0;
++	msg_pdbg("GPIO31 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_HOLD");
++	msg_pdbg("GPIO32 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_CS");
++	/* SPI_HOLD is not used on all boards, filter it out. */
++	if ((reg & 0x80) != 0x00) {
++		msg_pdbg("Not enabling SPI");
++		return 0;
++	}
++	/* GPIO47/SPI_CLK status */
++	reg = pci_read_byte(smbus_dev, 0xA7);
++	reg &= 0x40;
++	msg_pdbg("GPIO47 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_CLK");
++	if (reg != 0x00) {
++		msg_pdbg("Not enabling SPI");
++		return 0;
++	}
++
++	/* Bring the FIFO to a clean state. */
++	reset_internal_fifo_pointer();
++
++	buses_supported |= CHIP_BUSTYPE_SPI;
++	spi_controller = SPI_CONTROLLER_SB600;
++	return 0;
++}
++
++#endif
+diff --git a/serial.c b/serial.c
+index 37f2549..caf9389 100644
+--- a/serial.c
++++ b/serial.c
+@@ -22,7 +22,6 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+-#include "flash.h"
+ #include <string.h>
+ #include <ctype.h>
+ #include <fcntl.h>
+@@ -35,6 +34,8 @@
+ #else
+ #include <termios.h>
+ #endif
++#include "flash.h"
++#include "programmer.h"
+ 
+ fdtype sp_fd;
+ 
+diff --git a/serprog.c b/serprog.c
+index efcf9d8..8354f54 100644
+--- a/serprog.c
++++ b/serprog.c
+@@ -22,7 +22,6 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+-#include "flash.h"
+ #include <string.h>
+ #include <ctype.h>
+ #include <fcntl.h>
+@@ -36,6 +35,8 @@
+ #include <errno.h>
+ #include <inttypes.h>
+ #include <termios.h>
++#include "flash.h"
++#include "programmer.h"
+ 
+ #define MSGHEADER "serprog:"
+ 
+@@ -298,43 +299,80 @@ static int sp_stream_buffer_op(uint8_t cmd, uint32_t parmlen, uint8_t * parms)
+ int serprog_init(void)
+ {
+ 	uint16_t iface;
+-	int len;
+ 	unsigned char pgmname[17];
+ 	unsigned char rbuf[3];
+ 	unsigned char c;
+-	char *num;
+-	char *dev;
+-	msg_pspew("%s\n", __func__);
+-	/* the parameter is either of format "/dev/device:baud" or "ip:port" */
+-	if ((!programmer_param) || (!strlen(programmer_param))) {
+-		nodevice:
+-		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);
++	char *device;
++	char *baudport;
++	int have_device = 0;
++
++	/* the parameter is either of format "dev=/dev/device:baud" or "ip=ip:port" */
++	device = extract_programmer_param("dev");
++	if (device && strlen(device)) {
++		baudport = strstr(device, ":");
++		if (baudport) {
++			/* Split device from baudrate. */
++			*baudport = '\0';
++			baudport++;
++		}
++		if (!baudport || !strlen(baudport)) {
++			msg_perr("Error: No baudrate specified.\n"
++				 "Use flashrom -p serprog:dev=/dev/device:baud\n");
++			free(device);
++			return 1;		
++		}
++		if (strlen(device)) {
++			sp_fd = sp_openserport(device, atoi(baudport));
++			have_device++;
++		}
+ 	}
+-	num = strstr(programmer_param, ":");
+-	len = num - programmer_param;
+-	if (!len) goto nodevice;
+-	if (!num) {
+-		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);
++	if (device && !strlen(device)) {
++		msg_perr("Error: No device specified.\n"
++			 "Use flashrom -p serprog:dev=/dev/device:baud\n");
++		free(device);
++		return 1;
++	}
++	free(device);
++
++	device = extract_programmer_param("ip");
++	if (have_device && device) {
++		msg_perr("Error: Both host and device specified.\n"
++			 "Please use either dev= or ip= but not both.\n");
++		free(device);
++		return 1;
++	}
++	if (device && strlen(device)) {
++		baudport = strstr(device, ":");
++		if (baudport) {
++			/* Split host from port. */
++			*baudport = '\0';
++			baudport++;
++		}
++		if (!baudport || !strlen(baudport)) {
++			msg_perr("Error: No port specified.\n"
++				 "Use flashrom -p serprog:ip=ipaddr:port\n");
++			free(device);
++			return 1;		
++		}
++		if (strlen(device)) {
++			sp_fd = sp_opensocket(device, atoi(baudport));
++			have_device++;
++		}
++	}
++	if (device && !strlen(device)) {
++		msg_perr("Error: No host specified.\n"
++			 "Use flashrom -p serprog:ip=ipaddr:port\n");
++		free(device);
++		return 1;
++	}
++	free(device);
++
++	if (!have_device) {
++		msg_perr("Error: Neither host nor device specified.\n"
++			 "Use flashrom -p serprog:dev=/dev/device:baud or "
++			 "flashrom -p serprog:ip=ipaddr:port\n");
++		return 1;
+ 	}
+-	len = num - programmer_param;
+-	dev = malloc(len + 1);
+-	if (!dev) sp_die("Error: memory allocation failure");
+-	memcpy(dev, programmer_param, len);
+-	dev[len] = 0;
+-	num = strdup(num + 1);
+-	if (!num) sp_die("Error: memory allocation failure");
+-	free(programmer_param);
+-	programmer_param = NULL;
+-
+-	if (dev[0] == '/') sp_fd = sp_openserport(dev, atoi(num));
+-	else sp_fd = sp_opensocket(dev, atoi(num));
+-
+-	free(dev); dev = NULL;
+-	free(num); num = NULL;
+ 
+ 	msg_pdbg(MSGHEADER "connected - attempting to synchronize\n");
+ 
+diff --git a/sharplhf00l04.c b/sharplhf00l04.c
+index 70b8135..a891e2c 100644
+--- a/sharplhf00l04.c
++++ b/sharplhf00l04.c
+@@ -18,7 +18,6 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
+-#include <stdlib.h>
+ #include "flash.h"
+ #include "chipdrivers.h"
+ 
+diff --git a/spi.c b/spi.c
+index 9bb4687..e892e74 100644
+--- a/spi.c
++++ b/spi.c
+@@ -22,16 +22,13 @@
+  * Contains the generic SPI framework
+  */
+ 
+-#include <string.h>
+ #include "flash.h"
+ #include "flashchips.h"
+ #include "chipdrivers.h"
++#include "programmer.h"
+ #include "spi.h"
+ 
+ enum spi_controller spi_controller = SPI_CONTROLLER_NONE;
+-void *spibar = NULL;
+-
+-void spi_prettyprint_status_register(struct flashchip *flash);
+ 
+ const struct spi_programmer spi_programmer[] = {
+ 	{ /* SPI_CONTROLLER_NONE */
+@@ -41,7 +38,8 @@ const struct spi_programmer spi_programmer[] = {
+ 		.write_256 = NULL,
+ 	},
+ 
+-#if INTERNAL_SUPPORT == 1
++#if CONFIG_INTERNAL == 1
++#if defined(__i386__) || defined(__x86_64__)
+ 	{ /* SPI_CONTROLLER_ICH7 */
+ 		.command = ich_spi_send_command,
+ 		.multicommand = ich_spi_send_multicommand,
+@@ -67,7 +65,7 @@ const struct spi_programmer spi_programmer[] = {
+ 		.command = sb600_spi_send_command,
+ 		.multicommand = default_spi_send_multicommand,
+ 		.read = sb600_spi_read,
+-		.write_256 = sb600_spi_write_1,
++		.write_256 = sb600_spi_write_256,
+ 	},
+ 
+ 	{ /* SPI_CONTROLLER_VIA */
+@@ -81,11 +79,19 @@ const struct spi_programmer spi_programmer[] = {
+ 		.command = wbsio_spi_send_command,
+ 		.multicommand = default_spi_send_multicommand,
+ 		.read = wbsio_spi_read,
+-		.write_256 = wbsio_spi_write_1,
++		.write_256 = spi_chip_write_1_new,
++	},
++
++	{ /* SPI_CONTROLLER_MCP6X_BITBANG */
++		.command = bitbang_spi_send_command,
++		.multicommand = default_spi_send_multicommand,
++		.read = bitbang_spi_read,
++		.write_256 = bitbang_spi_write_256,
+ 	},
+ #endif
++#endif
+ 
+-#if FT2232_SPI_SUPPORT == 1
++#if CONFIG_FT2232_SPI == 1
+ 	{ /* SPI_CONTROLLER_FT2232 */
+ 		.command = ft2232_spi_send_command,
+ 		.multicommand = default_spi_send_multicommand,
+@@ -94,16 +100,16 @@ const struct spi_programmer spi_programmer[] = {
+ 	},
+ #endif
+ 
+-#if DUMMY_SUPPORT == 1
++#if CONFIG_DUMMY == 1
+ 	{ /* SPI_CONTROLLER_DUMMY */
+ 		.command = dummy_spi_send_command,
+ 		.multicommand = default_spi_send_multicommand,
+-		.read = NULL,
+-		.write_256 = NULL,
++		.read = dummy_spi_read,
++		.write_256 = dummy_spi_write_256,
+ 	},
+ #endif
+ 
+-#if BUSPIRATE_SPI_SUPPORT == 1
++#if CONFIG_BUSPIRATE_SPI == 1
+ 	{ /* SPI_CONTROLLER_BUSPIRATE */
+ 		.command = buspirate_spi_send_command,
+ 		.multicommand = default_spi_send_multicommand,
+@@ -112,12 +118,30 @@ const struct spi_programmer spi_programmer[] = {
+ 	},
+ #endif
+ 
+-#if DEDIPROG_SUPPORT == 1
++#if CONFIG_DEDIPROG == 1
+ 	{ /* SPI_CONTROLLER_DEDIPROG */
+ 		.command = dediprog_spi_send_command,
+ 		.multicommand = default_spi_send_multicommand,
+ 		.read = dediprog_spi_read,
+-		.write_256 = spi_chip_write_1,
++		.write_256 = spi_chip_write_1_new,
++	},
++#endif
++
++#if CONFIG_RAYER_SPI == 1
++	{ /* SPI_CONTROLLER_RAYER */
++		.command = bitbang_spi_send_command,
++		.multicommand = default_spi_send_multicommand,
++		.read = bitbang_spi_read,
++		.write_256 = bitbang_spi_write_256,
++	},
++#endif
++
++#if CONFIG_NICINTEL_SPI == 1
++	{ /* SPI_CONTROLLER_NICINTEL */
++		.command = bitbang_spi_send_command,
++		.multicommand = default_spi_send_multicommand,
++		.read = bitbang_spi_read,
++		.write_256 = bitbang_spi_write_256,
+ 	},
+ #endif
+ 
+@@ -131,7 +155,8 @@ int spi_send_command(unsigned int writecnt, unsigned int readcnt,
+ {
+ 	if (!spi_programmer[spi_controller].command) {
+ 		msg_perr("%s called, but SPI is unsupported on this "
+-			"hardware. Please report a bug.\n", __func__);
++			 "hardware. Please report a bug at "
++			 "flashrom at flashrom.org\n", __func__);
+ 		return 1;
+ 	}
+ 
+@@ -143,7 +168,8 @@ int spi_send_multicommand(struct spi_command *cmds)
+ {
+ 	if (!spi_programmer[spi_controller].multicommand) {
+ 		msg_perr("%s called, but SPI is unsupported on this "
+-			"hardware. Please report a bug.\n", __func__);
++			 "hardware. Please report a bug at "
++			 "flashrom at flashrom.org\n", __func__);
+ 		return 1;
+ 	}
+ 
+@@ -182,8 +208,9 @@ int default_spi_send_multicommand(struct spi_command *cmds)
+ int spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+ {
+ 	if (!spi_programmer[spi_controller].read) {
+-		msg_perr("%s called, but SPI read is unsupported on this"
+-			" hardware. Please report a bug.\n", __func__);
++		msg_perr("%s called, but SPI read is unsupported on this "
++			 "hardware. Please report a bug at "
++			 "flashrom at flashrom.org\n", __func__);
+ 		return 1;
+ 	}
+ 
+@@ -193,20 +220,58 @@ int spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+ /*
+  * Program chip using page (256 bytes) programming.
+  * Some SPI masters can't do this, they use single byte programming instead.
++ * The redirect to single byte programming is achieved by setting
++ * .write_256 = spi_chip_write_1
+  */
+-int spi_chip_write_256(struct flashchip *flash, uint8_t *buf)
++/* real chunksize is up to 256, logical chunksize is 256 */
++int spi_chip_write_256_new(struct flashchip *flash, uint8_t *buf, int start, int len)
+ {
+ 	if (!spi_programmer[spi_controller].write_256) {
+-		msg_perr("%s called, but SPI page write is unsupported "
+-			" on this hardware. Please report a bug.\n", __func__);
++		msg_perr("%s called, but SPI page write is unsupported on this "
++			 "hardware. Please report a bug at "
++			 "flashrom at flashrom.org\n", __func__);
+ 		return 1;
+ 	}
+ 
+-	return spi_programmer[spi_controller].write_256(flash, buf);
++	return spi_programmer[spi_controller].write_256(flash, buf, start, len);
+ }
+ 
++/* Wrapper function until the generic code is converted to partial writes. */
++int spi_chip_write_256(struct flashchip *flash, uint8_t *buf)
++{
++	int ret;
++
++	msg_pinfo("Erasing flash before programming... ");
++	if (erase_flash(flash)) {
++		msg_perr("ERASE FAILED!\n");
++		return -1;
++	}
++	msg_pinfo("done.\n");
++	msg_pinfo("Programming flash... ");
++	ret = spi_chip_write_256_new(flash, buf, 0, flash->total_size * 1024);
++	if (!ret)
++		msg_pinfo("done.\n");
++	else
++		msg_pinfo("\n");
++	return ret;
++}
++
++/*
++ * Get the lowest allowed address for read accesses. This often happens to
++ * be the lowest allowed address for all commands which take an address.
++ * This is a programmer limitation.
++ */
+ uint32_t spi_get_valid_read_addr(void)
+ {
+-	/* Need to return BBAR for ICH chipsets. */
+-	return 0;
++	switch (spi_controller) {
++#if CONFIG_INTERNAL == 1
++#if defined(__i386__) || defined(__x86_64__)
++	case SPI_CONTROLLER_ICH7:
++		/* Return BBAR for ICH chipsets. */
++		return ichspi_bbar;
++#endif
++#endif
++	default:
++		return 0;
++	}
+ }
+diff --git a/spi.h b/spi.h
+index 1b49d59..b908603 100644
+--- a/spi.h
++++ b/spi.h
+@@ -27,10 +27,11 @@
+ /* Read Electronic ID */
+ #define JEDEC_RDID		0x9f
+ #define JEDEC_RDID_OUTSIZE	0x01
++/* INSIZE may be 0x04 for some chips*/
+ #define JEDEC_RDID_INSIZE	0x03
+ 
+ /* AT25F512A has bit 3 as don't care bit in commands */
+-#define AT25F512A_RDID		0x15
++#define AT25F512A_RDID		0x15	/* 0x15 or 0x1d */
+ #define AT25F512A_RDID_OUTSIZE	0x01
+ #define AT25F512A_RDID_INSIZE	0x02
+ 
+@@ -42,6 +43,7 @@
+ /* Read Electronic Signature */
+ #define JEDEC_RES		0xab
+ #define JEDEC_RES_OUTSIZE	0x04
++/* INSIZE may be 0x02 for some chips*/
+ #define JEDEC_RES_INSIZE	0x01
+ 
+ /* Write Enable */
+@@ -106,14 +108,22 @@
+ /*      JEDEC_READ_INSIZE : any length */
+ 
+ /* Write memory byte */
+-#define JEDEC_BYTE_PROGRAM	0x02
++#define JEDEC_BYTE_PROGRAM		0x02
+ #define JEDEC_BYTE_PROGRAM_OUTSIZE	0x05
+ #define JEDEC_BYTE_PROGRAM_INSIZE	0x00
+ 
++/* Write AAI word (SST25VF080B) */
++#define JEDEC_AAI_WORD_PROGRAM			0xad
++#define JEDEC_AAI_WORD_PROGRAM_OUTSIZE		0x06
++#define JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE	0x03
++#define JEDEC_AAI_WORD_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
++#define SPI_FLASHROM_BUG	-5
++#define SPI_PROGRAMMER_ERROR	-6
+ 
+ #endif		/* !__SPI_H__ */
+diff --git a/spi25.c b/spi25.c
+index c6d1b65..16d162a 100644
+--- a/spi25.c
++++ b/spi25.c
+@@ -1,7 +1,7 @@
+ /*
+  * This file is part of the flashrom project.
+  *
+- * Copyright (C) 2007, 2008, 2009 Carl-Daniel Hailfinger
++ * Copyright (C) 2007, 2008, 2009, 2010 Carl-Daniel Hailfinger
+  * Copyright (C) 2008 coresystems GmbH
+  *
+  * This program is free software; you can redistribute it and/or modify
+@@ -26,6 +26,7 @@
+ #include "flash.h"
+ #include "flashchips.h"
+ #include "chipdrivers.h"
++#include "programmer.h"
+ #include "spi.h"
+ 
+ void spi_prettyprint_status_register(struct flashchip *flash);
+@@ -67,24 +68,28 @@ static int spi_rems(unsigned char *readarr)
+ 	return 0;
+ }
+ 
+-static int spi_res(unsigned char *readarr)
++static int spi_res(unsigned char *readarr, int bytes)
+ {
+ 	unsigned char cmd[JEDEC_RES_OUTSIZE] = { JEDEC_RES, 0, 0, 0 };
+ 	uint32_t readaddr;
+ 	int ret;
++	int i;
+ 
+-	ret = spi_send_command(sizeof(cmd), JEDEC_RES_INSIZE, cmd, readarr);
++	ret = spi_send_command(sizeof(cmd), bytes, cmd, readarr);
+ 	if (ret == SPI_INVALID_ADDRESS) {
+ 		/* Find the lowest even address allowed for reads. */
+ 		readaddr = (spi_get_valid_read_addr() + 1) & ~1;
+ 		cmd[1] = (readaddr >> 16) & 0xff,
+ 		cmd[2] = (readaddr >> 8) & 0xff,
+ 		cmd[3] = (readaddr >> 0) & 0xff,
+-		ret = spi_send_command(sizeof(cmd), JEDEC_RES_INSIZE, cmd, readarr);
++		ret = spi_send_command(sizeof(cmd), bytes, cmd, readarr);
+ 	}
+ 	if (ret)
+ 		return ret;
+-	msg_cspew("RES returned %02x. ", readarr[0]);
++	msg_cspew("RES returned");
++	for (i = 0; i < bytes; i++)
++		msg_cspew(" 0x%02x", readarr[i]);
++	msg_cspew(". ");
+ 	return 0;
+ }
+ 
+@@ -122,7 +127,9 @@ static int probe_spi_rdid_generic(struct flashchip *flash, int bytes)
+ 	if (!oddparity(readarr[0]))
+ 		msg_cdbg("RDID byte 0 parity violation. ");
+ 
+-	/* Check if this is a continuation vendor ID */
++	/* Check if this is a continuation vendor ID.
++	 * FIXME: Handle continuation device IDs.
++	 */
+ 	if (readarr[0] == 0x7f) {
+ 		if (!oddparity(readarr[1]))
+ 			msg_cdbg("RDID byte 1 parity violation. ");
+@@ -166,33 +173,23 @@ int probe_spi_rdid(struct flashchip *flash)
+ 	return probe_spi_rdid_generic(flash, 3);
+ }
+ 
+-/* support 4 bytes flash ID */
+ int probe_spi_rdid4(struct flashchip *flash)
+ {
+-	/* only some SPI chipsets support 4 bytes commands */
++	/* Some SPI controllers do not support commands with writecnt=1 and
++	 * readcnt=4.
++	 */
+ 	switch (spi_controller) {
+-#if INTERNAL_SUPPORT == 1
+-	case SPI_CONTROLLER_ICH7:
+-	case SPI_CONTROLLER_ICH9:
+-	case SPI_CONTROLLER_VIA:
+-	case SPI_CONTROLLER_SB600:
++#if CONFIG_INTERNAL == 1
++#if defined(__i386__) || defined(__x86_64__)
++	case SPI_CONTROLLER_IT87XX:
+ 	case SPI_CONTROLLER_WBSIO:
++		msg_cinfo("4 byte RDID not supported on this SPI controller\n");
++		return 0;
++		break;
+ #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:
+-		msg_cinfo("4b ID not supported on this SPI controller\n");
++		return probe_spi_rdid_generic(flash, 4);
+ 	}
+ 
+ 	return 0;
+@@ -233,13 +230,15 @@ int probe_spi_rems(struct flashchip *flash)
+ 	return 0;
+ }
+ 
+-int probe_spi_res(struct flashchip *flash)
++int probe_spi_res1(struct flashchip *flash)
+ {
+ 	unsigned char readarr[3];
+ 	uint32_t id2;
+ 	const unsigned char allff[] = {0xff, 0xff, 0xff};
+ 	const unsigned char all00[] = {0x00, 0x00, 0x00};
+ 
++	/* We only want one-byte RES if RDID and REMS are unusable. */
++
+ 	/* Check if RDID is usable and does not return 0xff 0xff 0xff or
+ 	 * 0x00 0x00 0x00. In that case, RES is pointless.
+ 	 */
+@@ -257,12 +256,13 @@ int probe_spi_res(struct flashchip *flash)
+ 		return 0;
+ 	}
+ 
+-	if (spi_res(readarr))
++	if (spi_res(readarr, 1))
+ 		return 0;
+ 
+-	/* FIXME: Handle the case where RES gives a 2-byte response. */
+ 	id2 = readarr[0];
++
+ 	msg_cdbg("%s: id 0x%x\n", __func__, id2);
++
+ 	if (id2 != flash->model_id)
+ 		return 0;
+ 
+@@ -273,6 +273,29 @@ int probe_spi_res(struct flashchip *flash)
+ 	return 1;
+ }
+ 
++int probe_spi_res2(struct flashchip *flash)
++{
++	unsigned char readarr[2];
++	uint32_t id1, id2;
++
++	if (spi_res(readarr, 2))
++		return 0;
++
++	id1 = readarr[0];
++	id2 = readarr[1];
++
++	msg_cdbg("%s: id1 0x%x, id2 0x%x\n", __func__, id1, id2);
++
++	if (id1 != flash->manufacture_id || id2 != flash->model_id)
++		return 0;
++
++	/* Print the status register to tell the
++	 * user about possible write protection.
++	 */
++	spi_prettyprint_status_register(flash);
++	return 1;
++}
++
+ uint8_t spi_read_status_register(void)
+ {
+ 	const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { JEDEC_RDSR };
+@@ -289,7 +312,16 @@ uint8_t spi_read_status_register(void)
+ }
+ 
+ /* Prettyprint the status register. Common definitions. */
+-void spi_prettyprint_status_register_common(uint8_t status)
++static void spi_prettyprint_status_register_welwip(uint8_t status)
++{
++	msg_cdbg("Chip status register: Write Enable Latch (WEL) is "
++		     "%sset\n", (status & (1 << 1)) ? "" : "not ");
++	msg_cdbg("Chip status register: Write In Progress (WIP/BUSY) is "
++		     "%sset\n", (status & (1 << 0)) ? "" : "not ");
++}
++
++/* Prettyprint the status register. Common definitions. */
++static void spi_prettyprint_status_register_common(uint8_t status)
+ {
+ 	msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is "
+ 		     "%sset\n", (status & (1 << 5)) ? "" : "not ");
+@@ -299,10 +331,132 @@ void spi_prettyprint_status_register_common(uint8_t status)
+ 		     "%sset\n", (status & (1 << 3)) ? "" : "not ");
+ 	msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is "
+ 		     "%sset\n", (status & (1 << 2)) ? "" : "not ");
+-	msg_cdbg("Chip status register: Write Enable Latch (WEL) is "
+-		     "%sset\n", (status & (1 << 1)) ? "" : "not ");
+-	msg_cdbg("Chip status register: Write In Progress (WIP/BUSY) is "
+-		     "%sset\n", (status & (1 << 0)) ? "" : "not ");
++	spi_prettyprint_status_register_welwip(status);
++}
++
++/* Prettyprint the status register. Works for
++ * AMIC A25L series
++ */
++void spi_prettyprint_status_register_amic_a25l(uint8_t status)
++{
++	msg_cdbg("Chip status register: Status Register Write Disable "
++		     "(SRWD) is %sset\n", (status & (1 << 7)) ? "" : "not ");
++	spi_prettyprint_status_register_common(status);
++}
++
++/* Prettyprint the status register. Common definitions. */
++static void spi_prettyprint_status_register_at25_srplepewpp(uint8_t status)
++{
++	msg_cdbg("Chip status register: Sector Protection Register Lock (SRPL) "
++		 "is %sset\n", (status & (1 << 7)) ? "" : "not ");
++	msg_cdbg("Chip status register: Bit 6 "
++		 "is %sset\n", (status & (1 << 6)) ? "" : "not ");
++	msg_cdbg("Chip status register: Erase/Program Error (EPE) "
++		 "is %sset\n", (status & (1 << 5)) ? "" : "not ");
++	msg_cdbg("Chip status register: WP# pin (WPP) "
++		 "is %sactive\n", (status & (1 << 4)) ? "not " : "");
++}
++
++int spi_prettyprint_status_register_at25df(struct flashchip *flash)
++{
++	uint8_t status;
++
++	status = spi_read_status_register();
++	msg_cdbg("Chip status register is %02x\n", status);
++
++	spi_prettyprint_status_register_at25_srplepewpp(status);
++	msg_cdbg("Chip status register: Software Protection Status (SWP): ");
++	switch (status & (3 << 2)) {
++	case 0x0 << 2:
++		msg_cdbg("no sectors are protected\n");
++		break;
++	case 0x1 << 2:
++		msg_cdbg("some sectors are protected\n");
++		/* FIXME: Read individual Sector Protection Registers. */
++		break;
++	case 0x3 << 2:
++		msg_cdbg("all sectors are protected\n");
++		break;
++	default:
++		msg_cdbg("reserved for future use\n");
++		break;
++	}
++	spi_prettyprint_status_register_welwip(status);
++	return 0;
++}
++
++int spi_prettyprint_status_register_at25df_sec(struct flashchip *flash)
++{
++	/* FIXME: We should check the security lockdown. */
++	msg_cdbg("Ignoring security lockdown (if present)\n");
++	msg_cdbg("Ignoring status register byte 2\n");
++	return spi_prettyprint_status_register_at25df(flash);
++}
++
++int spi_prettyprint_status_register_at25f(struct flashchip *flash)
++{
++	uint8_t status;
++
++	status = spi_read_status_register();
++	msg_cdbg("Chip status register is %02x\n", status);
++
++	spi_prettyprint_status_register_at25_srplepewpp(status);
++	msg_cdbg("Chip status register: Bit 3 "
++		 "is %sset\n", (status & (1 << 3)) ? "" : "not ");
++	msg_cdbg("Chip status register: Block Protect 0 (BP0) is "
++		 "%sset, %s sectors are protected\n",
++		 (status & (1 << 2)) ? "" : "not ",
++		 (status & (1 << 2)) ? "all" : "no");
++	spi_prettyprint_status_register_welwip(status);
++	return 0;
++}
++
++int spi_prettyprint_status_register_at25fs010(struct flashchip *flash)
++{
++	uint8_t status;
++
++	status = spi_read_status_register();
++	msg_cdbg("Chip status register is %02x\n", status);
++
++	msg_cdbg("Chip status register: Status Register Write Protect (WPEN) "
++		 "is %sset\n", (status & (1 << 7)) ? "" : "not ");
++	msg_cdbg("Chip status register: Bit 6 / Block Protect 4 (BP4) is "
++		 "%sset\n", (status & (1 << 6)) ? "" : "not ");
++	msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is "
++		 "%sset\n", (status & (1 << 5)) ? "" : "not ");
++	msg_cdbg("Chip status register: Bit 4 is "
++		 "%sset\n", (status & (1 << 4)) ? "" : "not ");
++	msg_cdbg("Chip status register: Bit 3 / Block Protect 1 (BP1) is "
++		 "%sset\n", (status & (1 << 3)) ? "" : "not ");
++	msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is "
++		 "%sset\n", (status & (1 << 2)) ? "" : "not ");
++	/* FIXME: Pretty-print detailed sector protection status. */
++	spi_prettyprint_status_register_welwip(status);
++	return 0;
++}
++
++int spi_prettyprint_status_register_at25fs040(struct flashchip *flash)
++{
++	uint8_t status;
++
++	status = spi_read_status_register();
++	msg_cdbg("Chip status register is %02x\n", status);
++
++	msg_cdbg("Chip status register: Status Register Write Protect (WPEN) "
++		 "is %sset\n", (status & (1 << 7)) ? "" : "not ");
++	msg_cdbg("Chip status register: Bit 6 / Block Protect 4 (BP4) is "
++		 "%sset\n", (status & (1 << 6)) ? "" : "not ");
++	msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is "
++		 "%sset\n", (status & (1 << 5)) ? "" : "not ");
++	msg_cdbg("Chip status register: Bit 4 / Block Protect 2 (BP2) is "
++		 "%sset\n", (status & (1 << 4)) ? "" : "not ");
++	msg_cdbg("Chip status register: Bit 3 / Block Protect 1 (BP1) is "
++		 "%sset\n", (status & (1 << 3)) ? "" : "not ");
++	msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is "
++		 "%sset\n", (status & (1 << 2)) ? "" : "not ");
++	/* FIXME: Pretty-print detailed sector protection status. */
++	spi_prettyprint_status_register_welwip(status);
++	return 0;
+ }
+ 
+ /* Prettyprint the status register. Works for
+@@ -367,6 +521,10 @@ void spi_prettyprint_status_register(struct flashchip *flash)
+ 	status = spi_read_status_register();
+ 	msg_cdbg("Chip status register is %02x\n", status);
+ 	switch (flash->manufacture_id) {
++	case AMIC_ID:
++		if ((flash->model_id & 0xff00) == 0x2000)
++		    spi_prettyprint_status_register_amic_a25l(status);
++		break;
+ 	case ST_ID:
+ 		if (((flash->model_id & 0xff00) == 0x2000) ||
+ 		    ((flash->model_id & 0xff00) == 0x2500))
+@@ -414,12 +572,6 @@ int spi_chip_erase_60(struct flashchip *flash)
+ 		.readarr	= NULL,
+ 	}};
+ 	
+-	result = spi_disable_blockprotect();
+-	if (result) {
+-		msg_cerr("spi_disable_blockprotect failed\n");
+-		return result;
+-	}
+-	
+ 	result = spi_send_multicommand(cmds);
+ 	if (result) {
+ 		msg_cerr("%s failed during command execution\n",
+@@ -460,12 +612,6 @@ int spi_chip_erase_c7(struct flashchip *flash)
+ 		.readarr	= NULL,
+ 	}};
+ 
+-	result = spi_disable_blockprotect();
+-	if (result) {
+-		msg_cerr("spi_disable_blockprotect failed\n");
+-		return result;
+-	}
+-
+ 	result = spi_send_multicommand(cmds);
+ 	if (result) {
+ 		msg_cerr("%s failed during command execution\n", __func__);
+@@ -624,29 +770,6 @@ int spi_block_erase_d7(struct flashchip *flash, unsigned int addr, unsigned int
+ 	return 0;
+ }
+ 
+-int spi_chip_erase_d8(struct flashchip *flash)
+-{
+-	int i, rc = 0;
+-	int total_size = flash->total_size * 1024;
+-	int erase_size = 64 * 1024;
+-
+-	spi_disable_blockprotect();
+-
+-	msg_cinfo("Erasing chip: \n");
+-
+-	for (i = 0; i < total_size / erase_size; i++) {
+-		rc = spi_block_erase_d8(flash, i * erase_size, erase_size);
+-		if (rc) {
+-			msg_cerr("Error erasing block at 0x%x\n", i);
+-			break;
+-		}
+-	}
+-
+-	msg_cinfo("\n");
+-
+-	return rc;
+-}
+-
+ /* Sector size is usually 4k, though Macronix eliteflash has 64k */
+ int spi_block_erase_20(struct flashchip *flash, unsigned int addr, unsigned int blocklen)
+ {
+@@ -730,12 +853,12 @@ int spi_write_status_enable(void)
+  * This is according the SST25VF016 datasheet, who knows it is more
+  * generic that this...
+  */
+-int spi_write_status_register(int status)
++static int spi_write_status_register_ewsr(struct flashchip *flash, int status)
+ {
+ 	int result;
+ 	struct spi_command cmds[] = {
+ 	{
+-	/* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
++	/* WRSR requires either EWSR or WREN depending on chip type. */
+ 		.writecnt	= JEDEC_EWSR_OUTSIZE,
+ 		.writearr	= (const unsigned char[]){ JEDEC_EWSR },
+ 		.readcnt	= 0,
+@@ -757,9 +880,59 @@ int spi_write_status_register(int status)
+ 		msg_cerr("%s failed during command execution\n",
+ 			__func__);
+ 	}
++	/* WRSR performs a self-timed erase before the changes take effect. */
++	programmer_delay(100 * 1000);
+ 	return result;
+ }
+ 
++static int spi_write_status_register_wren(struct flashchip *flash, int status)
++{
++	int result;
++	struct spi_command cmds[] = {
++	{
++	/* WRSR requires either EWSR or WREN depending on chip type. */
++		.writecnt	= JEDEC_WREN_OUTSIZE,
++		.writearr	= (const unsigned char[]){ JEDEC_WREN },
++		.readcnt	= 0,
++		.readarr	= NULL,
++	}, {
++		.writecnt	= JEDEC_WRSR_OUTSIZE,
++		.writearr	= (const unsigned char[]){ JEDEC_WRSR, (unsigned char) status },
++		.readcnt	= 0,
++		.readarr	= NULL,
++	}, {
++		.writecnt	= 0,
++		.writearr	= NULL,
++		.readcnt	= 0,
++		.readarr	= NULL,
++	}};
++
++	result = spi_send_multicommand(cmds);
++	if (result) {
++		msg_cerr("%s failed during command execution\n",
++			__func__);
++	}
++	/* WRSR performs a self-timed erase before the changes take effect. */
++	programmer_delay(100 * 1000);
++	return result;
++}
++
++static int spi_write_status_register(struct flashchip *flash, int status)
++{
++	int ret = 1;
++
++	if (!(flash->feature_bits & (FEATURE_WRSR_WREN | FEATURE_WRSR_EWSR))) {
++		msg_cdbg("Missing status register write definition, assuming "
++			 "EWSR is needed\n");
++		flash->feature_bits |= FEATURE_WRSR_EWSR;
++	}
++	if (flash->feature_bits & FEATURE_WRSR_WREN)
++		ret = spi_write_status_register_wren(flash, status);
++	if (ret && (flash->feature_bits & FEATURE_WRSR_EWSR))
++		ret = spi_write_status_register_ewsr(flash, status);
++	return ret;
++}
++
+ int spi_byte_program(int addr, uint8_t databyte)
+ {
+ 	int result;
+@@ -842,20 +1015,152 @@ int spi_nbyte_program(int addr, uint8_t *bytes, int len)
+ 	return result;
+ }
+ 
+-int spi_disable_blockprotect(void)
++/* A generic brute-force block protection disable works like this:
++ * Write 0x00 to the status register. Check if any locks are still set (that
++ * part is chip specific). Repeat once.
++ */
++int spi_disable_blockprotect(struct flashchip *flash)
+ {
+ 	uint8_t status;
+ 	int result;
+ 
+ 	status = spi_read_status_register();
+-	/* If there is block protection in effect, unprotect it first. */
++	/* If block protection is disabled, stop here. */
++	if ((status & 0x3c) == 0)
++		return 0;
++
++	msg_cdbg("Some block protection in effect, disabling\n");
++	result = spi_write_status_register(flash, status & ~0x3c);
++	if (result) {
++		msg_cerr("spi_write_status_register failed\n");
++		return result;
++	}
++	status = spi_read_status_register();
+ 	if ((status & 0x3c) != 0) {
+-		msg_cdbg("Some block protection in effect, disabling\n");
+-		result = spi_write_status_register(status & ~0x3c);
++		msg_cerr("Block protection could not be disabled!\n");
++		return 1;
++	}
++	return 0;
++}
++
++int spi_disable_blockprotect_at25df(struct flashchip *flash)
++{
++	uint8_t status;
++	int result;
++
++	status = spi_read_status_register();
++	/* If block protection is disabled, stop here. */
++	if ((status & (3 << 2)) == 0)
++		return 0;
++
++	msg_cdbg("Some block protection in effect, disabling\n");
++	if (status & (1 << 7)) {
++		msg_cdbg("Need to disable Sector Protection Register Lock\n");
++		if ((status & (1 << 4)) == 0) {
++			msg_cerr("WP# pin is active, disabling "
++				 "write protection is impossible.\n");
++			return 1;
++		}
++		/* All bits except bit 7 (SPRL) are readonly. */
++		result = spi_write_status_register(flash, status & ~(1 << 7));
+ 		if (result) {
+ 			msg_cerr("spi_write_status_register failed\n");
+ 			return result;
+ 		}
++		
++	}
++	/* Global unprotect. Make sure to mask SPRL as well. */
++	result = spi_write_status_register(flash, status & ~0xbc);
++	if (result) {
++		msg_cerr("spi_write_status_register failed\n");
++		return result;
++	}
++	status = spi_read_status_register();
++	if ((status & (3 << 2)) != 0) {
++		msg_cerr("Block protection could not be disabled!\n");
++		return 1;
++	}
++	return 0;
++}
++
++int spi_disable_blockprotect_at25df_sec(struct flashchip *flash)
++{
++	/* FIXME: We should check the security lockdown. */
++	msg_cinfo("Ignoring security lockdown (if present)\n");
++	return spi_disable_blockprotect_at25df(flash);
++}
++
++int spi_disable_blockprotect_at25f(struct flashchip *flash)
++{
++	/* spi_disable_blockprotect_at25df is not really the right way to do
++	 * this, but the side effects of said function work here as well.
++	 */
++	return spi_disable_blockprotect_at25df(flash);
++}
++
++int spi_disable_blockprotect_at25fs010(struct flashchip *flash)
++{
++	uint8_t status;
++	int result;
++
++	status = spi_read_status_register();
++	/* If block protection is disabled, stop here. */
++	if ((status & 0x6c) == 0)
++		return 0;
++
++	msg_cdbg("Some block protection in effect, disabling\n");
++	if (status & (1 << 7)) {
++		msg_cdbg("Need to disable Status Register Write Protect\n");
++		/* Clear bit 7 (WPEN). */
++		result = spi_write_status_register(flash, status & ~(1 << 7));
++		if (result) {
++			msg_cerr("spi_write_status_register failed\n");
++			return result;
++		}
++	}
++	/* Global unprotect. Make sure to mask WPEN as well. */
++	result = spi_write_status_register(flash, status & ~0xec);
++	if (result) {
++		msg_cerr("spi_write_status_register failed\n");
++		return result;
++	}
++	status = spi_read_status_register();
++	if ((status & 0x6c) != 0) {
++		msg_cerr("Block protection could not be disabled!\n");
++		return 1;
++	}
++	return 0;
++}
++int spi_disable_blockprotect_at25fs040(struct flashchip *flash)
++{
++	uint8_t status;
++	int result;
++
++	status = spi_read_status_register();
++	/* If block protection is disabled, stop here. */
++	if ((status & 0x7c) == 0)
++		return 0;
++
++	msg_cdbg("Some block protection in effect, disabling\n");
++	if (status & (1 << 7)) {
++		msg_cdbg("Need to disable Status Register Write Protect\n");
++		/* Clear bit 7 (WPEN). */
++		result = spi_write_status_register(flash, status & ~(1 << 7));
++		if (result) {
++			msg_cerr("spi_write_status_register failed\n");
++			return result;
++		}
++	}
++	/* Global unprotect. Make sure to mask WPEN as well. */
++	result = spi_write_status_register(flash, status & ~0xfc);
++	if (result) {
++		msg_cerr("spi_write_status_register failed\n");
++		return result;
++	}
++	status = spi_read_status_register();
++	if ((status & 0x7c) != 0) {
++		msg_cerr("Block protection could not be disabled!\n");
++		return 1;
+ 	}
+ 	return 0;
+ }
+@@ -874,7 +1179,8 @@ int spi_nbyte_read(int address, uint8_t *bytes, int len)
+ }
+ 
+ /*
+- * Read a complete flash chip.
++ * Read a part of the flash chip.
++ * FIXME: Use the chunk code from Michael Karcher instead.
+  * Each page is read separately in chunks with a maximum size of chunksize.
+  */
+ int spi_read_chunked(struct flashchip *flash, uint8_t *buf, int start, int len, int chunksize)
+@@ -913,25 +1219,64 @@ int spi_read_chunked(struct flashchip *flash, uint8_t *buf, int start, int len,
+ }
+ 
+ /*
++ * Write a part of the flash chip.
++ * FIXME: Use the chunk code from Michael Karcher instead.
++ * Each page is written separately in chunks with a maximum size of chunksize.
++ */
++int spi_write_chunked(struct flashchip *flash, uint8_t *buf, int start, int len, int chunksize)
++{
++	int rc = 0;
++	int i, j, starthere, lenhere;
++	/* FIXME: page_size is the wrong variable. We need max_writechunk_size
++	 * in struct flashchip to do this properly. All chips using
++	 * spi_chip_write_256 have page_size set to max_writechunk_size, so
++	 * we're OK for now.
++	 */
++	int page_size = flash->page_size;
++	int towrite;
++
++	/* Warning: This loop has a very unusual condition and body.
++	 * The loop needs to go through each page with at least one affected
++	 * byte. The lowest page number is (start / page_size) since that
++	 * division rounds down. The highest page number we want is the page
++	 * where the last byte of the range lives. That last byte has the
++	 * address (start + len - 1), thus the highest page number is
++	 * (start + len - 1) / page_size. Since we want to include that last
++	 * page as well, the loop condition uses <=.
++	 */
++	for (i = start / page_size; i <= (start + len - 1) / page_size; i++) {
++		/* Byte position of the first byte in the range in this page. */
++		/* starthere is an offset to the base address of the chip. */
++		starthere = max(start, i * page_size);
++		/* Length of bytes in the range in this page. */
++		lenhere = min(start + len, (i + 1) * page_size) - starthere;
++		for (j = 0; j < lenhere; j += chunksize) {
++			towrite = min(chunksize, lenhere - j);
++			rc = spi_nbyte_program(starthere + j, buf + starthere - start + j, towrite);
++			if (rc)
++				break;
++			while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP)
++				programmer_delay(10);
++		}
++		if (rc)
++			break;
++	}
++
++	return rc;
++}
++
++/*
+  * Program chip using byte programming. (SLOW!)
+  * This is for chips which can only handle one byte writes
+  * and for chips where memory mapped programming is impossible
+  * (e.g. due to size constraints in IT87* for over 512 kB)
+  */
+-int spi_chip_write_1(struct flashchip *flash, uint8_t *buf)
++/* real chunksize is 1, logical chunksize is 1 */
++int spi_chip_write_1_new(struct flashchip *flash, uint8_t *buf, int start, int len)
+ {
+-	int total_size = 1024 * flash->total_size;
+ 	int i, result = 0;
+ 
+-	spi_disable_blockprotect();
+-	/* Erase first */
+-	msg_cinfo("Erasing flash before programming... ");
+-	if (erase_flash(flash)) {
+-		msg_cerr("ERASE FAILED!\n");
+-		return -1;
+-	}
+-	msg_cinfo("done.\n");
+-	for (i = 0; i < total_size; i++) {
++	for (i = start; i < start + len; i++) {
+ 		result = spi_byte_program(i, buf[i]);
+ 		if (result)
+ 			return 1;
+@@ -942,40 +1287,107 @@ int spi_chip_write_1(struct flashchip *flash, uint8_t *buf)
+ 	return 0;
+ }
+ 
+-int spi_aai_write(struct flashchip *flash, uint8_t *buf)
++int spi_chip_write_1(struct flashchip *flash, uint8_t *buf)
+ {
+-	uint32_t pos = 2, size = flash->total_size * 1024;
+-	unsigned char w[6] = {0xad, 0, 0, 0, buf[0], buf[1]};
++	/* Erase first */
++	msg_cinfo("Erasing flash before programming... ");
++	if (erase_flash(flash)) {
++		msg_cerr("ERASE FAILED!\n");
++		return -1;
++	}
++	msg_cinfo("done.\n");
++
++	return spi_chip_write_1_new(flash, buf, 0, flash->total_size * 1024);
++}
++
++int spi_aai_write(struct flashchip *flash, uint8_t *buf, int start, int len)
++{
++	uint32_t pos = start;
+ 	int result;
++	unsigned char cmd[JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE] = {
++		JEDEC_AAI_WORD_PROGRAM,
++	};
++	struct spi_command cmds[] = {
++	{
++		.writecnt	= JEDEC_WREN_OUTSIZE,
++		.writearr	= (const unsigned char[]){ JEDEC_WREN },
++		.readcnt	= 0,
++		.readarr	= NULL,
++	}, {
++		.writecnt	= JEDEC_AAI_WORD_PROGRAM_OUTSIZE,
++		.writearr	= (const unsigned char[]){
++					JEDEC_AAI_WORD_PROGRAM,
++					(start >> 16) & 0xff,
++					(start >> 8) & 0xff,
++					(start & 0xff),
++					buf[0],
++					buf[1]
++				},
++		.readcnt	= 0,
++		.readarr	= NULL,
++	}, {
++		.writecnt	= 0,
++		.writearr	= NULL,
++		.readcnt	= 0,
++		.readarr	= NULL,
++	}};
+ 
+ 	switch (spi_controller) {
+-#if INTERNAL_SUPPORT == 1
++#if CONFIG_INTERNAL == 1
++#if defined(__i386__) || defined(__x86_64__)
++	case SPI_CONTROLLER_IT87XX:
+ 	case SPI_CONTROLLER_WBSIO:
+-		msg_cerr("%s: impossible with Winbond SPI masters,"
++		msg_perr("%s: impossible with this SPI controller,"
+ 				" degrading to byte program\n", __func__);
+-		return spi_chip_write_1(flash, buf);
++		return spi_chip_write_1_new(flash, buf, start, len);
++#endif
+ #endif
+ 	default:
+ 		break;
+ 	}
+-	if (erase_flash(flash)) {
+-		msg_cerr("ERASE FAILED!\n");
+-		return -1;
++
++	/* The even start address and even length requirements can be either
++	 * honored outside this function, or we can call spi_byte_program
++	 * for the first and/or last byte and use AAI for the rest.
++	 */
++	/* The data sheet requires a start address with the low bit cleared. */
++	if (start % 2) {
++		msg_cerr("%s: start address not even! Please report a bug at "
++			 "flashrom at flashrom.org\n", __func__);
++		return SPI_GENERIC_ERROR;
+ 	}
+-	/* FIXME: This will fail on ICH/VIA SPI. */
+-	result = spi_write_enable();
+-	if (result)
++	/* The data sheet requires total AAI write length to be even. */
++	if (len % 2) {
++		msg_cerr("%s: total write length not even! Please report a "
++			 "bug at flashrom at flashrom.org\n", __func__);
++		return SPI_GENERIC_ERROR;
++	}
++
++
++	result = spi_send_multicommand(cmds);
++	if (result) {
++		msg_cerr("%s failed during start command execution\n",
++			 __func__);
++		/* FIXME: Should we send WRDI here as well to make sure the chip
++		 * is not in AAI mode?
++		 */
+ 		return result;
+-	spi_send_command(6, 0, w, NULL);
++	}
+ 	while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP)
+-		programmer_delay(5); /* SST25VF040B Tbp is max 10us */
+-	while (pos < size) {
+-		w[1] = buf[pos++];
+-		w[2] = buf[pos++];
+-		spi_send_command(3, 0, w, NULL);
++		programmer_delay(10);
++
++	/* We already wrote 2 bytes in the multicommand step. */
++	pos += 2;
++
++	while (pos < start + len) {
++		cmd[1] = buf[pos++];
++		cmd[2] = buf[pos++];
++		spi_send_command(JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE, 0, cmd, NULL);
+ 		while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP)
+-			programmer_delay(5); /* SST25VF040B Tbp is max 10us */
++			programmer_delay(10);
+ 	}
++
++	/* Use WRDI to exit AAI mode. */
+ 	spi_write_disable();
+ 	return 0;
+ }
+diff --git a/sst28sf040.c b/sst28sf040.c
+index b9e33ab..c5f27a4 100644
+--- a/sst28sf040.c
++++ b/sst28sf040.c
+@@ -69,7 +69,7 @@ int erase_sector_28sf040(struct flashchip *flash, unsigned int address, unsigned
+ 	return 0;
+ }
+ 
+-int write_sector_28sf040(chipaddr bios, uint8_t *src, chipaddr dst,
++static int write_sector_28sf040(chipaddr bios, uint8_t *src, chipaddr dst,
+ 				unsigned int page_size)
+ {
+ 	int i;
+@@ -92,7 +92,7 @@ int write_sector_28sf040(chipaddr bios, uint8_t *src, chipaddr dst,
+ 	return 0;
+ }
+ 
+-int erase_28sf040(struct flashchip *flash)
++static int erase_28sf040(struct flashchip *flash)
+ {
+ 	chipaddr bios = flash->virtual_memory;
+ 
+diff --git a/sst49lfxxxc.c b/sst49lfxxxc.c
+index 1331cda..2f14b7a 100644
+--- a/sst49lfxxxc.c
++++ b/sst49lfxxxc.c
+@@ -20,11 +20,10 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
+-#include <stdlib.h>
+ #include "flash.h"
+ #include "chipdrivers.h"
+ 
+-int unlock_block_49lfxxxc(struct flashchip *flash, unsigned long address, unsigned char bits)
++static int write_lockbits_block_49lfxxxc(struct flashchip *flash, unsigned long address, unsigned char bits)
+ {
+ 	unsigned long lock = flash->virtual_registers + address + 2;
+ 	msg_cdbg("lockbits at address=0x%08lx is 0x%01x\n", lock, chip_readb(lock));
+@@ -41,31 +40,16 @@ static int write_lockbits_49lfxxxc(struct flashchip *flash, unsigned char bits)
+ 
+ 	msg_cdbg("\nbios=0x%08lx\n", registers);
+ 	for (i = 0; left > 65536; i++, left -= 65536) {
+-		msg_cdbg("lockbits at address=0x%08lx is 0x%01x\n",
+-			     registers + (i * 65536) + 2,
+-			     chip_readb(registers + (i * 65536) + 2));
+-		chip_writeb(bits, registers + (i * 65536) + 2);
++		write_lockbits_block_49lfxxxc(flash, i * 65536, bits);
+ 	}
+ 	address = i * 65536;
+-	msg_cdbg("lockbits at address=0x%08lx is 0x%01x\n",
+-		     registers + address + 2,
+-		     chip_readb(registers + address + 2));
+-	chip_writeb(bits, registers + address + 2);
++	write_lockbits_block_49lfxxxc(flash, address, bits);
+ 	address += 32768;
+-	msg_cdbg("lockbits at address=0x%08lx is 0x%01x\n",
+-		     registers + address + 2,
+-		     chip_readb(registers + address + 2));
+-	chip_writeb(bits, registers + address + 2);
++	write_lockbits_block_49lfxxxc(flash, address, bits);
+ 	address += 8192;
+-	msg_cdbg("lockbits at address=0x%08lx is 0x%01x\n",
+-		     registers + address + 2,
+-		     chip_readb(registers + address + 2));
+-	chip_writeb(bits, registers + address + 2);
++	write_lockbits_block_49lfxxxc(flash, address, bits);
+ 	address += 8192;
+-	msg_cdbg("lockbits at address=0x%08lx is 0x%01x\n",
+-		     registers + address + 2,
+-		     chip_readb(registers + address + 2));
+-	chip_writeb(bits, registers + address + 2);
++	write_lockbits_block_49lfxxxc(flash, address, bits);
+ 
+ 	return 0;
+ }
+diff --git a/sst_fwhub.c b/sst_fwhub.c
+index 2b867c8..65bd2b3 100644
+--- a/sst_fwhub.c
++++ b/sst_fwhub.c
+@@ -22,12 +22,10 @@
+ 
+ /* Adapted from the Intel FW hub stuff for 82802ax parts. */
+ 
+-#include <stdlib.h>
+-#include <string.h>
+ #include "flash.h"
+ #include "chipdrivers.h"
+ 
+-int check_sst_fwhub_block_lock(struct flashchip *flash, int offset)
++static int check_sst_fwhub_block_lock(struct flashchip *flash, int offset)
+ {
+ 	chipaddr registers = flash->virtual_registers;
+ 	uint8_t blockstatus;
+@@ -53,7 +51,7 @@ int check_sst_fwhub_block_lock(struct flashchip *flash, int offset)
+ 	return blockstatus & 0x1;
+ }
+ 
+-int clear_sst_fwhub_block_lock(struct flashchip *flash, int offset)
++static int clear_sst_fwhub_block_lock(struct flashchip *flash, int offset)
+ {
+ 	chipaddr registers = flash->virtual_registers;
+ 	uint8_t blockstatus;
+diff --git a/stm50flw0x0x.c b/stm50flw0x0x.c
+index 3539395..6a3b17d 100644
+--- a/stm50flw0x0x.c
++++ b/stm50flw0x0x.c
+@@ -27,8 +27,6 @@
+  * ST M50FLW080B (not yet tested)
+  */
+ 
+-#include <string.h>
+-#include <stdlib.h>
+ #include "flash.h"
+ #include "flashchips.h"
+ #include "chipdrivers.h"
+@@ -38,7 +36,7 @@
+  * The ST M50FLW080B and STM50FLW080B chips have to be unlocked,
+  * before you can erase them or write to them.
+  */
+-int unlock_block_stm50flw0x0x(struct flashchip *flash, int offset)
++static int unlock_block_stm50flw0x0x(struct flashchip *flash, int offset)
+ {
+ 	chipaddr wrprotect = flash->virtual_registers + 2;
+ 	const uint8_t unlock_sector = 0x00;
+diff --git a/udelay.c b/udelay.c
+index bae6467..981b1bb 100644
+--- a/udelay.c
++++ b/udelay.c
+@@ -19,13 +19,14 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
++#include <unistd.h>
+ #include <sys/time.h>
+ #include <stdlib.h>
+ #include <limits.h>
+ #include "flash.h"
+ 
+ /* loops per microsecond */
+-unsigned long micro = 1;
++static unsigned long micro = 1;
+ 
+ __attribute__ ((noinline)) void myusec_delay(int usecs)
+ {
+@@ -36,7 +37,31 @@ __attribute__ ((noinline)) void myusec_delay(int usecs)
+ 	}
+ }
+ 
+-unsigned long measure_delay(int usecs)
++static unsigned long measure_os_delay_resolution(void)
++{
++	unsigned long timeusec;
++	struct timeval start, end;
++	unsigned long counter = 0;
++	
++	gettimeofday(&start, 0);
++	timeusec = 0;
++	
++	while (!timeusec && (++counter < 1000000000)) {
++		gettimeofday(&end, 0);
++		timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
++			   (end.tv_usec - start.tv_usec);
++		/* Protect against time going forward too much. */
++		if ((end.tv_sec > start.tv_sec) &&
++		    ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1))
++			timeusec = 0;
++		/* Protect against time going backwards during leap seconds. */
++		if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
++			timeusec = 0;
++	}
++	return timeusec;
++}
++
++static unsigned long measure_delay(int usecs)
+ {
+ 	unsigned long timeusec;
+ 	struct timeval start, end;
+@@ -60,10 +85,16 @@ unsigned long measure_delay(int usecs)
+ void myusec_calibrate_delay(void)
+ {
+ 	unsigned long count = 1000;
+-	unsigned long timeusec;
++	unsigned long timeusec, resolution;
+ 	int i, tries = 0;
+ 
+-	printf("Calibrating delay loop... ");
++	msg_pinfo("Calibrating delay loop... ");
++	resolution = measure_os_delay_resolution();
++	if (resolution) {
++		msg_pdbg("OS timer resolution is %lu usecs, ", resolution);
++	} else {
++		msg_pinfo("OS timer resolution is unusable. ");
++	}
+ 
+ recalibrate:
+ 	count = 1000;
+@@ -93,9 +124,27 @@ recalibrate:
+ 		 * a scheduler delay or something similar.
+ 		 */
+ 		for (i = 0; i < 4; i++) {
+-			if (measure_delay(100) < 90) {
+-				msg_pdbg("delay more than 10%% too short, "
+-					 "recalculating... ");
++			if (resolution && (resolution < 10)) {
++				timeusec = measure_delay(100);
++			} else if (resolution && 
++				   (resolution < ULONG_MAX / 200)) {
++				timeusec = measure_delay(resolution * 10) *
++					   100 / (resolution * 10);
++			} else {
++				/* This workaround should be active for broken
++				 * OS and maybe libpayload. The criterion
++				 * here is horrible or non-measurable OS timer
++				 * resolution which will result in
++				 * measure_delay(100)=0 whereas a longer delay
++				 * (1000 ms) may be sufficient
++				 * to get a nonzero time measurement.
++				 */
++				timeusec = measure_delay(1000000) / 10000;
++			}
++			if (timeusec < 90) {
++				msg_pdbg("delay more than 10%% too short (got "
++					 "%lu%% of expected delay), "
++					 "recalculating... ", timeusec);
+ 				goto recalibrate;
+ 			}
+ 		}
+@@ -112,8 +161,10 @@ recalibrate:
+ 	msg_pdbg("1000 myus = %ld us, ", timeusec);
+ 	timeusec = measure_delay(10000);
+ 	msg_pdbg("10000 myus = %ld us, ", timeusec);
++	timeusec = measure_delay(resolution * 4);
++	msg_pdbg("%ld myus = %ld us, ", resolution * 4, timeusec);
+ 
+-	printf("OK.\n");
++	msg_pinfo("OK.\n");
+ }
+ 
+ void internal_delay(int usecs)
+diff --git a/util/z60_flashrom.rules b/util/z60_flashrom.rules
+new file mode 100644
+index 0000000..18177f7
+--- /dev/null
++++ b/util/z60_flashrom.rules
+@@ -0,0 +1,55 @@
++##
++## This file is part of the flashrom project.
++##
++## Copyright (C) 2010 Uwe Hermann <uwe at hermann-uwe.de>
++##
++## 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
++##
++
++##
++## Please keep this list sorted alphabetically by vendor/device name.
++##
++
++##
++## This is tested on udev version 154, other versions may need small fixes.
++##
++## Note that you might want to change the "plugdev" group to whatever is
++## suitable for your respective distribution.
++##
++
++ACTION!="add|change", GOTO="flashrom_rules_end"
++SUBSYSTEM!="usb|usb_device", GOTO="flashrom_rules_end"
++
++# Amontec JTAGkey(2)
++# http://www.amontec.com/jtagkey.shtml
++ATTRS{idVendor}=="0403", ATTRS{idProduct}=="cff8", MODE="664", GROUP="plugdev"
++
++# Buspirate
++# http://dangerousprototypes.com/2009/10/08/bus-pirate-raw-spi-mode/
++ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="664", GROUP="plugdev"
++
++# Dediprog SF100
++# http://www.dediprog.com/SPI-flash-in-circuit-programming/SF100
++ATTRS{idVendor}=="0483", ATTRS{idProduct}=="dada", MODE="664", GROUP="plugdev"
++
++# DLP Design DLP-USB1232H
++# http://www.dlpdesign.com/usb/usb1232h.shtml
++ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", MODE="664", GROUP="plugdev"
++
++# FTDI FT4232H Mini-Module
++# http://www.ftdichip.com/Products/EvaluationKits/FT4232H_MiniModule.htm
++ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", MODE="664", GROUP="plugdev"
++
++LABEL="flashrom_rules_end"
+diff --git a/w29ee011.c b/w29ee011.c
+index 6b88a1c..4fc8853 100644
+--- a/w29ee011.c
++++ b/w29ee011.c
+@@ -26,7 +26,6 @@ int probe_w29ee011(struct flashchip *flash)
+ {
+ 	chipaddr bios = flash->virtual_memory;
+ 	uint8_t id1, id2;
+-	extern char *chip_to_probe;
+ 
+ 	if (!chip_to_probe || strcmp(chip_to_probe, "W29EE011")) {
+ 		msg_cdbg("Probing disabled for Winbond W29EE011 because "
+diff --git a/wbsio_spi.c b/wbsio_spi.c
+index ca39322..acf9cb2 100644
+--- a/wbsio_spi.c
++++ b/wbsio_spi.c
+@@ -18,9 +18,11 @@
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+  */
+ 
+-#include <string.h>
++#if defined(__i386__) || defined(__x86_64__)
++
+ #include "flash.h"
+ #include "chipdrivers.h"
++#include "programmer.h"
+ #include "spi.h"
+ 
+ #define WBSIO_PORT1	0x2e
+@@ -58,7 +60,7 @@ done:
+ 	return flashport;
+ }
+ 
+-int wbsio_check_for_spi(const char *name)
++int wbsio_check_for_spi(void)
+ {
+ 	if (0 == (wbsio_spibase = wbsio_get_spibase(WBSIO_PORT1)))
+ 		if (0 == (wbsio_spibase = wbsio_get_spibase(WBSIO_PORT2)))
+@@ -68,6 +70,9 @@ int wbsio_check_for_spi(const char *name)
+ 
+ 	buses_supported |= CHIP_BUSTYPE_SPI;
+ 	spi_controller = SPI_CONTROLLER_WBSIO;
++	msg_pdbg("%s: Winbond saved on 4 register bits so max chip size is "
++		 "1024 KB!\n", __func__);
++	max_rom_decode.spi = 1024 * 1024;
+ 
+ 	return 0;
+ }
+@@ -178,24 +183,7 @@ 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 size = flash->total_size * 1024;
+-
+-	if (size > 1024 * 1024) {
+-		msg_perr("%s: Winbond saved on 4 register bits so max chip size is 1024 KB!\n", __func__);
+-		return 1;
+-	}
+-
+ 	return read_memmapped(flash, buf, start, len);
+ }
+ 
+-int wbsio_spi_write_1(struct flashchip *flash, uint8_t *buf)
+-{
+-	int size = flash->total_size * 1024;
+-
+-	if (size > 1024 * 1024) {
+-		msg_perr("%s: Winbond saved on 4 register bits so max chip size is 1024 KB!\n", __func__);
+-		return 1;
+-	}
+-
+-	return spi_chip_write_1(flash, buf);
+-}
++#endif
diff --git a/flashrom-svn_ver.diff b/flashrom-svn_ver.diff
new file mode 100644
index 0000000..705db6c
--- /dev/null
+++ b/flashrom-svn_ver.diff
@@ -0,0 +1,11 @@
+--- Makefile~	2010-09-12 18:42:42.750110259 +0400
++++ Makefile	2010-09-12 18:57:47.557609365 +0400
+@@ -97,7 +97,7 @@
+ # 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 := 1001
++SVNVERSION := 1158
+ 
+ RELEASE := 0.9.2
+ VERSION := $(RELEASE)-r$(SVNVERSION)
diff --git a/flashrom.spec b/flashrom.spec
index 69c036d..b048c08 100644
--- a/flashrom.spec
+++ b/flashrom.spec
@@ -1,17 +1,20 @@
-%global svnrev 1001
+%global svnrev 1158
 
 Summary:	Simple program for reading/writing BIOS chips content
 Name:		flashrom
 Version:	0.9.2
-Release:	2%{?dist}
+Release:	3.svn1158%{?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-r%{svnrev}.diff
+Patch0:		flashrom-r1002-r%{svnrev}.diff
+Patch1:		flashrom-svn_ver.diff
+Patch2:		flashrom-0001-Define-dmidecode-path-at-a-build-stage.patch
 BuildRequires:	pciutils-devel
 BuildRequires:	zlib-devel
+BuildRequires:	dmidecode
 Requires:	dmidecode
 BuildRoot:	%{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
@@ -24,17 +27,19 @@ and write new contents on the chips ("flash the chip").
 
 %prep
 %setup -q
-#%patch0 -p1
+%patch0 -p1
+%patch1 -p0
+%patch2 -p1
 
 %build
-CFLAGS="%{optflags}" %{__make} %{?_smp_mflags}
+CFLAGS="%{optflags}" make %{?_smp_mflags}
 
 %install
-%{__rm} -rf $RPM_BUILD_ROOT
-%{__make} install DESTDIR=$RPM_BUILD_ROOT PREFIX=%{_prefix}
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT PREFIX=%{_prefix}
 
 %clean
-%{__rm} -rf $RPM_BUILD_ROOT
+rm -rf $RPM_BUILD_ROOT
 
 %files
 %defattr(-,root,root,-)
@@ -43,6 +48,11 @@ CFLAGS="%{optflags}" %{__make} %{?_smp_mflags}
 %{_mandir}/man8/%{name}.*
 
 %changelog
+* Sun Sep 12 2010 Peter Lemenkov <lemenkov at gmail.com> 0.9.2-3.svn1158
+- Clean up spec-file
+- Updated to latest svn ver. 1158
+- Doubles the number of known boards!
+
 * Sun Jun 13 2010 Peter Lemenkov <lemenkov at gmail.com> 0.9.2-2
 - Added missing Requires - dmidecode (for accurate board matching)
 


More information about the scm-commits mailing list