Merge remote branch 'cros/master' into do_merge_R16-1193.B

chipset_enable.c needed CHIP_BUSTYPE_SPI converted to BUS_SPI
ichspi.c had some stale hardware sequencing code

Conflicts:
	chipset_enable.c
	ichspi.c

Change-Id: Id964ba8837fad285cdf577e115fe2451af7343c8
diff --git a/82802ab.c b/82802ab.c
index 2580706..8f671c9 100644
--- a/82802ab.c
+++ b/82802ab.c
@@ -29,7 +29,6 @@
 #include "flash.h"
 #include "chipdrivers.h"
 
-// I need that Berkeley bit-map printer
 void print_status_82802ab(uint8_t status)
 {
 	msg_cdbg("%s", status & 0x80 ? "Ready:" : "Busy:");
@@ -44,8 +43,7 @@
 int probe_82802ab(struct flashchip *flash)
 {
 	chipaddr bios = flash->virtual_memory;
-	uint8_t id1, id2;
-	uint8_t flashcontent1, flashcontent2;
+	uint8_t id1, id2, flashcontent1, flashcontent2;
 	int shifted = (flash->feature_bits & FEATURE_ADDR_SHIFTED) != 0;
 
 	/* Reset to get a clean state */
@@ -69,7 +67,10 @@
 	if (!oddparity(id1))
 		msg_cdbg(", id1 parity violation");
 
-	/* Read the product ID location again. We should now see normal flash contents. */
+	/*
+	 * Read the product ID location again. We should now see normal
+	 * flash contents.
+	 */
 	flashcontent1 = chip_readb(bios + (0x00 << shifted));
 	flashcontent2 = chip_readb(bios + (0x01 << shifted));
 
@@ -112,14 +113,13 @@
 	//chipaddr wrprotect = flash->virtual_registers + page + 2;
 
 	for (i = 0; i < flash->total_size * 1024; i+= flash->page_size)
-	{
 		chip_writeb(0, flash->virtual_registers + i + 2);
-	}
 
 	return 0;
 }
 
-int erase_block_82802ab(struct flashchip *flash, unsigned int page, unsigned int pagesize)
+int erase_block_82802ab(struct flashchip *flash, unsigned int page,
+			unsigned int pagesize)
 {
 	chipaddr bios = flash->virtual_memory;
 	uint8_t status;
@@ -141,7 +141,7 @@
 }
 
 /* chunksize is 1 */
-int write_82802ab(struct flashchip *flash, uint8_t *src, int start, int len)
+int write_82802ab(struct flashchip *flash, uint8_t *src, unsigned int start, unsigned int len)
 {
 	int i;
 	chipaddr dst = flash->virtual_memory + start;
@@ -178,7 +178,7 @@
 		msg_cdbg("unlocked!\n");
 		can_unlock = 1;
 	}
-	
+
 	/* Read block lock-bits */
 	for (i = 0; i < flash->total_size * 1024; i+= (64 * 1024)) {
 		bcfg = chip_readb(bios + i + 2); // read block lock config
@@ -208,3 +208,58 @@
 
 	return 0;
 }
+
+int unlock_lh28f008bjt(struct flashchip *flash)
+{
+	chipaddr bios = flash->virtual_memory;
+	uint8_t mcfg, bcfg;
+	uint8_t need_unlock = 0, can_unlock = 0;
+	int i;
+
+	/* Wait if chip is busy */
+	wait_82802ab(flash);
+
+	/* Read identifier codes */
+	chip_writeb(0x90, bios);
+
+	/* Read master lock-bit */
+	mcfg = chip_readb(bios + 0x3);
+	msg_cdbg("master lock is ");
+	if (mcfg) {
+		msg_cdbg("locked!\n");
+	} else {
+		msg_cdbg("unlocked!\n");
+		can_unlock = 1;
+	}
+
+	/* Read block lock-bits, 8 * 8 KB + 15 * 64 KB */
+	for (i = 0; i < flash->total_size * 1024;
+	     i += (i >= (64 * 1024) ? 64 * 1024 : 8 * 1024)) {
+		bcfg = chip_readb(bios + i + 2); /* read block lock config */
+		msg_cdbg("block lock at %06x is %slocked!\n", i,
+			 bcfg ? "" : "un");
+		if (bcfg)
+			need_unlock = 1;
+	}
+
+	/* Reset chip */
+	chip_writeb(0xFF, bios);
+
+	/* Unlock: clear block lock-bits, if needed */
+	if (can_unlock && need_unlock) {
+		msg_cdbg("Unlock: ");
+		chip_writeb(0x60, bios);
+		chip_writeb(0xD0, bios);
+		chip_writeb(0xFF, bios);
+		wait_82802ab(flash);
+		msg_cdbg("Done!\n");
+	}
+
+	/* Error: master locked or a block is locked */
+	if (!can_unlock && need_unlock) {
+		msg_cerr("At least one block is locked and lockdown is active!\n");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/Documentation/mysteries_intel.txt b/Documentation/mysteries_intel.txt
new file mode 100644
index 0000000..d6d3dfb
--- /dev/null
+++ b/Documentation/mysteries_intel.txt
@@ -0,0 +1,18 @@
+= BBAR on ICH8 =
+ There is no sign of BBAR (BIOS Base Address Configuration Register) in the
+ public datasheet (or specification update) of the ICH8. Also, the offset of
+ that register has changed between ICH7 (SPIBAR + 50h) and ICH9 (SPIBAR +
+ A0h), so we have no clue if or where it is on ICH8. Out current policy is to
+ not touch it at all and assume/hope it is 0.
+
+= Accesses beyond region bounds in descriptor mode =
+ Intel's flash image tool will always expand the last region so that it covers
+ the whole flash chip, but some boards ship with a different configuration.
+ It seems that in descriptor mode all addresses outside the used regions can not
+ be accessed whatsoever. This is not specified anywhere publicly as far as we
+ could tell. flashrom does not handle this explicitly yet. It will just fail
+ when trying to touch an address outside of any region.
+ See also http://www.flashrom.org/pipermail/flashrom/2011-August/007606.html
+
+= Unlocking the ME region =
+TODO
diff --git a/serprog-protocol.txt b/Documentation/serprog-protocol.txt
similarity index 90%
rename from serprog-protocol.txt
rename to Documentation/serprog-protocol.txt
index 52f901a..a3a4863 100644
--- a/serprog-protocol.txt
+++ b/Documentation/serprog-protocol.txt
@@ -19,7 +19,7 @@
 0x05	Query supported bustypes	none				ACK + 8-bit flags (as per flashrom) / NAK
 0x06	Query connected address lines	none				ACK + 8bit line count / NAK
 0x07	Query operation buffer size	none				ACK + 16bit size / NAK
-0x08	Query write-n maximum data len	none				ACK + 24bit maximum length / NAK
+0x08	Query maximum write-n length	none				ACK + 24bit length (0==2^24) / NAK
 0x09	Read byte			24-bit addr			ACK + BYTE / NAK
 0x0A	Read n bytes			24-bit addr + 24-bit length	ACK + length bytes / NAK
 0x0B	Initialize operation buffer	none				ACK / NAK
@@ -31,6 +31,8 @@
 0x10	Sync NOP			none				NAK + ACK (for synchronization)
 0x11	Query maximum read-n length	none				ACK + 24-bit length (0==2^24) / NAK
 0x12	Set used bustype		8-bit flags (as with 0x05)	ACK / NAK
+0x13	Perform SPI operation		24-bit slen + 24-bit rlen	ACK + rlen bytes of data / NAK
+					 + slen bytes of data
 0x??	unimplemented command - invalid.
 
 
@@ -50,7 +52,7 @@
 		it should return a big bogus value - eg 0xFFFF.
 	0x05 (Q_BUSTYPE):
 		The bit's are defined as follows:
-		bit 0: PARALLEL, bit 1: LPC, bit 2: FWH, bit 3: SPI (if ever supported).
+		bit 0: PARALLEL, bit 1: LPC, bit 2: FWH, bit 3: SPI.
 	0x06 (Q_CHIPSIZE):
 		Only applicable to parallel programmers.
 		An LPC/FWH/SPI-programmer can report this as not supported in the command bitmap.
@@ -66,6 +68,11 @@
 		Set's the used bustype if the programmer can support more than one flash protocol.
 		Sending a byte with more than 1 bit set will make the programmer decide among them
 		on it's own. Bit values as with Q_BUSTYPE.
+	0x13 (O_SPIOP):
+		Send and receive bytes via SPI.
+		Maximum slen is Q_WRNMAXLEN in case Q_BUSTYPE returns SPI only or S_BUSTYPE was used
+		to set SPI exclusively before. Same for rlen and Q_RDNMAXLEN.
+		This operation is immediate, meaning it doesnt use the operation buffer.
 	About mandatory commands:
 		The only truly mandatory commands for any device are 0x00, 0x01, 0x02 and 0x10,
 		but one can't really do anything with these commands.
@@ -99,3 +106,4 @@
 #define S_CMD_SYNCNOP		0x10		/* Special no-operation that returns NAK+ACK	*/
 #define S_CMD_Q_RDNMAXLEN	0x11		/* Query read-n maximum length			*/
 #define S_CMD_S_BUSTYPE		0x12		/* Set used bustype(s).				*/
+#define S_CMD_O_SPIOP		0x13		/* Perform SPI operation.			*/
diff --git a/Makefile b/Makefile
index 531bf6a..dc69924 100644
--- a/Makefile
+++ b/Makefile
@@ -37,9 +37,6 @@
 CFLAGS += -Werror
 endif
 
-# Determine the destination processor architecture
-override ARCH = $(strip $(shell LC_ALL=C $(CC) -E arch.h|grep -v '^\#'))
-
 # FIXME We have to differentiate between host and target OS architecture.
 OS_ARCH	?= $(shell uname)
 ifneq ($(OS_ARCH), SunOS)
@@ -202,10 +199,50 @@
 endif
 endif
 
+# Determine the destination processor architecture.
+# IMPORTANT: The following line must be placed before ARCH is ever used
+# (of course), but should come after any lines setting CC because the line
+# below uses CC itself. In some cases we set CC based on OS_ARCH, see above.
+override ARCH := $(strip $(shell LC_ALL=C $(CC) -E arch.h 2>/dev/null | grep -v '^\#'))
+
+ifeq ($(ARCH), "ppc")
+# There's no PCI port I/O support on PPC/PowerPC, yet.
+ifeq ($(CONFIG_NIC3COM), yes)
+UNSUPPORTED_FEATURES += CONFIG_NIC3COM=yes
+else
+override CONFIG_NIC3COM = no
+endif
+ifeq ($(CONFIG_NICREALTEK), yes)
+UNSUPPORTED_FEATURES += CONFIG_NICREALTEK=yes
+else
+override CONFIG_NICREALTEK = no
+endif
+ifeq ($(CONFIG_NICNATSEMI), yes)
+UNSUPPORTED_FEATURES += CONFIG_NICNATSEMI=yes
+else
+override CONFIG_NICNATSEMI = no
+endif
+ifeq ($(CONFIG_RAYER_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_RAYER_SPI=yes
+else
+override CONFIG_RAYER_SPI = no
+endif
+ifeq ($(CONFIG_ATAHPT), yes)
+UNSUPPORTED_FEATURES += CONFIG_ATAHPT=yes
+else
+override CONFIG_ATAHPT = no
+endif
+ifeq ($(CONFIG_SATAMV), yes)
+UNSUPPORTED_FEATURES += CONFIG_SATAMV=yes
+else
+override CONFIG_SATAMV = no
+endif
+endif
+
 CHIP_OBJS = jedec.o stm50flw0x0x.o w39.o w29ee011.o \
 	sst28sf040.o m29f400bt.o 82802ab.o pm49fl00x.o \
 	sst49lfxxxc.o sst_fwhub.o flashchips.o spi.o spi25.o sharplhf00l04.o \
-	a25.o at25.o writeprotect.o
+	a25.o at25.o opaque.o writeprotect.o
 
 LIB_OBJS = layout.o fmap.o power.o
 
@@ -215,7 +252,7 @@
 FEATURE_CFLAGS += -D'USE_BIG_LOCK=1'
 endif
 
-CLI_OBJS = flashrom.o cli_classic.o cli_mfg.o cli_output.o print.o
+CLI_OBJS = flashrom.o cli_mfg.o cli_output.o print.o
 
 PROGRAMMER_OBJS = udelay.o programmer.o
 
@@ -227,7 +264,7 @@
 # will not require subversion. The downloadable snapshots are already exported.
 SVNVERSION := $(shell ./util/getversion.sh)
 
-RELEASE := 0.9.3
+RELEASE := 0.9.4
 VERSION := $(RELEASE) $(SVNVERSION)
 RELEASENAME ?= $(VERSION)
 
@@ -282,6 +319,10 @@
 # Always enable Bus Pirate SPI for now.
 CONFIG_BUSPIRATE_SPI ?= no
 
+# Disable Linux spidev interface support for now, until we check for a Linux
+# device (not host, as DOS binaries for example are built on a Linux host).
+CONFIG_LINUX_SPI ?= no
+
 # Disable Dediprog SF100 until support is complete and tested.
 CONFIG_DEDIPROG ?= no
 
@@ -422,6 +463,11 @@
 NEED_SERIAL := yes
 endif
 
+ifeq ($(CONFIG_LINUX_SPI), yes)
+FEATURE_CFLAGS += -D'CONFIG_LINUX_SPI=1'
+PROGRAMMER_OBJS += linux_spi.o
+endif
+
 ifeq ($(CONFIG_DEDIPROG), yes)
 FEATURE_CFLAGS += -D'CONFIG_DEDIPROG=1'
 FEATURE_LIBS += -lusb
@@ -507,11 +553,23 @@
 strip: $(PROGRAM)$(EXEC_SUFFIX)
 	$(STRIP) $(STRIP_ARGS) $(PROGRAM)$(EXEC_SUFFIX)
 
+# to define test programs we use verbatim variables, which get exported
+# to environment variables and are referenced with $$<varname> later
+
+define COMPILER_TEST
+int main(int argc, char **argv)
+{
+	(void) argc;
+	(void) argv;
+	return 0;
+}
+endef
+export COMPILER_TEST
+
 compiler: featuresavailable
 	@printf "Checking for a C compiler... "
-	@$(shell ( echo "int main(int argc, char **argv)"; \
-		   echo "{ (void) argc; (void) argv; return 0; }"; ) > .test.c )
-	@$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .test.c -o .test$(EXEC_SUFFIX) >/dev/null &&	\
+	@echo "$$COMPILER_TEST" > .test.c
+	@$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .test.c -o .test$(EXEC_SUFFIX) >/dev/null 2>&1 &&	\
 		echo "found." || ( echo "not found."; \
 		rm -f .test.c .test$(EXEC_SUFFIX); exit 1)
 	@rm -f .test.c .test$(EXEC_SUFFIX)
@@ -521,15 +579,25 @@
 		( echo "unknown. Aborting."; exit 1)
 	@printf "%s\n" '$(ARCH)'
 
+define LIBPCI_TEST
+/* Avoid a failing test due to libpci header symbol shadowing breakage */
+#define index shadow_workaround_index
+#include <pci/pci.h>
+struct pci_access *pacc;
+int main(int argc, char **argv)
+{
+	(void) argc;
+	(void) argv;
+	pacc = pci_alloc();
+	return 0;
+}
+endef
+export LIBPCI_TEST
+
 ifeq ($(CHECK_LIBPCI), yes)
 pciutils: compiler
 	@printf "Checking for libpci headers... "
-	@# 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 "{ (void) argc; (void) argv; pacc = pci_alloc(); return 0; }"; ) > .test.c )
+	@echo "$$LIBPCI_TEST" > .test.c
 	@$(CC) -c $(CPPFLAGS) $(CFLAGS) .test.c -o .test.o >/dev/null 2>&1 &&		\
 		echo "found." || ( echo "not found."; echo;			\
 		echo "Please install libpci headers (package pciutils-devel).";	\
@@ -564,41 +632,47 @@
 	@false
 endif
 
-ifeq ($(CONFIG_FT2232_SPI), yes)
+define FTDI_TEST
+#include <ftdi.h>
+struct ftdi_context *ftdic = NULL;
+int main(int argc, char **argv)
+{
+	(void) argc;
+	(void) argv;
+	return ftdi_init(ftdic);
+}
+endef
+export FTDI_TEST
+
+define UTSNAME_TEST
+#include <sys/utsname.h>
+struct utsname osinfo;
+int main(int argc, char **argv)
+{
+	(void) argc;
+	(void) argv;
+	uname (&osinfo);
+	return 0;
+}
+endef
+export UTSNAME_TEST
+
 features: compiler
 	@echo "FEATURES := yes" > .features.tmp
+ifeq ($(CONFIG_FT2232_SPI), yes)
 	@printf "Checking for FTDI support... "
-	@$(shell ( echo "#include <ftdi.h>";		   \
-		   echo "struct ftdi_context *ftdic = NULL;";	   \
-		   echo "int main(int argc, char **argv)"; \
-		   echo "{ (void) argc; (void) argv; return ftdi_init(ftdic); }"; ) > .featuretest.c )
+	@echo "$$FTDI_TEST" > .featuretest.c
 	@$(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... "
-	@$(shell ( echo "#include <sys/utsname.h>";		   \
-		   echo "struct utsname osinfo;";	   \
-		   echo "int main(int argc, char **argv)"; \
-		   echo "{ (void) argc; (void) argv; uname (&osinfo); return 0; }"; ) > .featuretest.c )
-	@$(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$(EXEC_SUFFIX)
-else
-features: compiler
-	@echo "FEATURES := yes" > .features.tmp
-	@printf "Checking for utsname support... "
-	@$(shell ( echo "#include <sys/utsname.h>";		   \
-		   echo "struct utsname osinfo;";	   \
-		   echo "int main(int argc, char **argv)"; \
-		   echo "{ (void) argc; (void) argv; uname (&osinfo); return 0; }"; ) > .featuretest.c )
-	@$(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$(EXEC_SUFFIX)
 endif
+	@printf "Checking for utsname support... "
+	@echo "$$UTSNAME_TEST" > .featuretest.c
+	@$(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$(EXEC_SUFFIX)
 
 install: $(PROGRAM)$(EXEC_SUFFIX)
 	mkdir -p $(DESTDIR)$(PREFIX)/sbin
diff --git a/a25.c b/a25.c
index f38626c..5791c46 100644
--- a/a25.c
+++ b/a25.c
@@ -23,6 +23,12 @@
 
 /* Prettyprint the status register. Works for AMIC A25L series. */
 
+static void spi_prettyprint_status_register_amic_a25_srwd(uint8_t status)
+{
+	msg_cdbg("Chip status register: Status Register Write Disable "
+		     "(SRWD) is %sset\n", (status & (1 << 7)) ? "" : "not ");
+}
+
 int spi_prettyprint_status_register_amic_a25l05p(struct flashchip *flash)
 {
 	uint8_t status;
@@ -30,8 +36,7 @@
 	status = spi_read_status_register();
 	msg_cdbg("Chip status register is %02x\n", status);
 
-	msg_cdbg("Chip status register: Status Register Write Disable "
-		     "(SRWD) is %sset\n", (status & (1 << 7)) ? "" : "not ");
+	spi_prettyprint_status_register_amic_a25_srwd(status);
 	spi_prettyprint_status_register_bit(status, 6);
 	spi_prettyprint_status_register_bit(status, 5);
 	spi_prettyprint_status_register_bit(status, 4);
@@ -47,8 +52,7 @@
 	status = spi_read_status_register();
 	msg_cdbg("Chip status register is %02x\n", status);
 
-	msg_cdbg("Chip status register: Status Register Write Disable "
-		     "(SRWD) is %sset\n", (status & (1 << 7)) ? "" : "not ");
+	spi_prettyprint_status_register_amic_a25_srwd(status);
 	spi_prettyprint_status_register_bit(status, 6);
 	spi_prettyprint_status_register_bit(status, 5);
 	spi_prettyprint_status_register_bp3210(status, 2);
@@ -63,8 +67,7 @@
 	status = spi_read_status_register();
 	msg_cdbg("Chip status register is %02x\n", status);
 
-	msg_cdbg("Chip status register: Status Register Write Disable "
-		     "(SRWD) is %sset\n", (status & (1 << 7)) ? "" : "not ");
+	spi_prettyprint_status_register_amic_a25_srwd(status);
 	msg_cdbg("Chip status register: Sector Protect Size (SEC) "
 		 "is %i KB\n", (status & (1 << 6)) ? 4 : 64);
 	msg_cdbg("Chip status register: Top/Bottom (TB) "
@@ -82,8 +85,7 @@
 	status = spi_read_status_register();
 	msg_cdbg("Chip status register is %02x\n", status);
 
-	msg_cdbg("Chip status register: Status Register Write Disable "
-		     "(SRWD) is %sset\n", (status & (1 << 7)) ? "" : "not ");
+	spi_prettyprint_status_register_amic_a25_srwd(status);
 	msg_cdbg("Chip status register: Sector Protect Size (SEC) "
 		 "is %i KB\n", (status & (1 << 6)) ? 4 : 64);
 	msg_cdbg("Chip status register: Top/Bottom (TB) "
diff --git a/atahpt.c b/atahpt.c
index 2bf4a11..6252865 100644
--- a/atahpt.c
+++ b/atahpt.c
@@ -18,6 +18,8 @@
  * 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 "flash.h"
@@ -38,6 +40,17 @@
 	{},
 };
 
+static const struct par_programmer par_programmer_atahpt = {
+		.chip_readb		= atahpt_chip_readb,
+		.chip_readw		= fallback_chip_readw,
+		.chip_readl		= fallback_chip_readl,
+		.chip_readn		= fallback_chip_readn,
+		.chip_writeb		= atahpt_chip_writeb,
+		.chip_writew		= fallback_chip_writew,
+		.chip_writel		= fallback_chip_writel,
+		.chip_writen		= fallback_chip_writen,
+};
+
 static int atahpt_shutdown(void *data)
 {
 	/* Flash access is disabled automatically by PCI restore. */
@@ -59,10 +72,11 @@
 	reg32 |= (1 << 24);
 	rpci_write_long(pcidev_dev, REG_FLASH_ACCESS, reg32);
 
-	buses_supported = CHIP_BUSTYPE_PARALLEL;
-
 	if (register_shutdown(atahpt_shutdown, NULL))
 		return 1;
+
+	register_par_programmer(&par_programmer_atahpt, BUS_PARALLEL);
+
 	return 0;
 }
 
@@ -77,3 +91,7 @@
 	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/bitbang_spi.c b/bitbang_spi.c
index 3c718af..391fd32 100644
--- a/bitbang_spi.c
+++ b/bitbang_spi.c
@@ -22,7 +22,6 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include "flash.h"
-#include "chipdrivers.h"
 #include "programmer.h"
 #include "spi.h"
 
@@ -68,13 +67,13 @@
 		const unsigned char *writearr, unsigned char *readarr);
 
 static const struct spi_programmer spi_programmer_bitbang = {
-	.type = SPI_CONTROLLER_BITBANG,
-	.max_data_read = MAX_DATA_READ_UNLIMITED,
-	.max_data_write = MAX_DATA_WRITE_UNLIMITED,
-	.command = bitbang_spi_send_command,
-	.multicommand = default_spi_send_multicommand,
-	.read = default_spi_read,
-	.write_256 = default_spi_write_256,
+	.type		= SPI_CONTROLLER_BITBANG,
+	.max_data_read	= MAX_DATA_READ_UNLIMITED,
+	.max_data_write	= MAX_DATA_WRITE_UNLIMITED,
+	.command	= bitbang_spi_send_command,
+	.multicommand	= default_spi_send_multicommand,
+	.read		= default_spi_read,
+	.write_256	= default_spi_write_256,
 };
 
 int bitbang_spi_init(const struct bitbang_spi_master *master, int halfperiod)
diff --git a/board_enable.c b/board_enable.c
index 2a3e281..bdd5db3 100644
--- a/board_enable.c
+++ b/board_enable.c
@@ -179,7 +179,7 @@
 static const struct winbond_port w83627hf[3] = {
 	UNIMPLEMENTED_PORT,
 	{w83627hf_port2_mux, 0x08, 0, 0xF0},
-	UNIMPLEMENTED_PORT
+	UNIMPLEMENTED_PORT,
 };
 
 static const struct winbond_mux w83627ehf_port2_mux[8] = {
@@ -190,7 +190,7 @@
 	{0x2A, 0x01, 0x01},	/* or keyboard/mouse interface */
 	{0x2A, 0x01, 0x01},
 	{0x2A, 0x01, 0x01},
-	{0x2A, 0x01, 0x01}
+	{0x2A, 0x01, 0x01},
 };
 
 static const struct winbond_port w83627ehf[6] = {
@@ -199,7 +199,7 @@
 	UNIMPLEMENTED_PORT,
 	UNIMPLEMENTED_PORT,
 	UNIMPLEMENTED_PORT,
-	UNIMPLEMENTED_PORT
+	UNIMPLEMENTED_PORT,
 };
 
 static const struct winbond_mux w83627thf_port4_mux[8] = {
@@ -210,7 +210,7 @@
 	{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 */
+	{0x2D, 0x80, 0x80},	/* or panel switch output */
 };
 
 static const struct winbond_port w83627thf[5] = {
@@ -218,7 +218,7 @@
 	UNIMPLEMENTED_PORT,	/* GPIO2 */
 	UNIMPLEMENTED_PORT,	/* GPIO3 */
 	{w83627thf_port4_mux, 0x09, 1, 0xF4},
-	UNIMPLEMENTED_PORT	/* GPIO5 */
+	UNIMPLEMENTED_PORT,	/* GPIO5 */
 };
 
 static const struct winbond_chip winbond_chips[] = {
@@ -334,14 +334,14 @@
 }
 
 /*
- * Winbond W83627EHF: Raise GPIO24.
+ * Winbond W83627EHF: Raise GPIO22.
  *
  * Suited for:
  *  - ASUS A8N-VM CSM: AMD Socket 939 + GeForce 6150 (C51) + MCP51
  */
-static int w83627ehf_gpio24_raise_2e(void)
+static int w83627ehf_gpio22_raise_2e(void)
 {
-	return winbond_gpio_set(0x2e, WINBOND_W83627EHF_ID, 24, 1);
+	return winbond_gpio_set(0x2e, WINBOND_W83627EHF_ID, 22, 1);
 }
 
 /*
@@ -425,7 +425,7 @@
 	/* 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;
+		internal_buses_supported = BUS_PARALLEL;
 		/* Flash ROM I/F Writes Enable */
 		tmp |= 0x04;
 		msg_pdbg("Enabling IT8705F flash ROM interface write.\n");
@@ -561,7 +561,8 @@
 
 	id = sio_read(0x2E, 0x20);
 	if (id != chipid) {
-		msg_perr("PC8736x: unexpected ID %02x (expected %02x)\n", id, chipid);
+		msg_perr("PC8736x: unexpected ID %02x (expected %02x)\n",
+			 id, chipid);
 		return -1;
 	}
 
@@ -811,13 +812,13 @@
 {
 	struct pci_dev *dev;
 
-	dev = pci_dev_find(0x10DE, 0x0050);	/* NVIDIA CK804 ISA Bridge. */
+	dev = pci_dev_find(0x10DE, 0x0050);	/* NVIDIA CK804 ISA bridge. */
 	if (!dev) {
 		msg_perr("\nERROR: NVIDIA nForce4 ISA bridge not found.\n");
 		return -1;
 	}
 
-	/* one of those bits seems to be connected to TBL#, but -ENOINFO. */
+	/* One of those bits seems to be connected to TBL#, but -ENOINFO. */
 	pci_write_byte(dev, 0x92, 0);
 
 	return 0;
@@ -851,8 +852,7 @@
 static int nvidia_mcp_gpio_set(int gpio, int raise)
 {
 	struct pci_dev *dev;
-	uint16_t base;
-	uint16_t devclass;
+	uint16_t base, devclass;
 	uint8_t tmp;
 
 	if ((gpio < 0) || (gpio >= 0x40)) {
@@ -860,7 +860,7 @@
 		return -1;
 	}
 
-	/* First, check the ISA Bridge */
+	/* Check for the ISA bridge first. */
 	dev = pci_dev_find_vendorclass(0x10DE, 0x0601);
 	switch (dev->device_id) {
 	case 0x0030: /* CK804 */
@@ -921,6 +921,7 @@
 
 /*
  * Suited for:
+ *  - ASUS A8M2N-LA (HP OEM "NodusM3-GL8E"): NVIDIA MCP51
  *  - ASUS A8N-LA (HP OEM "Nagami-GL8E"): NVIDIA MCP51
  *  - ASUS M2NBP-VM CSM: NVIDIA MCP51
  */
@@ -1050,6 +1051,38 @@
 
 /*
  * Suited for:
+ *  - Sun Ultra 40 M2: Dual Socket F (1207) + MCP55
+ */
+static int board_sun_ultra_40_m2(void)
+{
+	int ret;
+	uint8_t reg;
+	uint16_t base;
+	struct pci_dev *dev;
+
+	ret = nvidia_mcp_gpio4_lower();
+	if (ret)
+		return ret;
+
+	dev = pci_dev_find(0x10de, 0x0364); /* NVIDIA MCP55 LPC bridge */
+	if (!dev) {
+		msg_perr("\nERROR: NVIDIA MCP55 LPC bridge not found.\n");
+		return -1;
+	}
+
+	base = pci_read_word(dev, 0xb4); /* some IO BAR? */
+	if (!base)
+		return -1;
+
+	reg = INB(base + 0x4b);
+	reg |= 0x10;
+	OUTB(reg, base + 0x4b);
+
+	return 0;
+}
+
+/*
+ * Suited for:
  *  - Artec Group DBE61 and DBE62
  */
 static int board_artecgroup_dbe6x(void)
@@ -1092,7 +1125,7 @@
 
 /*
  * Suited for:
- *  - Asus A8AE-LE (Codename AmberineM; used in Compaq Presario 061)
+ *  - ASUS A8AE-LE (Codename AmberineM; used in Compaq Presario 061)
  * Datasheet(s) used:
  *  - AMD document 43009 "AMD SB700/710/750 Register Reference Guide" rev. 1.00
  */
@@ -1101,7 +1134,7 @@
 	struct pci_dev *dev;
 	uint32_t reg;
 
-	dev = pci_dev_find(0x1002, 0x4372); /* AMD SMBus Controller */
+	dev = pci_dev_find(0x1002, 0x4372); /* AMD SMBus controller */
 	if (!dev) {
 		msg_perr("\nERROR: AMD SMBus Controller (0x4372) not found.\n");
 		return -1;
@@ -1128,43 +1161,43 @@
 	struct pci_dev *dev;
 	uint32_t tmp, base;
 
-	static const uint32_t nonmuxed_gpos  = 0x58000101; /* GPPO {0,8,27,28,30} are always available */
+	/* GPO{0,8,27,28,30} are always available. */
+	static const uint32_t nonmuxed_gpos = 0x58000101;
 
 	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}
+		{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");
@@ -1177,10 +1210,11 @@
 		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;
+	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;
 	}
 
 	dev = pci_dev_find(0x8086, 0x7113);	/* Intel PIIX4 PM */
@@ -1315,7 +1349,7 @@
 		/* 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) &&
-		    (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)
@@ -1327,7 +1361,7 @@
 	}
 
 	if (!dev) {
-		msg_perr("\nERROR: No Known Intel LPC Bridge found.\n");
+		msg_perr("\nERROR: No known Intel LPC bridge found.\n");
 		return -1;
 	}
 
@@ -1347,13 +1381,13 @@
 		allowed = (intel_ich_gpio_table[i].bank2 >> (gpio - 64)) & 0x01;
 
 	if (!allowed) {
-		msg_perr("\nERROR: This Intel LPC Bridge does not allow"
-			" setting GPIO%02d\n", gpio);
+		msg_perr("\nERROR: This Intel LPC bridge does not allow"
+			 " setting GPIO%02d\n", gpio);
 		return -1;
 	}
 
-	msg_pdbg("\nIntel ICH LPC Bridge: %sing GPIO%02d.\n",
-	       raise ? "Rais" : "Dropp", gpio);
+	msg_pdbg("\nIntel ICH LPC bridge: %sing GPIO%02d.\n",
+		 raise ? "Rais" : "Dropp", gpio);
 
 	if (gpio < 32) {
 		/* Set line to GPIO. */
@@ -1371,7 +1405,7 @@
 		if (dev->device_id > 0x2800) {
 			tmp = INL(base);
 			if (!(tmp & (1 << gpio))) {
-				msg_perr("\nERROR: This Intel LPC Bridge"
+				msg_perr("\nERROR: This Intel LPC bridge"
 					" does not allow setting GPIO%02d\n",
 					gpio);
 				return -1;
@@ -1403,7 +1437,7 @@
 		if (dev->device_id > 0x2800) {
 			tmp = INL(base + 30);
 			if (!(tmp & (1 << gpio))) {
-				msg_perr("\nERROR: This Intel LPC Bridge"
+				msg_perr("\nERROR: This Intel LPC bridge"
 					" does not allow setting GPIO%02d\n",
 					gpio + 32);
 				return -1;
@@ -1432,7 +1466,7 @@
 
 		tmp = INL(base + 40);
 		if (!(tmp & (1 << gpio))) {
-			msg_perr("\nERROR: This Intel LPC Bridge does "
+			msg_perr("\nERROR: This Intel LPC bridge does "
 				"not allow setting GPIO%02d\n", gpio + 64);
 			return -1;
 		}
@@ -1458,6 +1492,7 @@
  * Suited for:
  *  - abit IP35: Intel P35 + ICH9R
  *  - abit IP35 Pro: Intel P35 + ICH9R
+ *  - ASUS P5LD2
  */
 static int intel_ich_gpio16_raise(void)
 {
@@ -1475,15 +1510,6 @@
 
 /*
  * Suited for:
- *  - ASUS A8Jm (laptop): Intel 945 + ICH7
- */
-static int intel_ich_gpio34_raise(void)
-{
-	return intel_ich_gpio_set(34, 1);
-}
-
-/*
- * Suited for:
  *  - MSI MS-7046: LGA775 + 915P + ICH6
  */
 static int intel_ich_gpio19_raise(void)
@@ -1499,6 +1525,7 @@
  *  - ASUS P4P800-E Deluxe: Intel socket478 + 865PE + ICH5R
  *  - ASUS P4P800-VM: Intel socket478 + 865PE + ICH5R
  *  - ASUS P5GD1 Pro: Intel LGA 775 + 915P + ICH6R
+ *  - ASUS P5GD2 Premium: Intel LGA775 + 915G + ICH6R
  *  - ASUS P5GDC Deluxe: Intel socket775 + 915P + ICH6R
  *  - ASUS P5PE-VM: Intel LGA775 + 865G + ICH5
  *  - Samsung Polaris 32: socket478 + 865P + ICH5
@@ -1521,6 +1548,27 @@
 
 /*
  * Suited for:
+ *  - ASUS A8Jm (laptop): Intel 945 + ICH7
+ *  - ASUS P5LP-LE used in ...
+ *    - HP Media Center m7270.fr Desktop PC as "Lithium-UL8E"
+ *    - Epson Endeavor MT7700
+ */
+static int intel_ich_gpio34_raise(void)
+{
+	return intel_ich_gpio_set(34, 1);
+}
+
+/*
+ * Suited for:
+ *  - ASUS M6Ne (laptop): socket 479M (guessed) + Intel 855PM + ICH4-M
+ */
+static int intel_ich_gpio43_raise(void)
+{
+	return intel_ich_gpio_set(43, 1);
+}
+
+/*
+ * Suited for:
  *  - HP Vectra VL400: 815 + ICH + PC87360
  */
 static int board_hp_vl400(void)
@@ -1598,10 +1646,10 @@
 {
 	int ret;
 
-	/* vendor BIOS ends up in LDN6... maybe the board enable is wrong,
+	/* Vendor BIOS ends up in LDN6... maybe the board enable is wrong,
 	 * or perhaps it's not needed at all?
-	 * the regs it tries to touch are 0xF0, 0xF1, 0xF2 which means if it
-	 * were in the right LDN, it would have to be GPIO1 or GPIO3
+	 * The regs it tries to touch are 0xF0, 0xF1, 0xF2 which means if it
+	 * were in the right LDN, it would have to be GPIO1 or GPIO3.
 	 */
 /*
 	ret = winbond_gpio_set(0x2e, WINBOND_W83627EHF_ID, x, 0)
@@ -1650,10 +1698,9 @@
 static int via_apollo_gpo_set(int gpio, int raise)
 {
 	struct pci_dev *dev;
-	uint32_t base;
-	uint32_t tmp;
+	uint32_t base, tmp;
 
-	/* VT82C686 Power management */
+	/* VT82C686 power management */
 	dev = pci_dev_find(0x1106, 0x3057);
 	if (!dev) {
 		msg_perr("\nERROR: VT82C686 PM device not found.\n");
@@ -1661,24 +1708,23 @@
 	}
 
 	msg_pdbg("\nVIA Apollo ACPI: %sing GPIO%02d.\n",
-	       raise ? "Rais" : "Dropp", gpio);
+		 raise ? "Rais" : "Dropp", gpio);
 
-	/* select GPO function on multiplexed pins */
+	/* Select GPO function on multiplexed pins. */
 	tmp = pci_read_byte(dev, 0x54);
-	switch(gpio)
-	{
-		case 0:
-			tmp &= ~0x03;
-			break;
-		case 1:
-			tmp |= 0x04;
-			break;
-		case 2:
-			tmp |= 0x08;
-			break;
-		case 3:
-			tmp |= 0x10;
-			break;
+	switch (gpio) {
+	case 0:
+		tmp &= ~0x03;
+		break;
+	case 1:
+		tmp |= 0x04;
+		break;
+	case 2:
+		tmp |= 0x08;
+		break;
+	case 3:
+		tmp |= 0x10;
+		break;
 	}
 	pci_write_byte(dev, 0x54, tmp);
 
@@ -1716,12 +1762,13 @@
 }
 
 /*
- * Enable some GPIO pin on SiS southbridge.
+ * Enable some GPIO pin on SiS southbridge and enables SIO flash writes.
  *
  * Suited for:
  *  - MSI 651M-L: SiS651 / SiS962
+ *  - GIGABYTE GA-8SIMLH
  */
-static int board_msi_651ml(void)
+static int sis_gpio0_raise_and_w836xx_memw(void)
 {
 	struct pci_dev *dev;
 	uint16_t base, temp;
@@ -1732,7 +1779,6 @@
 		return 1;
 	}
 
-	/* Registers 68 and 64 seem like bitmaps. */
 	base = pci_read_word(dev, 0x74);
 	temp = INW(base + 0x68);
 	temp &= ~(1 << 0);		/* Make pin output? */
@@ -1816,6 +1862,22 @@
 
 /*
  * Suited for:
+ *  - abit AV8: Socket939 + K8T800Pro + VT8237
+ */
+static int board_abit_av8(void)
+{
+	uint8_t val;
+
+	/* Raise GPO pins GP22 & GP23 */
+	val = INB(0x404E);
+	val |= 0xC0;
+	OUTB(val, 0x404E);
+
+	return 0;
+}
+
+/*
+ * Suited for:
  *  - ASUS A7V333: VIA KT333 + VT8233A + IT8703F
  *  - ASUS A7V8X: VIA KT400 + VT8235 + IT8703F
  */
@@ -1855,54 +1917,90 @@
 }
 
 /*
- * General routine for raising/dropping GPIO lines on the ITE IT8712F.
- * There is only some limited checking on the port numbers.
+ * General routine for raising/dropping GPIO lines on the ITE IT87xx.
  */
-static int it8712f_gpio_set(unsigned int line, int raise)
+static int it87_gpio_set(unsigned int gpio, int raise)
 {
+	int allowed, sio;
 	unsigned int port;
-	uint16_t id, base;
+	uint16_t base, sioport;
 	uint8_t tmp;
 
-	port = line / 10;
-	port--;
-	line %= 10;
+	/* IT87 GPIO configuration table */
+	static const struct it87cfg {
+		uint16_t id;
+		uint8_t base_reg;
+		uint32_t bank0;
+		uint32_t bank1;
+		uint32_t bank2;
+	} it87_gpio_table[] = {
+		{0x8712, 0x62, 0xCFF3FC00, 0x00FCFF3F,          0},
+		{0x8718, 0x62, 0xCFF37C00, 0xF3FCDF3F, 0x0000000F},
+		{0, 0, 0, 0, 0} /* end marker */
+	};
+	const struct it87cfg *cfg = NULL;
 
-	/* 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);
-	    return -1;
+	/* Find the Super I/O in the probed list */
+	for (sio = 0; sio < superio_count; sio++) {
+		int i;
+		if (superios[sio].vendor != SUPERIO_VENDOR_ITE)
+			continue;
+
+		/* Is this device in our list? */
+		for (i = 0; it87_gpio_table[i].id; i++)
+			if (superios[sio].model == it87_gpio_table[i].id) {
+				cfg = &it87_gpio_table[i];
+				goto found;
+			}
 	}
 
-	/* Find the IT8712F. */
-	enter_conf_mode_ite(0x2E);
-	id = (sio_read(0x2E, 0x20) << 8) | sio_read(0x2E, 0x21);
-	exit_conf_mode_ite(0x2E);
-
-	if (id != 0x8712) {
-		msg_perr("\nERROR: IT8712F Super I/O not found.\n");
+	if (cfg == NULL) {
+		msg_perr("\nERROR: No IT87 Super I/O GPIO configuration "
+			 "found.\n");
 		return -1;
 	}
 
-	/* Get the GPIO base */
-	enter_conf_mode_ite(0x2E);
-	sio_write(0x2E, 0x07, 0x07);
-	base = (sio_read(0x2E, 0x62) << 8) | sio_read(0x2E, 0x63);
-	exit_conf_mode_ite(0x2E);
+found:
+	/* Check whether the gpio is allowed. */
+	if (gpio < 32)
+		allowed = (cfg->bank0 >> gpio) & 0x01;
+	else if (gpio < 64)
+		allowed = (cfg->bank1 >> (gpio - 32)) & 0x01;
+	else if (gpio < 96)
+		allowed = (cfg->bank2 >> (gpio - 64)) & 0x01;
+	else
+		allowed = 0;
+
+	if (!allowed) {
+		msg_perr("\nERROR: IT%02X does not allow setting GPIO%02u.\n",
+			 cfg->id, gpio);
+		return -1;
+	}
+
+	/* Read the Simple I/O Base Address Register */
+	sioport = superios[sio].port;
+	enter_conf_mode_ite(sioport);
+	sio_write(sioport, 0x07, 0x07);
+	base = (sio_read(sioport, cfg->base_reg) << 8) |
+		sio_read(sioport, cfg->base_reg + 1);
+	exit_conf_mode_ite(sioport);
 
 	if (!base) {
-		msg_perr("\nERROR: Failed to read IT8712F Super I/O GPIO"
-			" Base.\n");
+		msg_perr("\nERROR: Failed to read IT87 Super I/O GPIO Base.\n");
 		return -1;
 	}
 
+	msg_pdbg("Using IT87 GPIO base 0x%04x\n", base);
+
+	port = gpio / 10 - 1;
+	gpio %= 10;
+
 	/* set GPIO. */
 	tmp = INB(base + port);
 	if (raise)
-	    tmp |= 1 << line;
+		tmp |= 1 << gpio;
 	else
-	    tmp &= ~(1 << line);
+		tmp &= ~(1 << gpio);
 	OUTB(tmp, base + port);
 
 	return 0;
@@ -1910,12 +2008,31 @@
 
 /*
  * Suited for:
+ * - ASUS A7N8X-VM/400: NVIDIA nForce2 IGP2 + IT8712F
+ */
+static int it8712f_gpio12_raise(void)
+{
+	return it87_gpio_set(12, 1);
+}
+
+/*
+ * Suited for:
  * - ASUS A7V600-X: VIA KT600 + VT8237 + IT8712F
  * - ASUS A7V8X-X: VIA KT400 + VT8235 + IT8712F
  */
-static int it8712f_gpio3_1_raise(void)
+static int it8712f_gpio31_raise(void)
 {
-	return it8712f_gpio_set(32, 1);
+	return it87_gpio_set(32, 1);
+}
+
+/*
+ * Suited for:
+ * - ASUS P5N-D: NVIDIA MCP51 + IT8718F
+ * - ASUS P5N-E SLI: NVIDIA MCP51 + IT8718F
+ */
+static int it8718f_gpio63_raise(void)
+{
+	return it87_gpio_set(63, 1);
 }
 
 #endif
@@ -1960,11 +2077,12 @@
  */
 
 /* Please keep this list alphabetically ordered by vendor/board name. */
-const struct board_pciid_enable board_pciid_enables[] = {
+const struct board_match board_matches[] = {
 
 	/* first pci-id set [4],          second pci-id set [4],          dmi identifier, coreboot id [2],  phase, vendor name,  board name       max_rom_...  OK? flash enable */
 #if defined(__i386__) || defined(__x86_64__)
 	{0x10DE, 0x0547, 0x147B, 0x1C2F,  0x10DE, 0x0548, 0x147B, 0x1C2F, NULL,         NULL, NULL,           P3, "abit",        "AN-M2",                 0,   NT, nvidia_mcp_gpio2_raise},
+	{0x1106, 0x0282, 0x147B, 0x1415,  0x1106, 0x3227, 0x147B, 0x1415, "^AV8 ",      NULL, NULL,           P3, "abit",        "AV8",                   0,   OK, board_abit_av8},
 	{0x8086, 0x7190,      0,      0,  0x8086, 0x7110,      0,      0, "^i440BX-W977 (BM6)$", NULL, NULL,  P3, "abit",        "BM6",                   0,   OK, intel_piix4_gpo26_lower},
 	{0x8086, 0x24d3, 0x147b, 0x1014,  0x8086, 0x2578, 0x147b, 0x1014, NULL,         NULL, NULL,           P3, "abit",        "IC7",                   0,   NT, intel_ich_gpio23_raise},
 	{0x8086, 0x2930, 0x147b, 0x1084,  0x11ab, 0x4364, 0x147b, 0x1084, NULL,         NULL, NULL,           P3, "abit",        "IP35",                  0,   OK, intel_ich_gpio16_raise},
@@ -1980,44 +2098,60 @@
 	{0x1022, 0x2090,      0,      0,  0x1022, 0x2080,      0,      0, NULL,        "artecgroup", "dbe61", P3, "Artec Group", "DBE61",                 0,   OK, board_artecgroup_dbe6x},
 	{0x1022, 0x2090,      0,      0,  0x1022, 0x2080,      0,      0, NULL,        "artecgroup", "dbe62", P3, "Artec Group", "DBE62",                 0,   OK, board_artecgroup_dbe6x},
 	{0x8086, 0x277c, 0xa0a0, 0x060b,  0x8086, 0x27da, 0xa0a0, 0x060b, NULL,         NULL, NULL,           P3, "AOpen",       "i975Xa-YDG",            0,   OK, board_aopen_i975xa_ydg},
+	{0x8086, 0x27b8, 0x1849, 0x27b8,  0x8086, 0x27da, 0x1849, 0x27da, "^ConRoeXFire-eSATA2", NULL, NULL,  P3, "ASRock",      "ConRoeXFire-eSATA2",    0,   OK, intel_ich_gpio16_raise},
 	{0x1039, 0x0741, 0x1849, 0x0741,  0x1039, 0x5513, 0x1849, 0x5513, "^K7S41 $",   NULL, NULL,           P3, "ASRock",      "K7S41",                 0,   OK, w836xx_memw_enable_2e},
 	{0x1039, 0x0741, 0x1849, 0x0741,  0x1039, 0x5513, 0x1849, 0x5513, "^K7S41GX$",  NULL, NULL,           P3, "ASRock",      "K7S41GX",               0,   OK, w836xx_memw_enable_2e},
 	{0x8086, 0x24D4, 0x1849, 0x24D0,  0x8086, 0x24D5, 0x1849, 0x9739, NULL,         NULL, NULL,           P3, "ASRock",      "P4i65GV",               0,   OK, intel_ich_gpio23_raise},
 	{0x8086, 0x2570, 0x1849, 0x2570,  0x8086, 0x24d3, 0x1849, 0x24d0, NULL,         NULL, NULL,           P3, "ASRock",      "775i65G",               0,   OK, intel_ich_gpio23_raise},
-	{0x1106, 0x3189, 0x1043, 0x807F,  0x1106, 0x3065, 0x1043, 0x80ED, NULL,         NULL, NULL,           P3, "ASUS",        "A7V600-X",              0,   OK, it8712f_gpio3_1_raise},
+	{0x10DE, 0x0060, 0x1043, 0x80AD,  0x10DE, 0x01E0, 0x1043, 0x80C0, NULL,         NULL, NULL,           P3, "ASUS",        "A7N8X-VM/400",          0,   OK, it8712f_gpio12_raise},
+	{0x1106, 0x3189, 0x1043, 0x807F,  0x1106, 0x3065, 0x1043, 0x80ED, NULL,         NULL, NULL,           P3, "ASUS",        "A7V600-X",              0,   OK, it8712f_gpio31_raise},
 	{0x1106, 0x3177, 0x1043, 0x80A1,  0x1106, 0x3205, 0x1043, 0x8118, NULL,         NULL, NULL,           P3, "ASUS",        "A7V8X-MX SE",           0,   OK, w836xx_memw_enable_2e},
 	{0x1106, 0x3189, 0x1043, 0x807F,  0x1106, 0x3177, 0x1043, 0x808C, NULL,         NULL, NULL,           P3, "ASUS",        "A7V8X",                 0,   OK, it8703f_gpio51_raise},
 	{0x1106, 0x3099, 0x1043, 0x807F,  0x1106, 0x3147, 0x1043, 0x808C, NULL,         NULL, NULL,           P3, "ASUS",        "A7V333",                0,   OK, it8703f_gpio51_raise},
-	{0x1106, 0x3189, 0x1043, 0x807F,  0x1106, 0x3177, 0x1043, 0x80A1, NULL,         NULL, NULL,           P3, "ASUS",        "A7V8X-X",               0,   OK, it8712f_gpio3_1_raise},
+	{0x1106, 0x3189, 0x1043, 0x807F,  0x1106, 0x3177, 0x1043, 0x80A1, NULL,         NULL, NULL,           P3, "ASUS",        "A7V8X-X",               0,   OK, it8712f_gpio31_raise},
 	{0x1002, 0x4372, 0x103c, 0x2a26,  0x1002, 0x4377, 0x103c, 0x2a26, NULL,         NULL, NULL,           P3, "ASUS",        "A8AE-LE",               0,   OK, amd_sbxxx_gpio9_raise},
 	{0x8086, 0x27A0, 0x1043, 0x1287,  0x8086, 0x27DF, 0x1043, 0x1287, "^A8J",       NULL, NULL,           P3, "ASUS",        "A8Jm",                  0,   NT, intel_ich_gpio34_raise},
+	{0x10DE, 0x0260, 0x103C, 0x2A34,  0x10DE, 0x0264, 0x103C, 0x2A34, "NODUSM3",    NULL, NULL,           P3, "ASUS",        "A8M2N-LA (NodusM3-GL8E)",  0,   OK, nvidia_mcp_gpio0_raise},
 	{0x10DE, 0x0260, 0x103c, 0x2a3e,  0x10DE, 0x0264, 0x103c, 0x2a3e, "NAGAMI2L",   NULL, NULL,           P3, "ASUS",        "A8N-LA (Nagami-GL8E)",  0,   OK, nvidia_mcp_gpio0_raise},
-	{0x10DE, 0x005E, 0x1043, 0x815A,  0x10DE, 0x0054, 0x1043, 0x815A, "^A8N-SLI",   NULL, NULL,           P3, "ASUS",        "A8N-SLI Deluxe",        0,   NT, board_shuttle_fn25},
-	{0x10de, 0x0264, 0x1043, 0x81bc,  0x10de, 0x02f0, 0x1043, 0x81cd, NULL,         NULL, NULL,           P3, "ASUS",        "A8N-VM CSM",            0,   NT, w83627ehf_gpio24_raise_2e},
+	{0x10DE, 0x005E, 0x1043, 0x815A,  0x10DE, 0x0054, 0x1043, 0x815A, "^A8N-SLI DELUXE", NULL, NULL,      P3, "ASUS",        "A8N-SLI Deluxe",        0,   NT, board_shuttle_fn25},
+	{0x10de, 0x0264, 0x1043, 0x81bc,  0x10de, 0x02f0, 0x1043, 0x81cd, NULL,         NULL, NULL,           P3, "ASUS",        "A8N-VM CSM",            0,   OK, w83627ehf_gpio22_raise_2e},
 	{0x10DE, 0x0264, 0x1043, 0x81C0,  0x10DE, 0x0260, 0x1043, 0x81C0, NULL,         NULL, NULL,           P3, "ASUS",        "M2NBP-VM CSM",          0,   OK, nvidia_mcp_gpio0_raise},
 	{0x1106, 0x1336, 0x1043, 0x80ed,  0x1106, 0x3288, 0x1043, 0x8249, NULL,         NULL, NULL,           P3, "ASUS",        "M2V-MX",                0,   OK, via_vt823x_gpio5_raise},
+	{0x8086, 0x24cc,      0,      0,  0x8086, 0x24c3, 0x1043, 0x1869, "^M6Ne$",     NULL, NULL,           P3, "ASUS",        "M6Ne",                  0,   NT, intel_ich_gpio43_raise},
 	{0x8086, 0x7190,      0,      0,  0x8086, 0x7110,      0,      0, "^P2B-N$",    NULL, NULL,           P3, "ASUS",        "P2B-N",                 0,   OK, intel_piix4_gpo18_lower},
 	{0x8086, 0x1A30, 0x1043, 0x8025,  0x8086, 0x244B, 0x104D, 0x80F0, NULL,         NULL, NULL,           P3, "ASUS",        "P4B266-LM",             0,   OK, intel_ich_gpio21_raise},
 	{0x8086, 0x1a30, 0x1043, 0x8070,  0x8086, 0x244b, 0x1043, 0x8028, NULL,         NULL, NULL,           P3, "ASUS",        "P4B266",                0,   OK, intel_ich_gpio22_raise},
 	{0x8086, 0x1A30, 0x1043, 0x8088,  0x8086, 0x24C3, 0x1043, 0x8089, NULL,         NULL, NULL,           P3, "ASUS",        "P4B533-E",              0,   NT, intel_ich_gpio22_raise},
+	{0x8086, 0x2560, 0x103C, 0x2A00,  0x8086, 0x24C3, 0x103C, 0x2A01, "^Guppy",     NULL, NULL,           P3, "ASUS",        "P4GV-LA (Guppy)",       0,   OK, intel_ich_gpio21_raise},
 	{0x8086, 0x24D3, 0x1043, 0x80A6,  0x8086, 0x2578, 0x1043, 0x80F6, NULL,         NULL, NULL,           P3, "ASUS",        "P4C800-E Deluxe",       0,   OK, intel_ich_gpio21_raise},
 	{0x8086, 0x2570, 0x1043, 0x80F2,  0x8086, 0x24D5, 0x1043, 0x80F3, NULL,         NULL, NULL,           P3, "ASUS",        "P4P800",                0,   NT, intel_ich_gpio21_raise},
-	{0x8086, 0x2570, 0x1043, 0x80F2,  0x105A, 0x3373, 0x1043, 0x80F5, NULL,         NULL, NULL,           P3, "ASUS",        "P4P800-E Deluxe",       0,   OK, intel_ich_gpio21_raise},
+	{0x8086, 0x2570, 0x1043, 0x80F2,  0x8086, 0x24D3, 0x1043, 0x80A6, "^P4P800-E$", NULL, NULL,           P3, "ASUS",        "P4P800-E Deluxe",       0,   OK, intel_ich_gpio21_raise},
 	{0x8086, 0x2570, 0x1043, 0x80A5,  0x8086, 0x24d0,      0,      0, NULL,         NULL, NULL,           P3, "ASUS",        "P4P800-VM",             0,   OK, intel_ich_gpio21_raise},
 	{0x1039, 0x0651, 0x1043, 0x8081,  0x1039, 0x0962,      0,      0, NULL,         NULL, NULL,           P3, "ASUS",        "P4SC-E",                0,   OK, it8707f_write_enable_2e},
 	{0x8086, 0x2570, 0x1043, 0x80A5,  0x105A, 0x24D3, 0x1043, 0x80A6, NULL,         NULL, NULL,           P3, "ASUS",        "P4SD-LA",               0,   NT, intel_ich_gpio32_raise},
 	{0x1039, 0x0661, 0x1043, 0x8113,  0x1039, 0x5513, 0x1043, 0x8087, NULL,         NULL, NULL,           P3, "ASUS",        "P4S800-MX",             512, OK, w836xx_memw_enable_2e},
 	{0x10B9, 0x1541,      0,      0,  0x10B9, 0x1533,      0,      0, "^P5A$",      "asus", "p5a",        P3, "ASUS",        "P5A",                   0,   OK, board_asus_p5a},
-	{0x8086, 0x266a, 0x1043, 0x80a6,  0x8086, 0x2668, 0x1043, 0x814e, NULL,         NULL, NULL,           P3, "ASUS",        "P5GD1 Pro",             0,   OK, intel_ich_gpio21_raise},
-	{0x8086, 0x266a, 0x1043, 0x80a6,  0x8086, 0x2668, 0x1043, 0x813d, NULL,         NULL, NULL,           P3, "ASUS",        "P5GDC Deluxe",          0,   OK, intel_ich_gpio21_raise},
+	{0x8086, 0x266a, 0x1043, 0x80a6,  0x8086, 0x2668, 0x1043, 0x814e, "^P5GD1 PRO$", NULL, NULL,          P3, "ASUS",        "P5GD1 Pro",             0,   OK, intel_ich_gpio21_raise},
+	{0x8086, 0x266a, 0x1043, 0x80a6,  0x8086, 0x2668, 0x1043, 0x814e, "^P5GD1-VM$", NULL, NULL,           P3, "ASUS",        "P5GD1-VM/S",            0,   OK, intel_ich_gpio21_raise},
+	{0x8086, 0x266a, 0x1043, 0x80a6,  0x8086, 0x2668, 0x1043, 0x814e, NULL,         NULL, NULL,           P3, "ASUS",        "P5GD1(-VM)",            0,   NT, intel_ich_gpio21_raise},
+	{0x8086, 0x266a, 0x1043, 0x80a6,  0x8086, 0x2668, 0x1043, 0x813d, "^P5GD2-Premium$", NULL, NULL,      P3, "ASUS",        "P5GD2 Premium",         0,   OK, intel_ich_gpio21_raise},
+	{0x8086, 0x266a, 0x1043, 0x80a6,  0x8086, 0x2668, 0x1043, 0x813d, "^P5GDC-V$",  NULL, NULL,           P3, "ASUS",        "P5GDC-V Deluxe",        0,   OK, intel_ich_gpio21_raise},
+	{0x8086, 0x266a, 0x1043, 0x80a6,  0x8086, 0x2668, 0x1043, 0x813d, "^P5GDC$",    NULL, NULL,           P3, "ASUS",        "P5GDC Deluxe",          0,   OK, intel_ich_gpio21_raise},
+	{0x8086, 0x266a, 0x1043, 0x80a6,  0x8086, 0x2668, 0x1043, 0x813d, NULL,         NULL, NULL,           P3, "ASUS",        "P5GD2/C variants",      0,   NT, intel_ich_gpio21_raise},
+	{0x8086, 0x27b8, 0x103c, 0x2a22,  0x8086, 0x2770, 0x103c, 0x2a22, "^LITHIUM$",  NULL, NULL,           P3, "ASUS",        "P5LP-LE (Lithium-UL8E)",0,   OK, intel_ich_gpio34_raise},
+	{0x8086, 0x27b8, 0x1043, 0x2a22,  0x8086, 0x2770, 0x1043, 0x2a22, "^P5LP-LE$",  NULL, NULL,           P3, "ASUS",        "P5LP-LE (Epson OEM)",   0,   OK, intel_ich_gpio34_raise},
+	{0x8086, 0x27da, 0x1043, 0x8179,  0x8086, 0x27b8, 0x1043, 0x8179, "^P5LD2$",    NULL, NULL,           P3, "ASUS",        "P5LD2",                 0,   NT, intel_ich_gpio16_raise},
 	{0x10DE, 0x0030, 0x1043, 0x818a,  0x8086, 0x100E, 0x1043, 0x80EE, NULL,         NULL, NULL,           P3, "ASUS",        "P5ND2-SLI Deluxe",      0,   OK, nvidia_mcp_gpio10_raise},
+	{0x10DE, 0x0260, 0x1043, 0x81BC,  0x10DE, 0x026C, 0x1043, 0x829E, "^P5N-D$",    NULL, NULL,           P3, "ASUS",        "P5N-D",                 0,   OK, it8718f_gpio63_raise},
+	{0x10DE, 0x0260, 0x1043, 0x81BC,  0x10DE, 0x026C, 0x1043, 0x8249, "^P5N-E SLI$",NULL, NULL,           P3, "ASUS",        "P5N-E SLI",             0,   NT, it8718f_gpio63_raise},
 	{0x8086, 0x24dd, 0x1043, 0x80a6,  0x8086, 0x2570, 0x1043, 0x8157, NULL,         NULL, NULL,           P3, "ASUS",        "P5PE-VM",               0,   OK, intel_ich_gpio21_raise},
 	{0x10b7, 0x9055, 0x1028, 0x0082,  0x8086, 0x7190,      0,      0, NULL,         NULL, NULL,           P3, "Dell",        "OptiPlex GX1",          0,   OK, intel_piix4_gpo30_lower},
 	{0x8086, 0x3590, 0x1028, 0x016c,  0x1000, 0x0030, 0x1028, 0x016c, NULL,         NULL, NULL,           P3, "Dell",        "PowerEdge 1850",        0,   OK, intel_ich_gpio23_raise},
 	{0x10de, 0x03ea, 0x1019, 0x2602,  0x10de, 0x03e0, 0x1019, 0x2602, NULL,         NULL, NULL,           P3, "Elitegroup",  "GeForce6100SM-M",       0,   OK, board_ecs_geforce6100sm_m},
 	{0x1106, 0x3038, 0x1019, 0x0996,  0x1106, 0x3177, 0x1019, 0x0996, NULL,         NULL, NULL,           P3, "Elitegroup",  "K7VTA3",                256, OK, NULL},
 	{0x1106, 0x3177, 0x1106, 0x3177,  0x1106, 0x3059, 0x1695, 0x3005, NULL,         NULL, NULL,           P3, "EPoX",        "EP-8K5A2",              0,   OK, w836xx_memw_enable_2e},
-	{0x10DE, 0x005E, 0x1695, 0x1010,  0x10DE, 0x0050, 0x1695, 0x1010, NULL,         NULL, NULL,           P3, "EPoX",        "EP-8NPA7I",             0,   OK, nvidia_mcp_gpio4_raise},
+	{0x10DE, 0x005E, 0x1695, 0x1010,  0x10DE, 0x0050, 0x1695, 0x1010, "8NPA7I",     NULL, NULL,           P3, "EPoX",        "EP-8NPA7I",             0,   OK, nvidia_mcp_gpio4_raise},
+	{0x10DE, 0x005E, 0x1695, 0x1010,  0x10DE, 0x0050, 0x1695, 0x1010, "9NPA7I",     NULL, NULL,           P3, "EPoX",        "EP-9NPA7I",             0,   OK, nvidia_mcp_gpio4_raise},
 	{0x10EC, 0x8139, 0x1695, 0x9001,  0x11C1, 0x5811, 0x1695, 0x9015, NULL,         NULL, NULL,           P3, "EPoX",        "EP-8RDA3+",             0,   OK, nvidia_mcp_gpio31_raise},
 	{0x8086, 0x7110,      0,      0,  0x8086, 0x7190,      0,      0, NULL,         "epox", "ep-bx3",     P3, "EPoX",        "EP-BX3",                0,   NT, intel_piix4_gpo22_raise},
 	{0x10de, 0x02f0, 0x105b, 0x0d01,  0x10de, 0x0264, 0x105b, 0x0d01, NULL,         NULL, NULL,           P3, "Foxconn",     "6150K8MD-8EKRSH",       0,   NT, nvidia_mcp_gpio2_raise},
@@ -2026,6 +2160,7 @@
 	{0x8086, 0x2570, 0x1458, 0x2570,  0x8086, 0x24d0,      0,      0, "^8IP775/-G$",NULL, NULL,           P3, "GIGABYTE",    "GA-8IP775",             0,   OK, intel_ich_gpio32_raise},
 	{0x8086, 0x244b, 0x8086, 0x2442,  0x8086, 0x2445, 0x1458, 0xa002, NULL,         NULL, NULL,           P3, "GIGABYTE",    "GA-8IRML",              0,   OK, intel_ich_gpio25_raise},
 	{0x8086, 0x24c3, 0x1458, 0x24c2,  0x8086, 0x24cd, 0x1458, 0x5004, NULL,         NULL, NULL,           P3, "GIGABYTE",    "GA-8PE667 Ultra 2",     0,   OK, intel_ich_gpio32_raise},
+	{0x1039, 0x0651, 0x1039, 0x0651,  0x1039, 0x7002, 0x1458, 0x5004, "^GA-8SIMLH$",NULL, NULL,           P3, "GIGABYTE",    "GA-8SIMLH",             0,   OK, sis_gpio0_raise_and_w836xx_memw},
 	{0x10DE, 0x02F1, 0x1458, 0x5000,  0x10DE, 0x0261, 0x1458, 0x5001, NULL,         NULL, NULL,           P3, "GIGABYTE",    "GA-K8N51GMF",           0,   OK, nvidia_mcp_gpio3b_raise},
 	{0x10DE, 0x026C, 0x1458, 0xA102,  0x10DE, 0x0260, 0x1458, 0x5001, NULL,         NULL, NULL,           P3, "GIGABYTE",    "GA-K8N51GMF-9",         0,   OK, nvidia_mcp_gpio3b_raise},
 	{0x10de, 0x00e4, 0x1458, 0x0c11,  0x10de, 0x00e0, 0x1458, 0x0c11, NULL,         NULL, NULL,           P3, "GIGABYTE",    "GA-K8NS Pro-939",       0,   NT, nvidia_mcp_gpio0a_raise},
@@ -2053,7 +2188,7 @@
 	{0x1106, 0x0571, 0x1462, 0x7120,  0x1106, 0x3065, 0x1462, 0x7120, NULL,         NULL, NULL,           P3, "MSI",         "MS-6712 (KT4V)",        0,   OK, board_msi_kt4v},
 	{0x1106, 0x3148, 0     , 0     ,  0x1106, 0x3177, 0     , 0     , NULL,         "msi", "ms6787",      P3, "MSI",         "MS-6787 (P4MAM-V/P4MAM-L)", 0, OK, w836xx_memw_enable_2e},
 	{0x8086, 0x24d3, 0x1462, 0x7880,  0x8086, 0x2570,      0,      0, NULL,	        NULL, NULL,           P3, "MSI",         "MS-6788-040 (848P NeoV)", 0, OK, intel_ich_gpio32_raise},
-	{0x1039, 0x7012, 0x1462, 0x0050,  0x1039, 0x6325, 0x1462, 0x0058, NULL,         NULL, NULL,           P3, "MSI",         "MS-7005 (651M-L)",      0,   OK, board_msi_651ml},
+	{0x1039, 0x7012, 0x1462, 0x0050,  0x1039, 0x6325, 0x1462, 0x0058, NULL,         NULL, NULL,           P3, "MSI",         "MS-7005 (651M-L)",      0,   OK, sis_gpio0_raise_and_w836xx_memw},
 	{0x10DE, 0x00E0, 0x1462, 0x0250,  0x10DE, 0x00E1, 0x1462, 0x0250, NULL,         NULL, NULL,           P3, "MSI",         "MS-7025 (K8N Neo2 Platinum)", 0, OK, nvidia_mcp_gpio0c_raise},
 	{0x8086, 0x2658, 0x1462, 0x7046,  0x1106, 0x3044, 0x1462, 0x046d, NULL,         NULL, NULL,           P3, "MSI",         "MS-7046",               0,   OK, intel_ich_gpio19_raise},
 	{0x8086, 0x244b, 0x1462, 0x3910,  0x8086, 0x2442, 0x1462, 0x3910, NULL,         NULL, NULL,           P3, "MSI",         "MS-6391 (845 Pro4)",    0,   OK, intel_ich_gpio23_raise},
@@ -2066,6 +2201,7 @@
 	{0x1106, 0x3104, 0x1297, 0xa238,  0x1106, 0x3059, 0x1297, 0xc063, NULL,         NULL, NULL,           P3, "Shuttle",     "AK38N",                 256, OK, NULL},
 	{0x10DE, 0x0050, 0x1297, 0x5036,  0x1412, 0x1724, 0x1297, 0x5036, NULL,         NULL, NULL,           P3, "Shuttle",     "FN25",                  0,   OK, board_shuttle_fn25},
 	{0x1106, 0x3038, 0x0925, 0x1234,  0x1106, 0x3058, 0x15DD, 0x7609, NULL,         NULL, NULL,           P3, "Soyo",        "SY-7VCA",               0,   OK, via_apollo_gpo0_lower},
+	{0x10de, 0x0364, 0x108e, 0x6676,  0x10de, 0x0369, 0x108e, 0x6676, "^Sun Ultra 40 M2", NULL, NULL,     P3, "Sun",         "Ultra 40 M2",           0,   OK, board_sun_ultra_40_m2},
 	{0x1106, 0x3038, 0x0925, 0x1234,  0x1106, 0x0596, 0x1106,      0, NULL,         NULL, NULL,           P3, "Tekram",      "P6Pro-A5",              256, OK, NULL},
 	{0x1106, 0x3123, 0x1106, 0x3123,  0x1106, 0x3059, 0x1106, 0x4161, NULL,         NULL, NULL,           P3, "Termtek",     "TK-3370 (Rev:2.5B)",    0,   OK, w836xx_memw_enable_4e},
 	{0x8086, 0x1076, 0x8086, 0x1176,  0x1106, 0x3059, 0x10f1, 0x2498, NULL,         NULL, NULL,           P3, "Tyan",        "S2498 (Tomcat K7M)",    0,   OK, w836xx_memw_enable_2e},
@@ -2080,11 +2216,11 @@
  * Match boards on coreboot table gathered vendor and part name.
  * Require main PCI IDs to match too as extra safety.
  */
-static const struct board_pciid_enable *board_match_coreboot_name(const char *vendor,
-							    const char *part)
+static const struct board_match *board_match_cbname(const char *vendor,
+						    const char *part)
 {
-	const struct board_pciid_enable *board = board_pciid_enables;
-	const struct board_pciid_enable *partmatch = NULL;
+	const struct board_match *board = board_matches;
+	const struct board_match *partmatch = NULL;
 
 	for (; board->vendor_name; board++) {
 		if (vendor && (!board->lb_vendor
@@ -2108,7 +2244,7 @@
 			/* a second entry has a matching part name */
 			msg_pinfo("AMBIGUOUS BOARD NAME: %s\n", part);
 			msg_pinfo("At least vendors '%s' and '%s' match.\n",
-			       partmatch->lb_vendor, board->lb_vendor);
+				  partmatch->lb_vendor, board->lb_vendor);
 			msg_perr("Please use the full -m vendor:part syntax.\n");
 			return NULL;
 		}
@@ -2124,18 +2260,18 @@
 		 * expected to fix flashrom, too.
 		 */
 		msg_perr("\nUnknown vendor:board from -m option: %s:%s\n\n",
-		       vendor, part);
+			 vendor, part);
 	}
 	return NULL;
 }
 
 /*
  * Match boards on PCI IDs and subsystem IDs.
- * Second set of IDs can be main only or missing completely.
+ * Second set of IDs can be either main+subsystem IDs, main IDs or no IDs.
  */
-const static struct board_pciid_enable *board_match_pci_card_ids(enum board_match_phase phase)
+const static struct board_match *board_match_pci_ids(enum board_match_phase phase)
 {
-	const struct board_pciid_enable *board = board_pciid_enables;
+	const struct board_match *board = board_matches;
 
 	for (; board->vendor_name; board++) {
 		if ((!board->first_card_vendor || !board->first_card_device) &&
@@ -2166,8 +2302,8 @@
 		if (board->dmi_pattern) {
 			if (!has_dmi_support) {
 				msg_perr("WARNING: Can't autodetect %s %s,"
-				       " DMI info unavailable.\n",
-				       board->vendor_name, board->board_name);
+					 " DMI info unavailable.\n",
+					 board->vendor_name, board->board_name);
 				continue;
 			} else {
 				if (!dmi_match(board->dmi_pattern))
@@ -2181,7 +2317,7 @@
 	return NULL;
 }
 
-static int unsafe_board_handler(const struct board_pciid_enable *board)
+static int unsafe_board_handler(const struct board_match *board)
 {
 	if (!board)
 		return 1;
@@ -2191,12 +2327,12 @@
 
 	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 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);
+			  "code has not been tested, and thus will 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);
 		return 1;
 	}
 	msg_pinfo("NOTE: Running an untested board enable procedure.\n"
@@ -2208,9 +2344,9 @@
 /* FIXME: Should this be identical to board_flash_enable? */
 static int board_handle_phase(enum board_match_phase phase)
 {
-	const struct board_pciid_enable *board = NULL;
+	const struct board_match *board = NULL;
 
-	board = board_match_pci_card_ids(phase);
+	board = board_match_pci_ids(phase);
 
 	if (unsafe_board_handler(board))
 		board = NULL;
@@ -2239,14 +2375,14 @@
 
 int board_flash_enable(const char *vendor, const char *part)
 {
-	const struct board_pciid_enable *board = NULL;
+	const struct board_match *board = NULL;
 	int ret = 0;
 
 	if (part)
-		board = board_match_coreboot_name(vendor, part);
+		board = board_match_cbname(vendor, part);
 
 	if (!board)
-		board = board_match_pci_card_ids(P3);
+		board = board_match_pci_ids(P3);
 
 	if (unsafe_board_handler(board))
 		board = NULL;
diff --git a/buspirate_spi.c b/buspirate_spi.c
index f872309..fd06b59 100644
--- a/buspirate_spi.c
+++ b/buspirate_spi.c
@@ -23,7 +23,6 @@
 #include <ctype.h>
 #include <unistd.h>
 #include "flash.h"
-#include "chipdrivers.h"
 #include "programmer.h"
 #include "spi.h"
 
@@ -50,7 +49,8 @@
 #define sp_flush_incoming(...) 0
 #endif
 
-static 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;
 
@@ -90,13 +90,13 @@
 		const unsigned char *writearr, unsigned char *readarr);
 
 static const struct spi_programmer spi_programmer_buspirate = {
-	.type = SPI_CONTROLLER_BUSPIRATE,
-	.max_data_read = 12,
-	.max_data_write = 12,
-	.command = buspirate_spi_send_command,
-	.multicommand = default_spi_send_multicommand,
-	.read = default_spi_read,
-	.write_256 = default_spi_write_256,
+	.type		= SPI_CONTROLLER_BUSPIRATE,
+	.max_data_read	= 12,
+	.max_data_write	= 12,
+	.command	= buspirate_spi_send_command,
+	.multicommand	= default_spi_send_multicommand,
+	.read		= default_spi_read,
+	.write_256	= default_spi_write_256,
 };
 
 static const struct buspirate_spispeeds spispeeds[] = {
@@ -108,7 +108,7 @@
 	{"2.6M",	0x5},
 	{"4M",		0x6},
 	{"8M",		0x7},
-	{NULL,		0x0}
+	{NULL,		0x0},
 };
 
 static int buspirate_spi_shutdown(void *data)
@@ -149,11 +149,11 @@
 int buspirate_spi_init(void)
 {
 	unsigned char buf[512];
-	int ret = 0;
-	int i;
 	char *dev = NULL;
 	char *speed = NULL;
 	int spispeed = 0x7;
+	int ret = 0;
+	int i;
 
 	dev = extract_programmer_param("dev");
 	if (!dev || !strlen(dev)) {
@@ -295,7 +295,8 @@
 		const unsigned char *writearr, unsigned char *readarr)
 {
 	static unsigned char *buf = NULL;
-	int i = 0, ret = 0;
+	unsigned int i = 0;
+	int ret = 0;
 
 	if (writecnt > 16 || readcnt > 16 || (readcnt + writecnt) > 16)
 		return SPI_INVALID_LENGTH;
diff --git a/cbtable.c b/cbtable.c
index ea8964a..5361a3d 100644
--- a/cbtable.c
+++ b/cbtable.c
@@ -242,7 +242,7 @@
 	}
 
 	addr = ((char *)lb_table) - ((char *)table_area) + start;
-	msg_pinfo("coreboot table found at 0x%lx.\n", 
+	msg_pdbg("coreboot table found at 0x%lx.\n", 
 		(unsigned long)lb_table - (unsigned long)table_area + start);
 	rec = (struct lb_record *)(((char *)lb_table) + lb_table->header_bytes);
 	last = (struct lb_record *)(((char *)rec) + lb_table->table_bytes);
diff --git a/chipdrivers.h b/chipdrivers.h
index e674434..2ac030b 100644
--- a/chipdrivers.h
+++ b/chipdrivers.h
@@ -25,6 +25,8 @@
 #ifndef __CHIPDRIVERS_H__
 #define __CHIPDRIVERS_H__ 1
 
+#include "flash.h"	/* for chipaddr and flashchip */
+
 /* spi.c, should probably be in spi_chip.c */
 int probe_spi_rdid(struct flashchip *flash);
 int probe_spi_rdid4(struct flashchip *flash);
@@ -39,9 +41,9 @@
 int spi_block_erase_d8(struct flashchip *flash, unsigned int addr, unsigned int blocklen);
 int spi_block_erase_60(struct flashchip *flash, unsigned int addr, unsigned int blocklen);
 int spi_block_erase_c7(struct flashchip *flash, unsigned int addr, unsigned int blocklen);
-int spi_chip_write_1(struct flashchip *flash, uint8_t *buf, int start, int len);
-int spi_chip_write_256(struct flashchip *flash, uint8_t *buf, int start, int len);
-int spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len);
+int spi_chip_write_1(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
+int spi_chip_write_256(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
+int spi_chip_read(struct flashchip *flash, uint8_t *buf, unsigned int start, int unsigned len);
 uint8_t spi_read_status_register(void);
 int spi_write_status_register(struct flashchip *flash, int status);
 void spi_prettyprint_status_register_bit(uint8_t status, int bit);
@@ -49,12 +51,18 @@
 void spi_prettyprint_status_register_welwip(uint8_t status);
 int spi_prettyprint_status_register(struct flashchip *flash);
 int spi_disable_blockprotect(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_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);
+int spi_byte_program(unsigned int addr, uint8_t databyte);
+int spi_nbyte_program(unsigned int addr, uint8_t *bytes, unsigned int len);
+int spi_nbyte_read(unsigned int addr, uint8_t *bytes, unsigned int len);
+int spi_read_chunked(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len, unsigned int chunksize);
+int spi_write_chunked(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len, unsigned int chunksize);
+int spi_aai_write(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
+
+/* opaque.c */
+int probe_opaque(struct flashchip *flash);
+int read_opaque(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
+int write_opaque(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
+int erase_opaque(struct flashchip *flash, unsigned int blockaddr, unsigned int blocklen);
 
 /* a25.c */
 int spi_prettyprint_status_register_amic_a25l05p(struct flashchip *flash);
@@ -79,10 +87,11 @@
 uint8_t wait_82802ab(struct flashchip *flash);
 int probe_82802ab(struct flashchip *flash);
 int erase_block_82802ab(struct flashchip *flash, unsigned int page, unsigned int pagesize);
-int write_82802ab(struct flashchip *flash, uint8_t *buf, int start, int len);
+int write_82802ab(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
 void print_status_82802ab(uint8_t status);
 int unlock_82802ab(struct flashchip *flash);
 int unlock_28f004s5(struct flashchip *flash);
+int unlock_lh28f008bjt(struct flashchip *flash);
 
 /* ichspi.c */
 int ich_hwseq_probe(struct flashchip *flash);
@@ -97,8 +106,8 @@
 int write_byte_program_jedec(chipaddr bios, uint8_t *src,
 			     chipaddr dst);
 int probe_jedec(struct flashchip *flash);
-int write_jedec(struct flashchip *flash, uint8_t *buf, int start, int len);
-int write_jedec_1(struct flashchip *flash, uint8_t *buf, int start, int len);
+int write_jedec(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
+int write_jedec_1(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
 int erase_sector_jedec(struct flashchip *flash, unsigned int page, unsigned int pagesize);
 int erase_block_jedec(struct flashchip *flash, unsigned int page, unsigned int blocksize);
 int erase_chip_block_jedec(struct flashchip *flash, unsigned int page, unsigned int blocksize);
@@ -107,7 +116,7 @@
 int probe_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 start, int len);
+int write_m29f400bt(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
 void protect_m29f400bt(chipaddr bios);
 
 /* pm49fl00x.c */
@@ -117,7 +126,7 @@
 /* sst28sf040.c */
 int erase_chip_28sf040(struct flashchip *flash, unsigned int addr, unsigned int blocklen);
 int erase_sector_28sf040(struct flashchip *flash, unsigned int address, unsigned int sector_size);
-int write_28sf040(struct flashchip *flash, uint8_t *buf, int start, int len);
+int write_28sf040(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
 int unprotect_28sf040(struct flashchip *flash);
 int protect_28sf040(struct flashchip *flash);
 
diff --git a/chipset_enable.c b/chipset_enable.c
index a6e8658..07107ca 100644
--- a/chipset_enable.c
+++ b/chipset_enable.c
@@ -30,6 +30,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <inttypes.h>
+#include <errno.h>
 #include "flash.h"
 #include "programmer.h"
 
@@ -75,7 +77,8 @@
 	rpci_write_byte(dev, 0x40, new);
 	newer = pci_read_byte(dev, 0x40);
 	if (newer != new) {
-		msg_pinfo("tried to set register 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x40, new, name);
+		msg_pinfo("Setting register 0x%x to 0x%x on %s failed "
+			  "(WARNING ONLY).\n", 0x40, new, name);
 		msg_pinfo("Stuck at 0x%x\n", newer);
 		return -1;
 	}
@@ -85,7 +88,7 @@
 static struct pci_dev *find_southbridge(uint16_t vendor, const char *name)
 {
 	struct pci_dev *sbdev;
-	
+
 	sbdev = pci_dev_find_vendorclass(vendor, 0x0601);
 	if (!sbdev)
 		sbdev = pci_dev_find_vendorclass(vendor, 0x0680);
@@ -95,8 +98,8 @@
 		msg_perr("No southbridge found for %s!\n", name);
 	if (sbdev)
 		msg_pdbg("Found southbridge %04x:%04x at %02x:%02x:%01x\n",
-			     sbdev->vendor_id, sbdev->device_id,
-			     sbdev->bus, sbdev->dev, sbdev->func);
+			 sbdev->vendor_id, sbdev->device_id,
+			 sbdev->bus, sbdev->dev, sbdev->func);
 	return sbdev;
 }
 
@@ -163,7 +166,8 @@
 	rpci_write_byte(sbdev, 0x45, new);
 	newer = pci_read_byte(sbdev, 0x45);
 	if (newer != new) {
-		msg_pinfo("tried to set register 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x45, new, name);
+		msg_pinfo("Setting register 0x%x to 0x%x on %s failed "
+			  "(WARNING ONLY).\n", 0x45, new, name);
 		msg_pinfo("Stuck at 0x%x\n", newer);
 		ret = -1;
 	}
@@ -189,7 +193,8 @@
 	rpci_write_byte(sbdev, 0x45, new);
 	newer = pci_read_byte(sbdev, 0x45);
 	if (newer != new) {
-		msg_pinfo("tried to set register 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x45, new, name);
+		msg_pinfo("Setting register 0x%x to 0x%x on %s failed "
+			  "(WARNING ONLY).\n", 0x45, new, name);
 		msg_pinfo("Stuck at 0x%x\n", newer);
 		ret = -1;
 	}
@@ -208,7 +213,7 @@
 	uint16_t old, new;
 	uint16_t xbcs = 0x4e;	/* X-Bus Chip Select register. */
 
-	buses_supported = CHIP_BUSTYPE_PARALLEL;
+	internal_buses_supported = BUS_PARALLEL;
 
 	old = pci_read_word(dev, xbcs);
 
@@ -236,7 +241,8 @@
 	rpci_write_word(dev, xbcs, new);
 
 	if (pci_read_word(dev, xbcs) != new) {
-		msg_pinfo("tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n", xbcs, new, name);
+		msg_pinfo("Setting register 0x%x to 0x%x on %s failed "
+			  "(WARNING ONLY).\n", xbcs, new, name);
 		return -1;
 	}
 
@@ -250,19 +256,19 @@
 static int enable_flash_ich(struct pci_dev *dev, const char *name,
 			    int bios_cntl)
 {
-	uint8_t old, new;
+	uint8_t old, new, wanted;
 
 	/*
 	 * Note: the ICH0-ICH5 BIOS_CNTL register is actually 16 bit wide, but
 	 * just treating it as 8 bit wide seems to work fine in practice.
 	 */
 	old = pci_read_byte(dev, bios_cntl);
-	new = old;
+	wanted = old;
 
 	msg_pdbg("\nBIOS Lock Enable: %sabled, ",
-		     (old & (1 << 1)) ? "en" : "dis");
+		 (old & (1 << 1)) ? "en" : "dis");
 	msg_pdbg("BIOS Write Enable: %sabled, ",
-		     (old & (1 << 0)) ? "en" : "dis");
+		 (old & (1 << 0)) ? "en" : "dis");
 	msg_pdbg("BIOS_CNTL is 0x%x\n", old);
 
 	/*
@@ -270,23 +276,26 @@
 	 * "Bit 5: SMM BIOS Write Protect Disable (SMM_BWP)
 	 * 1 = BIOS region SMM protection is enabled.
 	 * The BIOS Region is not writable unless all processors are in SMM."
-	 * In earlier chipsets this bit is reserved. */
+	 * In earlier chipsets this bit is reserved.
+	 */
 	if (old & (1 << 5)) {
-		msg_pinfo("WARNING: BIOS region SMM protection is enabled!\n");
+		msg_pdbg("WARNING: BIOS region SMM protection is enabled!\n");
 		msg_pdbg("Trying to clear BIOS region SMM protection.\n");
-		new &= ~(1 << 5);
+		wanted &= ~(1 << 5);
 	}
 
-	new |= (1 << 0);
+	wanted |= (1 << 0);
 
 	/* Only write the register if it's necessary */
-	if (new == old)
+	if (wanted == old)
 		return 0;
 
-	rpci_write_byte(dev, bios_cntl, new);
+	rpci_write_byte(dev, bios_cntl, wanted);
 
-	if (pci_read_byte(dev, bios_cntl) != new) {
-		msg_pinfo("tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n", bios_cntl, new, name);
+	if ((new = pci_read_byte(dev, bios_cntl)) != wanted) {
+		msg_pinfo("WARNING: Setting 0x%x from 0x%x to 0x%x on %s "
+			  "failed. New value is 0x%x.\n",
+			  bios_cntl, old, wanted, name, new);
 		return -1;
 	}
 
@@ -300,36 +309,48 @@
 	 * FWH_DEC_EN1, but they are called FB_SEL1, FB_SEL2, FB_DEC_EN1 and
 	 * FB_DEC_EN2.
 	 */
-	buses_supported = CHIP_BUSTYPE_FWH;
+	internal_buses_supported = BUS_FWH;
 	return enable_flash_ich(dev, name, 0x4e);
 }
 
 static int enable_flash_ich_dc(struct pci_dev *dev, const char *name)
 {
 	uint32_t fwh_conf;
-	int i;
+	int i, tmp;
 	char *idsel = NULL;
-	int tmp;
-	int max_decode_fwh_idsel = 0;
-	int max_decode_fwh_decode = 0;
+	int max_decode_fwh_idsel = 0, max_decode_fwh_decode = 0;
 	int contiguous = 1;
 
 	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. */
-		msg_pinfo("\nSetting IDSEL=0x%x for top 16 MB", fwh_conf);
-		rpci_write_long(dev, 0xd0, fwh_conf);
-		rpci_write_word(dev, 0xd4, fwh_conf);
+		uint64_t fwh_idsel_old, fwh_idsel;
+		errno = 0;
+		/* Base 16, nothing else makes sense. */
+		fwh_idsel = (uint64_t)strtoull(idsel, NULL, 16);
+		if (errno) {
+			msg_perr("Error: fwh_idsel= specified, but value could "
+				 "not be converted.\n");
+			goto idsel_garbage_out;
+		}
+		if (fwh_idsel & 0xffff000000000000ULL) {
+			msg_perr("Error: fwh_idsel= specified, but value had "
+				 "unused bits set.\n");
+			goto idsel_garbage_out;
+		}
+		fwh_idsel_old = pci_read_long(dev, 0xd0);
+		fwh_idsel_old <<= 16;
+		fwh_idsel_old |= pci_read_word(dev, 0xd4);
+		msg_pdbg("\nSetting IDSEL from 0x%012" PRIx64 " to "
+			 "0x%012" PRIx64 " for top 16 MB.", fwh_idsel_old,
+			 fwh_idsel);
+		rpci_write_long(dev, 0xd0, (fwh_idsel >> 16) & 0xffffffff);
+		rpci_write_word(dev, 0xd4, fwh_idsel & 0xffff);
 		/* FIXME: Decode settings are not changed. */
 	} else if (idsel) {
-		msg_perr("Error: idsel= specified, but no number given.\n");
+		msg_perr("Error: fwh_idsel= specified, but no value given.\n");
+idsel_garbage_out:
 		free(idsel);
-		/* FIXME: Return failure here once internal_init() starts
-		 * to care about the return value of the chipset enable.
-		 */
-		exit(1);
+		return ERROR_FATAL;
 	}
 	free(idsel);
 
@@ -343,9 +364,9 @@
 	for (i = 7; i >= 0; i--) {
 		tmp = (fwh_conf >> (i * 4)) & 0xf;
 		msg_pdbg("\n0x%08x/0x%08x FWH IDSEL: 0x%x",
-			     (0x1ff8 + i) * 0x80000,
-			     (0x1ff0 + i) * 0x80000,
-			     tmp);
+			 (0x1ff8 + i) * 0x80000,
+			 (0x1ff0 + i) * 0x80000,
+			 tmp);
 		if ((tmp == 0) && contiguous) {
 			max_decode_fwh_idsel = (8 - i) * 0x80000;
 		} else {
@@ -357,9 +378,9 @@
 	for (i = 3; i >= 0; i--) {
 		tmp = (fwh_conf >> (i * 4)) & 0xf;
 		msg_pdbg("\n0x%08x/0x%08x FWH IDSEL: 0x%x",
-			     (0xff4 + i) * 0x100000,
-			     (0xff0 + i) * 0x100000,
-			     tmp);
+			 (0xff4 + i) * 0x100000,
+			 (0xff0 + i) * 0x100000,
+			 tmp);
 		if ((tmp == 0) && contiguous) {
 			max_decode_fwh_idsel = (8 - i) * 0x100000;
 		} else {
@@ -372,9 +393,9 @@
 	for (i = 7; i >= 0; i--) {
 		tmp = (fwh_conf >> (i + 0x8)) & 0x1;
 		msg_pdbg("\n0x%08x/0x%08x FWH decode %sabled",
-			     (0x1ff8 + i) * 0x80000,
-			     (0x1ff0 + i) * 0x80000,
-			     tmp ? "en" : "dis");
+			 (0x1ff8 + i) * 0x80000,
+			 (0x1ff0 + i) * 0x80000,
+			 tmp ? "en" : "dis");
 		if ((tmp == 1) && contiguous) {
 			max_decode_fwh_decode = (8 - i) * 0x80000;
 		} else {
@@ -384,9 +405,9 @@
 	for (i = 3; i >= 0; i--) {
 		tmp = (fwh_conf >> i) & 0x1;
 		msg_pdbg("\n0x%08x/0x%08x FWH decode %sabled",
-			     (0xff4 + i) * 0x100000,
-			     (0xff0 + i) * 0x100000,
-			     tmp ? "en" : "dis");
+			 (0xff4 + i) * 0x100000,
+			 (0xff0 + i) * 0x100000,
+			 tmp ? "en" : "dis");
 		if ((tmp == 1) && contiguous) {
 			max_decode_fwh_decode = (8 - i) * 0x100000;
 		} else {
@@ -397,57 +418,50 @@
 	msg_pdbg("\nMaximum FWH chip size: 0x%x bytes", max_rom_decode.fwh);
 
 	/* If we're called by enable_flash_ich_dc_spi, it will override
-	 * buses_supported anyway.
+	 * internal_buses_supported anyway.
 	 */
-	buses_supported = CHIP_BUSTYPE_FWH;
+	internal_buses_supported = BUS_FWH;
 	return enable_flash_ich(dev, name, 0xdc);
 }
 
 static int enable_flash_poulsbo(struct pci_dev *dev, const char *name)
 {
-       uint16_t old, new;
-       int err;
+	uint16_t old, new;
+	int err;
 
-       if ((err = enable_flash_ich(dev, name, 0xd8)) != 0)
-               return err;
+	if ((err = enable_flash_ich(dev, name, 0xd8)) != 0)
+		return err;
 
-       old = pci_read_byte(dev, 0xd9);
-       msg_pdbg("BIOS Prefetch Enable: %sabled, ",
-                    (old & 1) ? "en" : "dis");
-       new = old & ~1;
+	old = pci_read_byte(dev, 0xd9);
+	msg_pdbg("BIOS Prefetch Enable: %sabled, ",
+		 (old & 1) ? "en" : "dis");
+	new = old & ~1;
 
-       if (new != old)
-               rpci_write_byte(dev, 0xd9, new);
+	if (new != old)
+		rpci_write_byte(dev, 0xd9, new);
 
-	buses_supported = CHIP_BUSTYPE_FWH;
-       return 0;
+	internal_buses_supported = BUS_FWH;
+	return 0;
 }
 
-
-#define ICH_STRAP_RSVD 0x00
-#define ICH_STRAP_SPI  0x01
-#define ICH_STRAP_PCI  0x02
-#define ICH_STRAP_LPC  0x03
-
-static int enable_flash_vt8237s_spi(struct pci_dev *dev, const char *name)
+static int enable_flash_tunnelcreek(struct pci_dev *dev, const char *name)
 {
-	/* Do we really need no write enable? */
-	return via_init_spi(dev);
-}
-
-static int enable_flash_ich_dc_spi(struct pci_dev *dev, const char *name,
-				   int ich_generation)
-{
-	int ret;
-	uint8_t bbs, buc;
-	uint32_t tmp, gcs;
+	uint16_t old, new;
+	uint32_t tmp, bnt;
 	void *rcrb;
-	//TODO: These names are incorrect for EP80579. For that, the solution would look like the commented line
-	//static const char *straps_names[] = {"SPI", "reserved", "reserved", "LPC" };
-	static const char *straps_names[] = { "reserved", "SPI", "PCI", "LPC" };
+	int ret;
 
 	/* Enable Flash Writes */
-	ret = enable_flash_ich_dc(dev, name);
+	ret = enable_flash_ich(dev, name, 0xd8);
+	if (ret == ERROR_FATAL)
+		return ret;
+
+	/* Make sure BIOS prefetch mechanism is disabled */
+	old = pci_read_byte(dev, 0xd9);
+	msg_pdbg("BIOS Prefetch Enable: %sabled, ", (old & 1) ? "en" : "dis");
+	new = old & ~1;
+	if (new != old)
+		rpci_write_byte(dev, 0xd9, new);
 
 	/* Get physical address of Root Complex Register Block */
 	tmp = pci_read_long(dev, 0xf0) & 0xffffc000;
@@ -456,13 +470,89 @@
 	/* Map RCBA to virtual memory */
 	rcrb = physmap("ICH RCRB", tmp, 0x4000);
 
+	/* Test Boot BIOS Strap Status */
+	bnt = mmio_readl(rcrb + 0x3410);
+	if (bnt & 0x02) {
+		/* If strapped to LPC, no SPI initialization is required */
+		internal_buses_supported = BUS_FWH;
+		return 0;
+	}
+
+	/* This adds BUS_SPI */
+	if (ich_init_spi(dev, tmp, rcrb, 7) != 0) {
+		if (!ret)
+			ret = ERROR_NONFATAL;
+	}
+
+	return ret;
+}
+
+static int enable_flash_vt8237s_spi(struct pci_dev *dev, const char *name)
+{
+	/* Do we really need no write enable? */
+	return via_init_spi(dev);
+}
+
+static int enable_flash_ich_dc_spi(struct pci_dev *dev, const char *name,
+				   enum ich_chipset ich_generation)
+{
+	int ret, ret_spi;
+	uint8_t bbs, buc;
+	uint32_t tmp, gcs;
+	void *rcrb;
+	const char *const *straps_names;
+
+	static const char *const straps_names_EP80579[] = { "SPI", "reserved", "reserved", "LPC" };
+	static const char *const straps_names_ich7_nm10[] = { "reserved", "SPI", "PCI", "LPC" };
+	static const char *const straps_names_ich8910[] = { "SPI", "SPI", "PCI", "LPC" };
+	static const char *const straps_names_pch56[] = { "LPC", "reserved", "PCI", "SPI" };
+	static const char *const straps_names_unknown[] = { "unknown", "unknown", "unknown", "unknown" };
+
+	switch (ich_generation) {
+	case CHIPSET_ICH7:
+		/* EP80579 may need further changes, but this is the least
+		 * intrusive way to get correct BOOT Strap printing without
+		 * changing the rest of its code path). */
+		if (strcmp(name, "EP80579") == 0)
+			straps_names = straps_names_EP80579;
+		else
+			straps_names = straps_names_ich7_nm10;
+		break;
+	case CHIPSET_ICH8:
+	case CHIPSET_ICH9:
+	case CHIPSET_ICH10:
+		straps_names = straps_names_ich8910;
+		break;
+	case CHIPSET_5_SERIES_IBEX_PEAK:
+	case CHIPSET_6_SERIES_COUGAR_POINT:
+		straps_names = straps_names_pch56;
+		break;
+	default:
+		msg_gerr("%s: unknown ICH generation. Please report!\n",
+			 __func__);
+		straps_names = straps_names_unknown;
+		break;
+	}
+
+	/* Enable Flash Writes */
+	ret = enable_flash_ich_dc(dev, name);
+	if (ret == ERROR_FATAL)
+		return ret;
+
+	/* Get physical address of Root Complex Register Block */
+	tmp = pci_read_long(dev, 0xf0) & 0xffffc000;
+	msg_pdbg("Root Complex Register Block address = 0x%x\n", tmp);
+
+	/* Map RCBA to virtual memory */
+	rcrb = physmap("ICH RCRB", tmp, 0x4000);
+
 	/* Set BBS (Boot BIOS Straps) field of GCS register. */
 	gcs = mmio_readl(rcrb + 0x3410);
-	if (target_bus == CHIP_BUSTYPE_LPC) {
+	if (target_bus == BUS_LPC) {
 		msg_pdbg("Setting BBS to LPC\n");
 		gcs = (gcs & ~0xc00) | (0x3 << 10);
 		rmmio_writel(gcs, rcrb + 0x3410);
-	} else if (target_bus == CHIP_BUSTYPE_SPI) {
+	} else if (target_bus == BUS_SPI) {
 		msg_pdbg("Setting BBS to SPI\n");
 		gcs = (gcs & ~0xc00) | (0x1 << 10);
 		rmmio_writel(gcs, rcrb + 0x3410);
@@ -470,57 +560,71 @@
 
 	msg_pdbg("GCS = 0x%x: ", gcs);
 	msg_pdbg("BIOS Interface Lock-Down: %sabled, ",
-		     (gcs & 0x1) ? "en" : "dis");
+		 (gcs & 0x1) ? "en" : "dis");
 	bbs = (gcs >> 10) & 0x3;
-	msg_pdbg("BOOT BIOS Straps: 0x%x (%s)\n", bbs, straps_names[bbs]);
+	msg_pdbg("Boot BIOS Straps: 0x%x (%s)\n", bbs, straps_names[bbs]);
 
 	buc = mmio_readb(rcrb + 0x3414);
 	msg_pdbg("Top Swap : %s\n",
-		     (buc & 1) ? "enabled (A16 inverted)" : "not enabled");
+		 (buc & 1) ? "enabled (A16 inverted)" : "not enabled");
 
 	/* It seems the ICH7 does not support SPI and LPC chips at the same
 	 * time. At least not with our current code. So we prevent searching
 	 * on ICH7 when the southbridge is strapped to LPC
 	 */
-
-	buses_supported = CHIP_BUSTYPE_FWH;
-	if (ich_generation == 7) {
-		if(bbs == ICH_STRAP_LPC) {
-			/* No further SPI initialization required */
+	internal_buses_supported = BUS_FWH;
+	if (ich_generation == CHIPSET_ICH7) {
+		if (bbs == 0x03) {
+			/* If strapped to LPC, no further SPI initialization is
+			 * required. */
 			return ret;
-		}
-		else
+		} else {
 			/* Disable LPC/FWH if strapped to PCI or SPI */
-			buses_supported = 0;
+			internal_buses_supported = BUS_NONE;
+		}
 	}
 
-	/* this adds CHIP_BUSTYPE_SPI */
-	if (ich_init_spi(dev, tmp, rcrb, ich_generation) != 0) {
-	        if (!ret)
-		        ret = ERROR_NONFATAL;
-        }
+	/* This adds BUS_SPI */
+	ret_spi = ich_init_spi(dev, tmp, rcrb, ich_generation);
+	if (ret_spi == ERROR_FATAL)
+		return ret_spi;
+	
+	if (ret || ret_spi)
+		ret = ERROR_NONFATAL;
 
 	return ret;
 }
 
 static int enable_flash_ich7(struct pci_dev *dev, const char *name)
 {
-	return enable_flash_ich_dc_spi(dev, name, 7);
+	return enable_flash_ich_dc_spi(dev, name, CHIPSET_ICH7);
 }
 
 static int enable_flash_ich8(struct pci_dev *dev, const char *name)
 {
-	return enable_flash_ich_dc_spi(dev, name, 8);
+	return enable_flash_ich_dc_spi(dev, name, CHIPSET_ICH8);
 }
 
 static int enable_flash_ich9(struct pci_dev *dev, const char *name)
 {
-	return enable_flash_ich_dc_spi(dev, name, 9);
+	return enable_flash_ich_dc_spi(dev, name, CHIPSET_ICH9);
 }
 
 static int enable_flash_ich10(struct pci_dev *dev, const char *name)
 {
-	return enable_flash_ich_dc_spi(dev, name, 10);
+	return enable_flash_ich_dc_spi(dev, name, CHIPSET_ICH10);
+}
+
+/* Ibex Peak aka. 5 series & 3400 series */
+static int enable_flash_pch5(struct pci_dev *dev, const char *name)
+{
+	return enable_flash_ich_dc_spi(dev, name, CHIPSET_5_SERIES_IBEX_PEAK);
+}
+
+/* Cougar Point aka. 6 series & c200 series */
+static int enable_flash_pch6(struct pci_dev *dev, const char *name)
+{
+	return enable_flash_ich_dc_spi(dev, name, CHIPSET_6_SERIES_COUGAR_POINT);
 }
 
 static int via_no_byte_merge(struct pci_dev *dev, const char *name)
@@ -528,8 +632,7 @@
 	uint8_t val;
 
 	val = pci_read_byte(dev, 0x71);
-	if (val & 0x40)
-	{
+	if (val & 0x40) {
 		msg_pdbg("Disabling byte merging\n");
 		val &= ~0x40;
 		rpci_write_byte(dev, 0x71, val);
@@ -541,7 +644,7 @@
 {
 	uint8_t val;
 
-	/* enable ROM decode range (1MB) FFC00000 - FFFFFFFF */
+	/* Enable ROM decode range (1MB) FFC00000 - FFFFFFFF. */
 	rpci_write_byte(dev, 0x41, 0x7f);
 
 	/* ROM write enable */
@@ -551,15 +654,15 @@
 
 	if (pci_read_byte(dev, 0x40) != val) {
 		msg_pinfo("\nWARNING: Failed to enable flash write on \"%s\"\n",
-		       name);
+			  name);
 		return -1;
 	}
 
 	if (dev->device_id == 0x3227) { /* VT8237R */
-	    /* All memory cycles, not just ROM ones, go to LPC. */
-	    val = pci_read_byte(dev, 0x59);
-	    val &= ~0x80;
-	    rpci_write_byte(dev, 0x59, val);
+		/* All memory cycles, not just ROM ones, go to LPC. */
+		val = pci_read_byte(dev, 0x59);
+		val &= ~0x80;
+		rpci_write_byte(dev, 0x59, val);
 	}
 
 	return 0;
@@ -582,7 +685,7 @@
 #define CS5530_ENABLE_SA2320		(1 << 2)
 #define CS5530_ENABLE_SA20		(1 << 6)
 
-	buses_supported = CHIP_BUSTYPE_PARALLEL;
+	internal_buses_supported = BUS_PARALLEL;
 	/* Decode 0x000E0000-0x000FFFFF (128 kB), not just 64 kB, and
 	 * decode 0xFF000000-0xFFFFFFFF (16 MB), not just 256 kB.
 	 * FIXME: Should we really touch the low mapping below 1 MB? Flashrom
@@ -667,7 +770,8 @@
 	new = pci_read_byte(dev, 0x52);
 
 	if (new != 0xee) {
-		msg_pinfo("tried to set register 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x52, new, name);
+		msg_pinfo("Setting register 0x%x to 0x%x on %s failed "
+			  "(WARNING ONLY).\n", 0x52, new, name);
 		return -1;
 	}
 
@@ -685,7 +789,8 @@
 	if (new != old) {
 		rpci_write_byte(dev, 0x43, new);
 		if (pci_read_byte(dev, 0x43) != new) {
-			msg_pinfo("tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x43, new, name);
+			msg_pinfo("Setting register 0x%x to 0x%x on %s failed "
+				  "(WARNING ONLY).\n", 0x43, new, name);
 		}
 	}
 
@@ -697,7 +802,8 @@
 	rpci_write_byte(dev, 0x40, new);
 
 	if (pci_read_byte(dev, 0x40) != new) {
-		msg_pinfo("tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x40, new, name);
+		msg_pinfo("Setting register 0x%x to 0x%x on %s failed "
+			  "(WARNING ONLY).\n", 0x40, new, name);
 		return -1;
 	}
 
@@ -717,22 +823,22 @@
 		if ((prot & 0x3) == 0)
 			continue;
 		msg_pinfo("SB600 %s%sprotected from 0x%08x to 0x%08x\n",
-			(prot & 0x1) ? "write " : "",
-			(prot & 0x2) ? "read " : "",
-			(prot & 0xfffff800),
-			(prot & 0xfffff800) + (((prot & 0x7fc) << 8) | 0x3ff));
+			  (prot & 0x1) ? "write " : "",
+			  (prot & 0x2) ? "read " : "",
+			  (prot & 0xfffff800),
+			  (prot & 0xfffff800) + (((prot & 0x7fc) << 8) | 0x3ff));
 		prot &= 0xfffffffc;
 		rpci_write_byte(dev, reg, prot);
 		prot = pci_read_long(dev, reg);
 		if (prot & 0x3)
 			msg_perr("SB600 %s%sunprotect failed from 0x%08x to 0x%08x\n",
-				(prot & 0x1) ? "write " : "",
-				(prot & 0x2) ? "read " : "",
-				(prot & 0xfffff800),
-				(prot & 0xfffff800) + (((prot & 0x7fc) << 8) | 0x3ff));
+				 (prot & 0x1) ? "write " : "",
+				 (prot & 0x2) ? "read " : "",
+				 (prot & 0xfffff800),
+				 (prot & 0xfffff800) + (((prot & 0x7fc) << 8) | 0x3ff));
 	}
 
-	buses_supported = CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH;
+	internal_buses_supported = BUS_LPC | BUS_FWH;
 
 	ret = sb600_probe_spi(dev);
 
@@ -791,12 +897,19 @@
 {
 	uint8_t old, new;
 
+	pci_write_byte(dev, 0x92, 0x00);
+	if (pci_read_byte(dev, 0x92) != 0x00) {
+		msg_pinfo("Setting register 0x%x to 0x%x on %s failed "
+			  "(WARNING ONLY).\n", 0x92, 0x00, name);
+	}
+
 	old = pci_read_byte(dev, 0x88);
 	new = old | 0xc0;
 	if (new != old) {
 		rpci_write_byte(dev, 0x88, new);
 		if (pci_read_byte(dev, 0x88) != new) {
-			msg_pinfo("tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x88, new, name);
+			msg_pinfo("Setting register 0x%x to 0x%x on %s failed "
+				  "(WARNING ONLY).\n", 0x88, new, name);
 		}
 	}
 
@@ -807,7 +920,8 @@
 	rpci_write_byte(dev, 0x6d, new);
 
 	if (pci_read_byte(dev, 0x6d) != new) {
-		msg_pinfo("tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x6d, new, name);
+		msg_pinfo("Setting register 0x%x to 0x%x on %s failed "
+			  "(WARNING ONLY).\n", 0x6d, new, name);
 		return -1;
 	}
 
@@ -818,7 +932,7 @@
 {
 	uint8_t tmp;
 
-	buses_supported = CHIP_BUSTYPE_PARALLEL;
+	internal_buses_supported = BUS_PARALLEL;
 
 	tmp = INB(0xc06);
 	tmp |= 0x1;
@@ -842,7 +956,7 @@
 
 	if (!smbusdev) {
 		msg_perr("ERROR: SMBus device not found. Aborting.\n");
-		exit(1);
+		return ERROR_FATAL;
 	}
 
 	/* Enable some SMBus stuff. */
@@ -890,7 +1004,8 @@
 	rpci_write_byte(dev, 0x6d, new);
 
 	if (pci_read_byte(dev, 0x6d) != new) {
-		msg_pinfo("tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x6d, new, name);
+		msg_pinfo("Setting register 0x%x to 0x%x on %s failed "
+			  "(WARNING ONLY).\n", 0x6d, new, name);
 		return -1;
 	}
 
@@ -904,8 +1019,7 @@
  */
 static int enable_flash_mcp6x_7x(struct pci_dev *dev, const char *name)
 {
-	int ret = 0;
-	int want_spi = 0;
+	int ret = 0, want_spi = 0;
 	uint8_t val;
 
 	msg_pinfo("This chipset is not really supported yet. Guesswork...\n");
@@ -918,7 +1032,7 @@
 	switch ((val >> 5) & 0x3) {
 	case 0x0:
 		ret = enable_flash_mcp55(dev, name);
-		buses_supported = CHIP_BUSTYPE_LPC;
+		internal_buses_supported = BUS_LPC;
 		msg_pdbg("Flash bus type is LPC\n");
 		break;
 	case 0x2:
@@ -926,14 +1040,15 @@
 		/* 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;
+		internal_buses_supported = BUS_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;
+		msg_pinfo("SPI on this chipset is WIP. Please report any "
+			  "success or failure by mailing us the verbose "
+			  "output to flashrom@flashrom.org, thanks!\n");
 		break;
 	default:
 		/* Should not happen. */
-		buses_supported = CHIP_BUSTYPE_NONE;
+		internal_buses_supported = BUS_NONE;
 		msg_pdbg("Flash bus type is unknown (none)\n");
 		msg_pinfo("Something went wrong with bus type detection.\n");
 		goto out_msg;
@@ -947,9 +1062,9 @@
 	rpci_write_byte(dev, 0x8a, val);
 #endif
 
-	if (mcp6x_spi_init(want_spi)) {
+	if (mcp6x_spi_init(want_spi))
 		ret = 1;
-	}
+
 out_msg:
 	msg_pinfo("Please send the output of \"flashrom -V\" to "
 		  "flashrom@flashrom.org with\n"
@@ -1015,7 +1130,8 @@
 			flashbase = parx << 12;
 		}
 	} else {
-		msg_pinfo("AMD Elan SC520 detected, but no BOOTCS. Assuming flash at 4G\n");
+		msg_pinfo("AMD Elan SC520 detected, but no BOOTCS. "
+			  "Assuming flash at 4G.\n");
 	}
 
 	/* 4. Clean up */
@@ -1037,19 +1153,20 @@
 	{0x1022, 0x3000, OK, "AMD", "Elan SC520",	get_flashbase_sc520},
 	{0x1022, 0x7440, OK, "AMD", "AMD-768",		enable_flash_amd8111},
 	{0x1022, 0x7468, OK, "AMD", "AMD8111",		enable_flash_amd8111},
+	{0x1022, 0x780e, OK, "AMD", "Hudson",		enable_flash_sb600},
 	{0x1039, 0x0406, NT, "SiS", "501/5101/5501",	enable_flash_sis501},
 	{0x1039, 0x0496, NT, "SiS", "85C496+497",	enable_flash_sis85c496},
-	{0x1039, 0x0530, NT, "SiS", "530",		enable_flash_sis530},
+	{0x1039, 0x0530, OK, "SiS", "530",		enable_flash_sis530},
 	{0x1039, 0x0540, NT, "SiS", "540",		enable_flash_sis540},
 	{0x1039, 0x0620, NT, "SiS", "620",		enable_flash_sis530},
 	{0x1039, 0x0630, NT, "SiS", "630",		enable_flash_sis540},
 	{0x1039, 0x0635, NT, "SiS", "635",		enable_flash_sis540},
 	{0x1039, 0x0640, NT, "SiS", "640",		enable_flash_sis540},
 	{0x1039, 0x0645, NT, "SiS", "645",		enable_flash_sis540},
-	{0x1039, 0x0646, NT, "SiS", "645DX",		enable_flash_sis540},
+	{0x1039, 0x0646, OK, "SiS", "645DX",		enable_flash_sis540},
 	{0x1039, 0x0648, NT, "SiS", "648",		enable_flash_sis540},
 	{0x1039, 0x0650, NT, "SiS", "650",		enable_flash_sis540},
-	{0x1039, 0x0651, NT, "SiS", "651",		enable_flash_sis540},
+	{0x1039, 0x0651, OK, "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},
@@ -1076,7 +1193,7 @@
 	{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},
+	{0x10de, 0x0260, OK, "NVIDIA", "MCP51",		enable_flash_ck804},
 	{0x10de, 0x0261, NT, "NVIDIA", "MCP51",		enable_flash_ck804},
 	{0x10de, 0x0262, NT, "NVIDIA", "MCP51",		enable_flash_ck804},
 	{0x10de, 0x0263, NT, "NVIDIA", "MCP51",		enable_flash_ck804},
@@ -1097,7 +1214,7 @@
 	{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_mcp6x_7x},
-	{0x10de, 0x03e1, NT, "NVIDIA", "MCP61",		enable_flash_mcp6x_7x},
+	{0x10de, 0x03e1, OK, "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},
@@ -1106,7 +1223,7 @@
 	{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, 0x075d, OK, "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},
@@ -1123,7 +1240,7 @@
 	{0x1106, 0x0586, OK, "VIA", "VT82C586A/B",	enable_flash_amd8111},
 	{0x1106, 0x0596, OK, "VIA", "VT82C596",		enable_flash_amd8111},
 	{0x1106, 0x0686, NT, "VIA", "VT82C686A/B",	enable_flash_amd8111},
-	{0x1106, 0x3074, NT, "VIA", "VT8233",		enable_flash_vt823x},
+	{0x1106, 0x3074, OK, "VIA", "VT8233",		enable_flash_vt823x},
 	{0x1106, 0x3147, OK, "VIA", "VT8233A",		enable_flash_vt823x},
 	{0x1106, 0x3177, OK, "VIA", "VT8235",		enable_flash_vt823x},
 	{0x1106, 0x3227, OK, "VIA", "VT8237",		enable_flash_vt823x},
@@ -1137,21 +1254,23 @@
 	{0x1166, 0x0205, OK, "Broadcom", "HT-1000",	enable_flash_ht1000},
 	{0x8086, 0x122e, OK, "Intel", "PIIX",		enable_flash_piix4},
 	{0x8086, 0x1234, NT, "Intel", "MPIIX",		enable_flash_piix4},
-	{0x8086, 0x1c44, NT, "Intel", "Z68",		enable_flash_ich10},
-	{0x8086, 0x1c46, NT, "Intel", "P67",		enable_flash_ich10},
-	{0x8086, 0x1c47, NT, "Intel", "UM67",		enable_flash_ich10},
-	{0x8086, 0x1c49, NT, "Intel", "HM65",		enable_flash_ich10},
-	{0x8086, 0x1c4a, NT, "Intel", "H67",		enable_flash_ich10},
-	{0x8086, 0x1c4b, NT, "Intel", "HM67",		enable_flash_ich10},
-	{0x8086, 0x1c4c, NT, "Intel", "Q65",		enable_flash_ich10},
-	{0x8086, 0x1c4d, NT, "Intel", "QS67",		enable_flash_ich10},
-	{0x8086, 0x1c4e, NT, "Intel", "Q67",		enable_flash_ich10},
-	{0x8086, 0x1c4f, NT, "Intel", "QM67",		enable_flash_ich10},
-	{0x8086, 0x1c50, NT, "Intel", "B65",		enable_flash_ich10},
-	{0x8086, 0x1c52, NT, "Intel", "C202",		enable_flash_ich10},
-	{0x8086, 0x1c54, NT, "Intel", "C204",		enable_flash_ich10},
-	{0x8086, 0x1c56, NT, "Intel", "C206",		enable_flash_ich10},
-	{0x8086, 0x1c5c, NT, "Intel", "H61",		enable_flash_ich10},
+	{0x8086, 0x1c44, OK, "Intel", "Z68",		enable_flash_pch6},
+	{0x8086, 0x1c46, OK, "Intel", "P67",		enable_flash_pch6},
+	{0x8086, 0x1c47, NT, "Intel", "UM67",		enable_flash_pch6},
+	{0x8086, 0x1c49, OK, "Intel", "HM65",		enable_flash_pch6},
+	{0x8086, 0x1c4a, OK, "Intel", "H67",		enable_flash_pch6},
+	{0x8086, 0x1c4b, NT, "Intel", "HM67",		enable_flash_pch6},
+	{0x8086, 0x1c4c, NT, "Intel", "Q65",		enable_flash_pch6},
+	{0x8086, 0x1c4d, NT, "Intel", "QS67",		enable_flash_pch6},
+	{0x8086, 0x1c4e, NT, "Intel", "Q67",		enable_flash_pch6},
+	{0x8086, 0x1c4f, NT, "Intel", "QM67",		enable_flash_pch6},
+	{0x8086, 0x1c50, NT, "Intel", "B65",		enable_flash_pch6},
+	{0x8086, 0x1c52, NT, "Intel", "C202",		enable_flash_pch6},
+	{0x8086, 0x1c54, NT, "Intel", "C204",		enable_flash_pch6},
+	{0x8086, 0x1c56, NT, "Intel", "C206",		enable_flash_pch6},
+	{0x8086, 0x1c5c, NT, "Intel", "H61",		enable_flash_pch6},
+	{0x8086, 0x1d40, OK, "Intel", "X79",		enable_flash_ich10}, /* FIXME: when datasheet is available */
+	{0x8086, 0x1d41, NT, "Intel", "X79",		enable_flash_ich10}, /* FIXME: when datasheet is available */
 	{0x8086, 0x2410, OK, "Intel", "ICH",		enable_flash_ich_4e},
 	{0x8086, 0x2420, OK, "Intel", "ICH0",		enable_flash_ich_4e},
 	{0x8086, 0x2440, OK, "Intel", "ICH2",		enable_flash_ich_4e},
@@ -1190,29 +1309,29 @@
 	{0x8086, 0x3a18, OK, "Intel", "ICH10",		enable_flash_ich10},
 	{0x8086, 0x3a1a, OK, "Intel", "ICH10D",		enable_flash_ich10},
 	{0x8086, 0x3a1e, NT, "Intel", "ICH10 Engineering Sample", enable_flash_ich10},
-	{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, OK, "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, 0x3b00, NT, "Intel", "3400 Desktop",	enable_flash_pch5},
+	{0x8086, 0x3b01, NT, "Intel", "3400 Mobile",	enable_flash_pch5},
+	{0x8086, 0x3b02, NT, "Intel", "P55",		enable_flash_pch5},
+	{0x8086, 0x3b03, NT, "Intel", "PM55",		enable_flash_pch5},
+	{0x8086, 0x3b06, OK, "Intel", "H55",		enable_flash_pch5},
+	{0x8086, 0x3b07, OK, "Intel", "QM57",		enable_flash_pch5},
+	{0x8086, 0x3b08, NT, "Intel", "H57",		enable_flash_pch5},
+	{0x8086, 0x3b09, NT, "Intel", "HM55",		enable_flash_pch5},
+	{0x8086, 0x3b0a, NT, "Intel", "Q57",		enable_flash_pch5},
+	{0x8086, 0x3b0b, NT, "Intel", "HM57",		enable_flash_pch5},
+	{0x8086, 0x3b0d, NT, "Intel", "3400 Mobile SFF", enable_flash_pch5},
+	{0x8086, 0x3b0e, NT, "Intel", "B55",		enable_flash_pch5},
+	{0x8086, 0x3b0f, OK, "Intel", "QS57",		enable_flash_pch5},
+	{0x8086, 0x3b12, NT, "Intel", "3400",		enable_flash_pch5},
+	{0x8086, 0x3b14, NT, "Intel", "3420",		enable_flash_pch5},
+	{0x8086, 0x3b16, NT, "Intel", "3450",		enable_flash_pch5},
+	{0x8086, 0x3b1e, NT, "Intel", "B55",		enable_flash_pch5},
 	{0x8086, 0x5031, OK, "Intel", "EP80579",	enable_flash_ich7},
 	{0x8086, 0x7000, OK, "Intel", "PIIX3",		enable_flash_piix4},
 	{0x8086, 0x7110, OK, "Intel", "PIIX4/4E/4M",	enable_flash_piix4},
 	{0x8086, 0x7198, OK, "Intel", "440MX",		enable_flash_piix4},
 	{0x8086, 0x8119, OK, "Intel", "SCH Poulsbo",	enable_flash_poulsbo},
-	{0x8086, 0x8186, NT, "Intel", "Atom E6xx(T)/Tunnel Creek", enable_flash_poulsbo},
+	{0x8086, 0x8186, OK, "Intel", "Atom E6xx(T)/Tunnel Creek", enable_flash_tunnelcreek},
 #endif
 	{},
 };
@@ -1222,7 +1341,6 @@
 	struct pci_dev *dev = NULL;
 	int ret = -2;		/* Nothing! */
 	int i;
-	char *s;
 
 	/* Now let's try to find the chipset we have... */
 	for (i = 0; chipset_enables[i].vendor_name != NULL; i++) {
@@ -1241,13 +1359,22 @@
 					chipset_enables[i].device_name);
 			continue;
 		}
-		msg_pdbg("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, ",
+		msg_pdbg("Found chipset \"%s %s\"",
+			  chipset_enables[i].vendor_name,
+			  chipset_enables[i].device_name);
+		msg_pdbg(" with PCI ID %04x:%04x",
 			 chipset_enables[i].vendor_id,
 			 chipset_enables[i].device_id);
+		msg_pdbg(". ");
 
+		if (chipset_enables[i].status == NT) {
+			msg_pinfo("\nThis chipset is marked as untested. If "
+				  "you are using an up-to-date version\nof "
+				  "flashrom please email a report to "
+				  "flashrom@flashrom.org including a\nverbose "
+				  "(-V) log. Thank you!\n");
+		}
+		msg_pdbg("Enabling flash write... ");
 		ret = chipset_enables[i].doit(dev,
 					      chipset_enables[i].device_name);
 		if (ret == NOT_DONE_YET) {
@@ -1255,16 +1382,16 @@
 			msg_pdbg("OK - searching further chips.\n");
 		} else if (ret < 0)
 			msg_pinfo("FAILED!\n");
-		else if(ret == 0)
+		else if (ret == 0)
 			msg_pdbg("OK.\n");
-		else if(ret == ERROR_NONFATAL)
+		else if (ret == ERROR_NONFATAL)
 			msg_pinfo("PROBLEMS, continuing anyway\n");
+		if (ret == ERROR_FATAL) {
+			msg_perr("FATAL ERROR!\n");
+			return ret;
+		}
 	}
 
-	s = flashbuses_to_text(buses_supported);
-	msg_pdbg("This chipset supports the following protocols: %s.\n", s);
-	free(s);
-
 	return ret;
 }
 
@@ -1300,13 +1427,13 @@
 			gcs = mmio_readl(rcrb + 0x3410);
 			switch ((gcs & 0xc00) >> 10) {
 			case 0x0:
-				*bus = CHIP_BUSTYPE_LPC;
+				*bus = BUS_LPC;
 				break;
 			case 0x3:
-				*bus = CHIP_BUSTYPE_SPI;
+				*bus = BUS_SPI;
 				break;
 			default:
-				*bus = CHIP_BUSTYPE_UNKNOWN;
+				*bus = BUS_NONE;
 				ret = -2;  /* unknown bus type. */
 				break;
 			}
@@ -1321,13 +1448,13 @@
 			gcs = mmio_readl(rcrb + 0x3410);
 			switch ((gcs & 0xc00) >> 10) {
 			case 0x1:
-				*bus = CHIP_BUSTYPE_SPI;
+				*bus = BUS_SPI;
 				break;
 			case 0x3:
-				*bus = CHIP_BUSTYPE_LPC;
+				*bus = BUS_LPC;
 				break;
 			default:
-				*bus = CHIP_BUSTYPE_UNKNOWN;
+				*bus = BUS_NONE;
 				ret = -2;  /* unknown bus type. */
 				break;
 			}
diff --git a/cli_classic.c b/cli_classic.c
index 8538b3f..cbf5591 100644
--- a/cli_classic.c
+++ b/cli_classic.c
@@ -31,6 +31,74 @@
 #include "flashchips.h"
 #include "programmer.h"
 
+#if CONFIG_INTERNAL == 1
+static enum programmer default_programmer = PROGRAMMER_INTERNAL;
+#elif CONFIG_DUMMY == 1
+static enum programmer default_programmer = PROGRAMMER_DUMMY;
+#else
+/* If neither internal nor dummy are selected, we must pick a sensible default.
+ * Since there is no reason to prefer a particular external programmer, we fail
+ * if more than one of them is selected. If only one is selected, it is clear
+ * that the user wants that one to become the default.
+ */
+#if 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+CONFIG_NICINTEL_SPI+CONFIG_OGP_SPI+CONFIG_SATAMV > 1
+#error Please enable either CONFIG_DUMMY or CONFIG_INTERNAL or disable support for all programmers except one.
+#endif
+static enum programmer default_programmer =
+#if CONFIG_NIC3COM == 1
+	PROGRAMMER_NIC3COM
+#endif
+#if CONFIG_NICREALTEK == 1
+	PROGRAMMER_NICREALTEK
+#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_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 == 1
+	PROGRAMMER_NICINTEL
+#endif
+#if CONFIG_NICINTEL_SPI == 1
+	PROGRAMMER_NICINTEL_SPI
+#endif
+#if CONFIG_OGP_SPI == 1
+	PROGRAMMER_OGP_SPI
+#endif
+#if CONFIG_SATAMV == 1
+	PROGRAMMER_SATAMV
+#endif
+#if CONFIG_LINUX_SPI == 1
+	PROGRAMMER_LINUX_SPI
+#endif
+;
+#endif
+
 static void cli_classic_usage(const char *name)
 {
 	printf("Usage: flashrom [-n] [-V] [-f] [-h|-R|-L|"
@@ -96,50 +164,47 @@
 	exit(1);
 }
 
-int cli_classic(int argc, char *argv[])
+int main(int argc, char *argv[])
 {
 	unsigned long size;
 	/* Probe for up to three flash chips. */
 	const struct flashchip *flash;
 	struct flashchip flashes[3];
 	struct flashchip *fill_flash;
-	int startchip = 0;
-	int chipcount = 0;
 	const char *name;
-	int namelen;
-	int opt;
-	int option_index = 0;
-	int force = 0;
-	int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0;
-	int dont_verify_it = 0, list_supported = 0;
+	int namelen, opt, i;
+	int startchip = 0, chipcount = 0, option_index = 0, force = 0;
 #if CONFIG_PRINT_WIKI == 1
 	int list_supported_wiki = 0;
 #endif
-	int operation_specified = 0;
-	int i;
+	int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0;
+	int dont_verify_it = 0, list_supported = 0, operation_specified = 0;
+	enum programmer prog = PROGRAMMER_INVALID;
+	int ret = 0;
 
 	static const char optstring[] = "r:Rw:v:nVEfc:m:l:i:p:Lzh";
 	static const struct option long_options[] = {
-		{"read", 1, NULL, 'r'},
-		{"write", 1, NULL, 'w'},
-		{"erase", 0, NULL, 'E'},
-		{"verify", 1, NULL, 'v'},
-		{"noverify", 0, NULL, 'n'},
-		{"chip", 1, NULL, 'c'},
-		{"mainboard", 1, NULL, 'm'},
-		{"verbose", 0, NULL, 'V'},
-		{"force", 0, NULL, 'f'},
-		{"layout", 1, NULL, 'l'},
-		{"image", 1, NULL, 'i'},
-		{"list-supported", 0, NULL, 'L'},
-		{"list-supported-wiki", 0, NULL, 'z'},
-		{"programmer", 1, NULL, 'p'},
-		{"help", 0, NULL, 'h'},
-		{"version", 0, NULL, 'R'},
-		{NULL, 0, NULL, 0}
+		{"read",		1, NULL, 'r'},
+		{"write",		1, NULL, 'w'},
+		{"erase",		0, NULL, 'E'},
+		{"verify",		1, NULL, 'v'},
+		{"noverify",		0, NULL, 'n'},
+		{"chip",		1, NULL, 'c'},
+		{"mainboard",		1, NULL, 'm'},
+		{"verbose",		0, NULL, 'V'},
+		{"force",		0, NULL, 'f'},
+		{"layout",		1, NULL, 'l'},
+		{"image",		1, NULL, 'i'},
+		{"list-supported",	0, NULL, 'L'},
+		{"list-supported-wiki",	0, NULL, 'z'},
+		{"programmer",		1, NULL, 'p'},
+		{"help",		0, NULL, 'h'},
+		{"version",		0, NULL, 'R'},
+		{NULL,			0, NULL, 0},
 	};
 
 	char *filename = NULL;
+	char *diff_file = NULL;
 
 	char *tempstr = NULL;
 	char *pparam = NULL;
@@ -232,9 +297,14 @@
 				cli_classic_abort_usage();
 			break;
 		case 'i':
+			/* FIXME: -l has to be specified before -i. */
 			tempstr = strdup(optarg);
-			if (register_include_arg(tempstr) < 0)
-				exit(1);
+			if (find_romentry(tempstr) < 0) {
+				fprintf(stderr, "Error: image %s not found in "
+					"layout file or -i specified before "
+					"-l\n", tempstr);
+				cli_classic_abort_usage();
+			}
 			break;
 		case 'L':
 			if (++operation_specified > 1) {
@@ -259,8 +329,16 @@
 #endif
 			break;
 		case 'p':
-			for (programmer = 0; programmer < PROGRAMMER_INVALID; programmer++) {
-				name = programmer_table[programmer].name;
+			if (prog != PROGRAMMER_INVALID) {
+				fprintf(stderr, "Error: --programmer specified "
+					"more than once. You can separate "
+					"multiple\nparameters for a programmer "
+					"with \",\". Please see the man page "
+					"for details.\n");
+				cli_classic_abort_usage();
+			}
+			for (prog = 0; prog < PROGRAMMER_INVALID; prog++) {
+				name = programmer_table[prog].name;
 				namelen = strlen(name);
 				if (strncmp(optarg, name, namelen) == 0) {
 					switch (optarg[namelen]) {
@@ -284,7 +362,7 @@
 					break;
 				}
 			}
-			if (programmer == PROGRAMMER_INVALID) {
+			if (prog == PROGRAMMER_INVALID) {
 				fprintf(stderr, "Error: Unknown programmer "
 					"%s.\n", optarg);
 				cli_classic_abort_usage();
@@ -314,6 +392,11 @@
 		}
 	}
 
+	if (optind < argc) {
+		fprintf(stderr, "Error: Extra parameter found.\n");
+		cli_classic_abort_usage();
+	}
+
 	/* FIXME: Print the actions flashrom will take. */
 
 	if (list_supported) {
@@ -328,19 +411,6 @@
 	}
 #endif
 
-	if (optind < argc) {
-		fprintf(stderr, "Error: Extra parameter found.\n");
-		cli_classic_abort_usage();
-	}
-
-#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();
-	}
-#endif
-
 	if (chip_to_probe) {
 		for (flash = flashchips; flash && flash->name; flash++)
 			if (!strcmp(flash->name, chip_to_probe))
@@ -349,21 +419,36 @@
 			fprintf(stderr, "Error: Unknown chip '%s' specified.\n",
 				chip_to_probe);
 			printf("Run flashrom -L to view the hardware supported "
-				"in this flashrom version.\n");
+			       "in this flashrom version.\n");
 			exit(1);
 		}
 		/* Clean up after the check. */
 		flash = NULL;
 	}
 
+	if (prog == PROGRAMMER_INVALID)
+		prog = default_programmer;
+
+#if CONFIG_INTERNAL == 1
+	if ((prog != PROGRAMMER_INTERNAL) && (lb_part || lb_vendor)) {
+		fprintf(stderr, "Error: --mainboard requires the internal "
+				"programmer. Aborting.\n");
+		cli_classic_abort_usage();
+	}
+#endif
+
 	/* FIXME: Delay calibration should happen in programmer code. */
 	myusec_calibrate_delay();
 
-	if (programmer_init(pparam)) {
+	if (programmer_init(prog, pparam)) {
 		fprintf(stderr, "Error: Programmer initialization failed.\n");
-		programmer_shutdown();
-		exit(1);
+		ret = 1;
+		goto out_shutdown;
 	}
+	tempstr = flashbuses_to_text(buses_supported);
+	msg_pdbg("This programmer supports the following protocols: %s.\n",
+		 tempstr);
+	free(tempstr);
 
 	for (i = 0; i < ARRAY_SIZE(flashes); i++) {
 		startchip = probe_flash(startchip, &flashes[i], 0);
@@ -374,31 +459,43 @@
 	}
 
 	if (chipcount > 1) {
-		printf("Multiple flash chips were detected:");
-		for (i = 0; i < chipcount; i++)
-			printf(" %s", flashes[i].name);
-		printf("\nPlease specify which chip to use with the -c <chipname> option.\n");
-		programmer_shutdown();
-		exit(1);
+		printf("Multiple flash chips were detected: \"%s\"",
+			flashes[0].name);
+		for (i = 1; i < chipcount; i++)
+			printf(", \"%s\"", flashes[i].name);
+		printf("\nPlease specify which chip to use with the "
+		       "-c <chipname> option.\n");
+		ret = 1;
+		goto out_shutdown;
 	} else if (!chipcount) {
 		printf("No EEPROM/flash device found.\n");
 		if (!force || !chip_to_probe) {
-			printf("Note: flashrom can never write if the flash chip isn't found automatically.\n");
+			printf("Note: flashrom can never write if the flash "
+			       "chip isn't found automatically.\n");
 		}
 		if (force && read_it && chip_to_probe) {
-			printf("Force read (-f -r -c) requested, pretending the chip is there:\n");
+			printf("Force read (-f -r -c) requested, pretending "
+			       "the chip is there:\n");
 			startchip = probe_flash(0, &flashes[0], 1);
 			if (startchip == -1) {
-				printf("Probing for flash chip '%s' failed.\n", chip_to_probe);
-				programmer_shutdown();
-				exit(1);
+				printf("Probing for flash chip '%s' failed.\n",
+				       chip_to_probe);
+				ret = 1;
+				goto out_shutdown;
 			}
-			printf("Please note that forced reads most likely contain garbage.\n");
+			printf("Please note that forced reads most likely "
+			       "contain garbage.\n");
 			return read_flash_to_file(&flashes[0], filename);
 		}
-		// FIXME: flash writes stay enabled!
-		programmer_shutdown();
-		exit(1);
+		ret = 1;
+		goto out_shutdown;
+	} else if (!chip_to_probe) {
+		/* repeat for convenience when looking at foreign logs */
+		tempstr = flashbuses_to_text(flashes[0].bustype);
+		msg_gdbg("Found %s flash chip \"%s\" (%d kB, %s).\n",
+			 flashes[0].vendor, flashes[0].name,
+			 flashes[0].total_size, tempstr);
+		free(tempstr);
 	}
 
 	fill_flash = &flashes[0];
@@ -410,22 +507,19 @@
 	    (!force)) {
 		fprintf(stderr, "Chip is too big for this programmer "
 			"(-V gives details). Use --force to override.\n");
-		programmer_shutdown();
-		return 1;
+		ret = 1;
+		goto out_shutdown;
 	}
 
 	if (!(read_it | write_it | verify_it | erase_it)) {
 		printf("No operations were specified.\n");
-		// FIXME: flash writes stay enabled!
-		programmer_shutdown();
-		exit(0);
+		goto out_shutdown;
 	}
 
 	if (!filename && !erase_it) {
 		printf("Error: No filename specified.\n");
-		// FIXME: flash writes stay enabled!
-		programmer_shutdown();
-		exit(1);
+		ret = 1;
+		goto out_shutdown;
 	}
 
 	/* Always verify write operations unless -n is used. */
@@ -437,5 +531,9 @@
 	 * Give the chip time to settle.
 	 */
 	programmer_delay(100000);
-	return doit(fill_flash, force, filename, read_it, write_it, erase_it, verify_it);
+	return doit(fill_flash, force, filename, read_it, write_it, erase_it, verify_it, diff_file);
+
+out_shutdown:
+	programmer_shutdown();
+	return ret;
 }
diff --git a/cli_mfg.c b/cli_mfg.c
index 4499b3c..57b3953 100644
--- a/cli_mfg.c
+++ b/cli_mfg.c
@@ -41,6 +41,74 @@
 /* This variable is shared with doit() in flashrom.c */
 int set_ignore_fmap = 0;
 
+#if CONFIG_INTERNAL == 1
+static enum programmer default_programmer = PROGRAMMER_INTERNAL;
+#elif CONFIG_DUMMY == 1
+static enum programmer default_programmer = PROGRAMMER_DUMMY;
+#else
+/* If neither internal nor dummy are selected, we must pick a sensible default.
+ * Since there is no reason to prefer a particular external programmer, we fail
+ * if more than one of them is selected. If only one is selected, it is clear
+ * that the user wants that one to become the default.
+ */
+#if 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+CONFIG_NICINTEL_SPI+CONFIG_OGP_SPI+CONFIG_SATAMV > 1
+#error Please enable either CONFIG_DUMMY or CONFIG_INTERNAL or disable support for all programmers except one.
+#endif
+static enum programmer default_programmer =
+#if CONFIG_NIC3COM == 1
+	PROGRAMMER_NIC3COM
+#endif
+#if CONFIG_NICREALTEK == 1
+	PROGRAMMER_NICREALTEK
+#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_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 == 1
+	PROGRAMMER_NICINTEL
+#endif
+#if CONFIG_NICINTEL_SPI == 1
+	PROGRAMMER_NICINTEL_SPI
+#endif
+#if CONFIG_OGP_SPI == 1
+	PROGRAMMER_OGP_SPI
+#endif
+#if CONFIG_SATAMV == 1
+	PROGRAMMER_SATAMV
+#endif
+#if CONFIG_LINUX_SPI == 1
+	PROGRAMMER_LINUX_SPI
+#endif
+;
+#endif
+
 void cli_mfg_usage(const char *name)
 {
 	const char *pname;
@@ -84,6 +152,7 @@
 	       "   -f | --force                      force specific operations "
 	         "(see man page)\n"
 	       "   -n | --noverify                   don't auto-verify\n"
+	       "        --fast-verify                only verify -i part\n"
 	       "   -l | --layout <file>              read ROM layout from "
 	         "<file>\n"
 	       "   -i | --image <name>[:<file>]      only access image <name> "
@@ -128,6 +197,7 @@
 	}
 
 	printf("Long-options:\n");
+	printf("   --diff <file>                     diff from file instead of ROM\n");
 	printf("   --get-size                        get chip size (bytes)\n");
 	printf("   --wp-status                       show write protect status\n");
 	printf("   --wp-range <start> <length>       set write protect range\n");
@@ -154,6 +224,7 @@
 enum LONGOPT_RETURN_VALUES {
 	/* start after ASCII chars */
 	LONGOPT_GET_SIZE = 256,
+	LONGOPT_DIFF,
 	LONGOPT_WP_STATUS,
 	LONGOPT_WP_SET_RANGE,
 	LONGOPT_WP_ENABLE,
@@ -163,7 +234,7 @@
 	LONGOPT_FAST_VERIFY,
 };
 
-int cli_mfg(int argc, char *argv[])
+int main(int argc, char *argv[])
 {
 	unsigned long size;
 	/* Probe for up to three flash chips. */
@@ -181,11 +252,13 @@
 	    get_size = 0, set_wp_range = 0, set_wp_enable = 0,
 	    set_wp_disable = 0, wp_status = 0, wp_list = 0;
 	int dont_verify_it = 0, list_supported = 0;
+	int diff = 0;
 #if CONFIG_PRINT_WIKI == 1
 	int list_supported_wiki = 0;
 #endif
 	int operation_specified = 0;
 	int i;
+	enum programmer prog = PROGRAMMER_INVALID;
 	int rc = 0;
 
 	const char *optstring = "r:Rw:v:nVEfc:m:l:i:p:Lzhb";
@@ -207,6 +280,7 @@
 		{"help", 0, 0, 'h'},
 		{"version", 0, 0, 'R'},
 		{"get-size", 0, 0, LONGOPT_GET_SIZE},
+		{"diff", 1, 0, LONGOPT_DIFF},
 		{"wp-status", 0, 0, LONGOPT_WP_STATUS},
 		{"wp-range", 0, 0, LONGOPT_WP_SET_RANGE},
 		{"wp-enable", 0, 0, LONGOPT_WP_ENABLE},
@@ -219,6 +293,7 @@
 	};
 
 	char *filename = NULL;
+	char *diff_file = NULL;
 
 	char *tempstr = NULL;
 	char *pparam = NULL;
@@ -349,8 +424,16 @@
 #endif
 			break;
 		case 'p':
-			for (programmer = 0; programmer < PROGRAMMER_INVALID; programmer++) {
-				name = programmer_table[programmer].name;
+			if (prog != PROGRAMMER_INVALID) {
+				fprintf(stderr, "Error: --programmer specified "
+					"more than once. You can separate "
+					"multiple\nparameters for a programmer "
+					"with \",\". Please see the man page "
+					"for details.\n");
+				cli_mfg_abort_usage(argv[0]);
+			}
+			for (prog = 0; prog < PROGRAMMER_INVALID; prog++) {
+				name = programmer_table[prog].name;
 				namelen = strlen(name);
 				if (strncmp(optarg, name, namelen) == 0) {
 					switch (optarg[namelen]) {
@@ -374,7 +457,7 @@
 					break;
 				}
 			}
-			if (programmer == PROGRAMMER_INVALID) {
+			if (prog == PROGRAMMER_INVALID) {
 				fprintf(stderr, "Error: Unknown programmer "
 					"%s.\n", optarg);
 				cli_mfg_abort_usage(argv[0]);
@@ -416,6 +499,10 @@
 		case LONGOPT_WP_DISABLE:
 			set_wp_disable = 1;
 			break;
+		case LONGOPT_DIFF:
+			diff = 1;
+			diff_file = strdup(optarg);
+			break;
 		case LONGOPT_IGNORE_FMAP:
 			set_ignore_fmap = 1;
 			break;
@@ -452,14 +539,6 @@
 	}
 #endif
 
-#if CONFIG_INTERNAL == 1
-	if ((programmer != PROGRAMMER_INTERNAL) && (lb_part || lb_vendor)) {
-		fprintf(stderr, "Error: --mainboard requires the internal "
-				"programmer. Aborting.\n");
-		cli_mfg_abort_usage(argv[0]);
-	}
-#endif
-
 	if (chip_to_probe) {
 		for (flash = flashchips; flash && flash->name; flash++)
 			if (!strcmp(flash->name, chip_to_probe))
@@ -475,6 +554,17 @@
 		flash = NULL;
 	}
 
+	if (prog == PROGRAMMER_INVALID)
+		prog = default_programmer;
+
+#if CONFIG_INTERNAL == 1
+	if ((prog != PROGRAMMER_INTERNAL) && (lb_part || lb_vendor)) {
+		fprintf(stderr, "Error: --mainboard requires the internal "
+				"programmer. Aborting.\n");
+		cli_mfg_abort_usage(argv[0]);
+	}
+#endif
+
 #if USE_BIG_LOCK == 1
 	/* get lock before doing any work that touches hardware */
 	msg_gdbg("Acquiring lock (timeout=%d sec)...\n", LOCK_TIMEOUT_SECS);
@@ -496,7 +586,7 @@
 	/* FIXME: Delay calibration should happen in programmer code. */
 	myusec_calibrate_delay();
 
-	if (programmer_init(pparam)) {
+	if (programmer_init(prog, pparam)) {
 		fprintf(stderr, "Error: Programmer initialization failed.\n");
 		rc = 1;
 		goto cli_mfg_release_lock_exit;
@@ -646,7 +736,8 @@
 
 	if (read_it || write_it || erase_it || verify_it) {
 		rc = doit(fill_flash, force, filename,
-		          read_it, write_it, erase_it, verify_it);
+		          read_it, write_it, erase_it, verify_it,
+		          diff_file);
 	}
 
 	msg_ginfo("%s\n", rc ? "FAILED" : "SUCCESS");
diff --git a/cli_output.c b/cli_output.c
index 1c85f7a..2a67bea 100644
--- a/cli_output.c
+++ b/cli_output.c
@@ -33,6 +33,9 @@
 		output_type = stderr;
 		break;
 	case MSG_BARF:
+		if (verbose < 3)
+			return 0;
+	case MSG_DEBUG2:
 		if (verbose < 2)
 			return 0;
 	case MSG_DEBUG:
diff --git a/dediprog.c b/dediprog.c
index 1cbde18..ca2d3e0 100644
--- a/dediprog.c
+++ b/dediprog.c
@@ -71,7 +71,7 @@
 static int dediprog_set_leds(int leds)
 {
 	int ret, target_leds;
-	
+
 	if (leds < 0 || leds > 7)
 		leds = 0; // Bogus value, enable all LEDs
 
@@ -92,9 +92,11 @@
 		target_leds = leds;
 	}
 
-	ret = usb_control_msg(dediprog_handle, 0x42, 0x07, 0x09, target_leds, NULL, 0x0, DEFAULT_TIMEOUT);
+	ret = usb_control_msg(dediprog_handle, 0x42, 0x07, 0x09, target_leds,
+			      NULL, 0x0, DEFAULT_TIMEOUT);
 	if (ret != 0x0) {
-		msg_perr("Command Set LED 0x%x failed (%s)!\n", leds, usb_strerror());
+		msg_perr("Command Set LED 0x%x failed (%s)!\n",
+			 leds, usb_strerror());
 		return 1;
 	}
 
@@ -129,9 +131,11 @@
 	msg_pdbg("Setting SPI voltage to %u.%03u V\n", millivolt / 1000,
 		 millivolt % 1000);
 
-	ret = usb_control_msg(dediprog_handle, 0x42, 0x9, voltage_selector, 0xff, NULL, 0x0, DEFAULT_TIMEOUT);
+	ret = usb_control_msg(dediprog_handle, 0x42, 0x9, voltage_selector,
+			      0xff, NULL, 0x0, DEFAULT_TIMEOUT);
 	if (ret != 0x0) {
-		msg_perr("Command Set SPI Voltage 0x%x failed!\n", voltage_selector);
+		msg_perr("Command Set SPI Voltage 0x%x failed!\n",
+			 voltage_selector);
 		return 1;
 	}
 	return 0;
@@ -186,7 +190,8 @@
 	}
 	msg_pdbg("Setting SPI speed to %u kHz\n", khz);
 
-	ret = usb_control_msg(dediprog_handle, 0x42, 0x61, speed, 0xff, NULL, 0x0, DEFAULT_TIMEOUT);
+	ret = usb_control_msg(dediprog_handle, 0x42, 0x61, speed, 0xff, NULL,
+			      0x0, DEFAULT_TIMEOUT);
 	if (ret != 0x0) {
 		msg_perr("Command Set SPI Speed 0x%x failed!\n", speed);
 		return 1;
@@ -201,13 +206,13 @@
  * @return	0 on success, 1 on failure
  */
 static int dediprog_spi_bulk_read(struct flashchip *flash, uint8_t *buf,
-				  int start, int len)
+				  unsigned int start, unsigned int len)
 {
 	int ret;
-	int i;
+	unsigned int i;
 	/* chunksize must be 512, other sizes will NOT work at all. */
-	const int chunksize = 0x200;
-	const int count = len / chunksize;
+	const unsigned int chunksize = 0x200;
+	const unsigned int count = len / chunksize;
 	const char count_and_chunk[] = {count & 0xff,
 					(count >> 8) & 0xff,
 					chunksize & 0xff,
@@ -248,13 +253,14 @@
 	return 0;
 }
 
-static int dediprog_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+static int dediprog_spi_read(struct flashchip *flash, uint8_t *buf,
+			     unsigned int start, unsigned int len)
 {
 	int ret;
 	/* chunksize must be 512, other sizes will NOT work at all. */
-	const int chunksize = 0x200;
-	int residue = start % chunksize ? chunksize - start % chunksize : 0;
-	int bulklen;
+	const unsigned int chunksize = 0x200;
+	unsigned int residue = start % chunksize ? chunksize - start % chunksize : 0;
+	unsigned int bulklen;
 
 	dediprog_set_leds(PASS_OFF|BUSY_ON|ERROR_OFF);
 
@@ -293,7 +299,8 @@
 	return 0;
 }
 
-static int dediprog_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
+static int dediprog_spi_write_256(struct flashchip *flash, uint8_t *buf,
+				  unsigned int start, unsigned int len)
 {
 	int ret;
 
@@ -327,7 +334,9 @@
 		return 1;
 	}
 	
-	ret = usb_control_msg(dediprog_handle, 0x42, 0x1, 0xff, readcnt ? 0x1 : 0x0, (char *)writearr, writecnt, DEFAULT_TIMEOUT);
+	ret = usb_control_msg(dediprog_handle, 0x42, 0x1, 0xff,
+			      readcnt ? 0x1 : 0x0, (char *)writearr, writecnt,
+			      DEFAULT_TIMEOUT);
 	if (ret != writecnt) {
 		msg_perr("Send SPI failed, expected %i, got %i %s!\n",
 			 writecnt, ret, usb_strerror());
@@ -336,7 +345,8 @@
 	if (!readcnt)
 		return 0;
 	memset(readarr, 0, readcnt);
-	ret = usb_control_msg(dediprog_handle, 0xc2, 0x01, 0xbb8, 0x0000, (char *)readarr, readcnt, DEFAULT_TIMEOUT);
+	ret = usb_control_msg(dediprog_handle, 0xc2, 0x01, 0xbb8, 0x0000,
+			     (char *)readarr, readcnt, DEFAULT_TIMEOUT);
 	if (ret != readcnt) {
 		msg_perr("Receive SPI failed, expected %i, got %i %s!\n",
 			 readcnt, ret, usb_strerror());
@@ -353,7 +363,8 @@
 
 	/* Command Prepare Receive Device String. */
 	memset(buf, 0, sizeof(buf));
-	ret = usb_control_msg(dediprog_handle, 0xc3, 0x7, 0x0, 0xef03, buf, 0x1, DEFAULT_TIMEOUT);
+	ret = usb_control_msg(dediprog_handle, 0xc3, 0x7, 0x0, 0xef03, buf,
+			      0x1, DEFAULT_TIMEOUT);
 	/* The char casting is needed to stop gcc complaining about an always true comparison. */
 	if ((ret != 0x1) || (buf[0] != (char)0xff)) {
 		msg_perr("Unexpected response to Command Prepare Receive Device"
@@ -362,7 +373,8 @@
 	}
 	/* Command Receive Device String. */
 	memset(buf, 0, sizeof(buf));
-	ret = usb_control_msg(dediprog_handle, 0xc2, 0x8, 0xff, 0xff, buf, 0x10, DEFAULT_TIMEOUT);
+	ret = usb_control_msg(dediprog_handle, 0xc2, 0x8, 0xff, 0xff, buf,
+			      0x10, DEFAULT_TIMEOUT);
 	if (ret != 0x10) {
 		msg_perr("Incomplete/failed Command Receive Device String!\n");
 		return 1;
@@ -397,7 +409,8 @@
 	char buf[0x1];
 
 	memset(buf, 0, sizeof(buf));
-	ret = usb_control_msg(dediprog_handle, 0xc3, 0xb, 0x0, 0x0, buf, 0x1, DEFAULT_TIMEOUT);
+	ret = usb_control_msg(dediprog_handle, 0xc3, 0xb, 0x0, 0x0, buf,
+			      0x1, DEFAULT_TIMEOUT);
 	if (ret < 0) {
 		msg_perr("Command A failed (%s)!\n", usb_strerror());
 		return 1;
@@ -420,7 +433,8 @@
 	char buf[0x3];
 
 	memset(buf, 0, sizeof(buf));
-	ret = usb_control_msg(dediprog_handle, 0xc3, 0x7, 0x0, 0xef00, buf, 0x3, DEFAULT_TIMEOUT);
+	ret = usb_control_msg(dediprog_handle, 0xc3, 0x7, 0x0, 0xef00, buf,
+			      0x3, DEFAULT_TIMEOUT);
 	if (ret < 0) {
 		msg_perr("Command B failed (%s)!\n", usb_strerror());
 		return 1;
@@ -444,7 +458,8 @@
 {
 	int ret;
 
-	ret = usb_control_msg(dediprog_handle, 0x42, 0x4, 0x0, 0x0, NULL, 0x0, DEFAULT_TIMEOUT);
+	ret = usb_control_msg(dediprog_handle, 0x42, 0x4, 0x0, 0x0, NULL,
+			      0x0, DEFAULT_TIMEOUT);
 	if (ret != 0x0) {
 		msg_perr("Command C failed (%s)!\n", usb_strerror());
 		return 1;
@@ -464,7 +479,8 @@
 	char buf[0x1];
 
 	memset(buf, 0, sizeof(buf));
-	ret = usb_control_msg(dediprog_handle, 0xc2, 0x11, 0xff, 0xff, buf, 0x1, timeout);
+	ret = usb_control_msg(dediprog_handle, 0xc2, 0x11, 0xff, 0xff, buf,
+			      0x1, timeout);
 	/* This check is most probably wrong. Command F always causes a timeout
 	 * in the logs, so we should check for timeout instead of checking for
 	 * success.
@@ -481,8 +497,7 @@
 {
 	char *tmp = NULL;
 	int i;
-	int millivolt;
-	int fraction = 0;
+	int millivolt = 0, fraction = 0;
 
 	if (!voltage || !strlen(voltage)) {
 		msg_perr("Empty voltage= specified.\n");
@@ -527,13 +542,13 @@
 }
 
 static const struct spi_programmer spi_programmer_dediprog = {
-	.type = SPI_CONTROLLER_DEDIPROG,
-	.max_data_read = MAX_DATA_UNSPECIFIED,
-	.max_data_write = MAX_DATA_UNSPECIFIED,
-	.command = dediprog_spi_send_command,
-	.multicommand = default_spi_send_multicommand,
-	.read = dediprog_spi_read,
-	.write_256 = dediprog_spi_write_256,
+	.type		= SPI_CONTROLLER_DEDIPROG,
+	.max_data_read	= MAX_DATA_UNSPECIFIED,
+	.max_data_write	= MAX_DATA_UNSPECIFIED,
+	.command	= dediprog_spi_send_command,
+	.multicommand	= default_spi_send_multicommand,
+	.read		= dediprog_spi_read,
+	.write_256	= dediprog_spi_write_256,
 };
 
 static int dediprog_shutdown(void *data)
@@ -569,9 +584,8 @@
 	if (voltage) {
 		millivolt = parse_voltage(voltage);
 		free(voltage);
-		if (millivolt < 0) {
+		if (millivolt < 0)
 			return 1;
-		}
 		msg_pinfo("Setting voltage to %i mV\n", millivolt);
 	}
 
@@ -585,8 +599,7 @@
 		return 1;
 	}
 	msg_pdbg("Found USB device (%04x:%04x).\n",
-		 dev->descriptor.idVendor,
-		 dev->descriptor.idProduct);
+		 dev->descriptor.idVendor, dev->descriptor.idProduct);
 	dediprog_handle = usb_open(dev);
 	ret = usb_set_configuration(dediprog_handle, 1);
 	if (ret < 0) {
@@ -666,7 +679,8 @@
 	/* JEDEC RDID */
 	msg_pdbg("Sending RDID\n");
 	buf[0] = JEDEC_RDID;
-	if (dediprog_spi_send_command(JEDEC_RDID_OUTSIZE, JEDEC_RDID_INSIZE, (unsigned char *)buf, (unsigned char *)buf))
+	if (dediprog_spi_send_command(JEDEC_RDID_OUTSIZE, JEDEC_RDID_INSIZE,
+				(unsigned char *)buf, (unsigned char *)buf))
 		return 1;
 	msg_pdbg("Receiving response: ");
 	print_hex(buf, JEDEC_RDID_INSIZE);
diff --git a/dmi.c b/dmi.c
index e1544a9..2ab79a3 100644
--- a/dmi.c
+++ b/dmi.c
@@ -54,7 +54,12 @@
 	"baseboard-version",
 };
 
-/* A full list of chassis types can be found in the System Management BIOS
+/* This list is used to identify supposed laptops. The is_laptop field has the
+ * following meaning:
+ * 	- 0: in all likelihood not a laptop
+ * 	- 1: in all likelihood a laptop
+ * 	- 2: chassis-type is not specific enough
+ * A full list of chassis types can be found in the System Management BIOS
  * (SMBIOS) Reference Specification 2.7.0 section 7.4.1 "Chassis Types" at
  * http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.0.pdf
  * The types below are the most common ones.
@@ -64,14 +69,19 @@
 	unsigned char is_laptop;
 	const char *name;
 } dmi_chassis_types[] = {
-	{0x01, 0, "Other"},
-	{0x02, 0, "Unknown"},
+	{0x01, 2, "Other"},
+	{0x02, 2, "Unknown"},
 	{0x03, 0, "Desktop",},
+	{0x06, 0, "Mini Tower"},
+	{0x07, 0, "Tower"},
 	{0x08, 1, "Portable"},
 	{0x09, 1, "Laptop"},
 	{0x0a, 1, "Notebook"},
 	{0x0b, 1, "Hand Held"},
 	{0x0e, 1, "Sub Notebook"},
+	{0x11, 0, "Main Server Chassis"},
+	{0x17, 0, "Rack Mount Chassis"},
+	{0x18, 0, "Sealed-case PC"}, /* used by Supermicro (X8SIE) */
 };
 
 #define DMI_COMMAND_LEN_MAX 260
@@ -98,13 +108,14 @@
 	}
 
 	/* 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. */
+	 * 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)) {
+			if (ferror(dmidecode_pipe)) {
 				msg_perr("DMI pipe read error\n");
 				pclose(dmidecode_pipe);
 				return NULL;
@@ -112,7 +123,7 @@
 				answerbuf[0] = 0;	/* Hit EOF */
 			}
 		}
-	} while(answerbuf[0] == '#');
+	} while (answerbuf[0] == '#');
 
 	/* Toss all output above DMI_MAX_ANSWER_LEN away to prevent
 	   deadlock on pclose. */
@@ -125,8 +136,7 @@
 	}
 
 	/* Chomp trailing newline. */
-	if (answerbuf[0] != 0 &&
-	    answerbuf[strlen(answerbuf) - 1] == '\n')
+	if (answerbuf[0] != 0 && answerbuf[strlen(answerbuf) - 1] == '\n')
 		answerbuf[strlen(answerbuf) - 1] = 0;
 	msg_pdbg("DMI string %s: \"%s\"\n", string_name, answerbuf);
 
@@ -152,16 +162,25 @@
 	}
 
 	chassis_type = get_dmi_string("chassis-type");
-	if (chassis_type) {
-		for (i = 0; i < ARRAY_SIZE(dmi_chassis_types); i++) {
-			if (!strcasecmp(chassis_type,
-					dmi_chassis_types[i].name) &&
-			    dmi_chassis_types[i].is_laptop) {
-				msg_pdbg("Laptop detected via DMI\n");
-				is_laptop = 1;
-			}
+	if (chassis_type == NULL)
+		return;
+
+	is_laptop = 2;
+	for (i = 0; i < ARRAY_SIZE(dmi_chassis_types); i++) {
+		if (strcasecmp(chassis_type, dmi_chassis_types[i].name) == 0) {
+			is_laptop = dmi_chassis_types[i].is_laptop;
+			break;
 		}
 	}
+
+	switch (is_laptop) {
+	case 1:
+		msg_pdbg("Laptop detected via DMI.\n");
+		break;
+	case 2:
+		msg_pdbg("DMI chassis-type is not specific enough.\n");
+		break;
+	}
 	free(chassis_type);
 }
 
diff --git a/drkaiser.c b/drkaiser.c
index db4be60..9f2202e 100644
--- a/drkaiser.c
+++ b/drkaiser.c
@@ -39,6 +39,17 @@
 
 static uint8_t *drkaiser_bar;
 
+static const struct par_programmer par_programmer_drkaiser = {
+		.chip_readb		= drkaiser_chip_readb,
+		.chip_readw		= fallback_chip_readw,
+		.chip_readl		= fallback_chip_readl,
+		.chip_readn		= fallback_chip_readn,
+		.chip_writeb		= drkaiser_chip_writeb,
+		.chip_writew		= fallback_chip_writew,
+		.chip_writel		= fallback_chip_writel,
+		.chip_writen		= fallback_chip_writen,
+};
+
 static int drkaiser_shutdown(void *data)
 {
 	physunmap(drkaiser_bar, DRKAISER_MEMMAP_SIZE);
@@ -58,16 +69,18 @@
 
 	/* Write magic register to enable flash write. */
 	rpci_write_word(pcidev_dev, PCI_MAGIC_DRKAISER_ADDR,
-		       PCI_MAGIC_DRKAISER_VALUE);
+			PCI_MAGIC_DRKAISER_VALUE);
 
 	/* Map 128kB flash memory window. */
 	drkaiser_bar = physmap("Dr. Kaiser PC-Waechter flash memory",
 			       addr, DRKAISER_MEMMAP_SIZE);
 
-	buses_supported = CHIP_BUSTYPE_PARALLEL;
-
 	if (register_shutdown(drkaiser_shutdown, NULL))
 		return 1;
+
+	max_rom_decode.parallel = 128 * 1024;
+	register_par_programmer(&par_programmer_drkaiser, BUS_PARALLEL);
+
 	return 0;
 }
 
diff --git a/dummyflasher.c b/dummyflasher.c
index 1c02187..e8b4014 100644
--- a/dummyflasher.c
+++ b/dummyflasher.c
@@ -55,34 +55,48 @@
 };
 static enum emu_chip emu_chip = EMULATE_NONE;
 static char *emu_persistent_image = NULL;
-static int emu_chip_size = 0;
+static unsigned int emu_chip_size = 0;
 #if EMULATE_SPI_CHIP
-static int emu_max_byteprogram_size = 0;
-static int emu_max_aai_size = 0;
-static int emu_jedec_se_size = 0;
-static int emu_jedec_be_52_size = 0;
-static int emu_jedec_be_d8_size = 0;
-static int emu_jedec_ce_60_size = 0;
-static int emu_jedec_ce_c7_size = 0;
+static unsigned int emu_max_byteprogram_size = 0;
+static unsigned int emu_max_aai_size = 0;
+static unsigned int emu_jedec_se_size = 0;
+static unsigned int emu_jedec_be_52_size = 0;
+static unsigned int emu_jedec_be_d8_size = 0;
+static unsigned int emu_jedec_ce_60_size = 0;
+static unsigned int emu_jedec_ce_c7_size = 0;
 #endif
 #endif
 
-static int spi_write_256_chunksize = 256;
+static unsigned int spi_write_256_chunksize = 256;
 
 static int dummy_spi_send_command(unsigned int writecnt, unsigned int readcnt,
 		      const unsigned char *writearr, unsigned char *readarr);
-static int dummy_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len);
+static int dummy_spi_write_256(struct flashchip *flash, uint8_t *buf,
+			       unsigned int start, unsigned int len);
 
 static const struct spi_programmer spi_programmer_dummyflasher = {
-	.type = SPI_CONTROLLER_DUMMY,
-	.max_data_read = MAX_DATA_READ_UNLIMITED,
-	.max_data_write = MAX_DATA_UNSPECIFIED,
-	.command = dummy_spi_send_command,
-	.multicommand = default_spi_send_multicommand,
-	.read = default_spi_read,
-	.write_256 = dummy_spi_write_256,
+	.type		= SPI_CONTROLLER_DUMMY,
+	.max_data_read	= MAX_DATA_READ_UNLIMITED,
+	.max_data_write	= MAX_DATA_UNSPECIFIED,
+	.command	= dummy_spi_send_command,
+	.multicommand	= default_spi_send_multicommand,
+	.read		= default_spi_read,
+	.write_256	= dummy_spi_write_256,
 };
 
+static const struct par_programmer par_programmer_dummy = {
+		.chip_readb		= dummy_chip_readb,
+		.chip_readw		= dummy_chip_readw,
+		.chip_readl		= dummy_chip_readl,
+		.chip_readn		= dummy_chip_readn,
+		.chip_writeb		= dummy_chip_writeb,
+		.chip_writew		= dummy_chip_writew,
+		.chip_writel		= dummy_chip_writel,
+		.chip_writen		= dummy_chip_writen,
+};
+
+enum chipbustype dummy_buses_supported = BUS_NONE;
+
 static int dummy_shutdown(void *data)
 {
 	msg_pspew("%s\n", __func__);
@@ -119,24 +133,24 @@
 	/* Convert the parameters to lowercase. */
 	tolower_string(bustext);
 
-	buses_supported = CHIP_BUSTYPE_NONE;
+	dummy_buses_supported = BUS_NONE;
 	if (strstr(bustext, "parallel")) {
-		buses_supported |= CHIP_BUSTYPE_PARALLEL;
+		dummy_buses_supported |= BUS_PARALLEL;
 		msg_pdbg("Enabling support for %s flash.\n", "parallel");
 	}
 	if (strstr(bustext, "lpc")) {
-		buses_supported |= CHIP_BUSTYPE_LPC;
+		dummy_buses_supported |= BUS_LPC;
 		msg_pdbg("Enabling support for %s flash.\n", "LPC");
 	}
 	if (strstr(bustext, "fwh")) {
-		buses_supported |= CHIP_BUSTYPE_FWH;
+		dummy_buses_supported |= BUS_FWH;
 		msg_pdbg("Enabling support for %s flash.\n", "FWH");
 	}
 	if (strstr(bustext, "spi")) {
-		register_spi_programmer(&spi_programmer_dummyflasher);
+		dummy_buses_supported |= BUS_SPI;
 		msg_pdbg("Enabling support for %s flash.\n", "SPI");
 	}
-	if (buses_supported == CHIP_BUSTYPE_NONE)
+	if (dummy_buses_supported == BUS_NONE)
 		msg_pdbg("Support for all flash bus types disabled.\n");
 	free(bustext);
 
@@ -280,6 +294,14 @@
 		free(flashchip_contents);
 		return 1;
 	}
+	if (dummy_buses_supported & (BUS_PARALLEL | BUS_LPC | BUS_FWH))
+		register_par_programmer(&par_programmer_dummy,
+					dummy_buses_supported &
+						(BUS_PARALLEL | BUS_LPC |
+						 BUS_FWH));
+	if (dummy_buses_supported & BUS_SPI)
+		register_spi_programmer(&spi_programmer_dummyflasher);
+
 	return 0;
 }
 
@@ -353,8 +375,8 @@
 static int emulate_spi_chip_response(unsigned int writecnt, unsigned int readcnt,
 		      const unsigned char *writearr, unsigned char *readarr)
 {
-	int offs;
-	static int aai_offs;
+	unsigned int offs;
+	static int unsigned aai_offs;
 	static int aai_active = 0;
 
 	if (writecnt == 0) {
@@ -583,14 +605,14 @@
 	}
 #endif
 	msg_pspew(" reading %u bytes:", readcnt);
-	for (i = 0; i < readcnt; i++) {
+	for (i = 0; i < readcnt; i++)
 		msg_pspew(" 0x%02x", readarr[i]);
-	}
 	msg_pspew("\n");
 	return 0;
 }
 
-static int dummy_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
+static int dummy_spi_write_256(struct flashchip *flash, uint8_t *buf,
+			       unsigned int start, unsigned int len)
 {
 	return spi_write_chunked(flash, buf, start, len,
 				 spi_write_256_chunksize);
diff --git a/flash.h b/flash.h
index 2059f62..9da4d9a 100644
--- a/flash.h
+++ b/flash.h
@@ -71,13 +71,13 @@
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
 
 enum chipbustype {
-	CHIP_BUSTYPE_NONE	= 0,
-	CHIP_BUSTYPE_PARALLEL	= 1 << 0,
-	CHIP_BUSTYPE_LPC	= 1 << 1,
-	CHIP_BUSTYPE_FWH	= 1 << 2,
-	CHIP_BUSTYPE_SPI	= 1 << 3,
-	CHIP_BUSTYPE_NONSPI	= CHIP_BUSTYPE_PARALLEL | CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH,
-	CHIP_BUSTYPE_UNKNOWN	= CHIP_BUSTYPE_PARALLEL | CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI,
+	BUS_NONE	= 0,
+	BUS_PARALLEL	= 1 << 0,
+	BUS_LPC		= 1 << 1,
+	BUS_FWH		= 1 << 2,
+	BUS_SPI		= 1 << 3,
+	BUS_PROG	= 1 << 4,
+	BUS_NONSPI	= BUS_PARALLEL | BUS_LPC | BUS_FWH,
 };
 
 /* used to select bus which target chip resides */
@@ -125,9 +125,9 @@
 	uint32_t model_id;
 
 	/* Total chip size in kilobytes */
-	int total_size;
+	unsigned int total_size;
 	/* Chip page size in bytes */
-	int page_size;
+	unsigned int page_size;
 	int feature_bits;
 
 	/*
@@ -138,8 +138,10 @@
 
 	int (*probe) (struct flashchip *flash);
 
-	/* Delay after "enter/exit ID mode" commands in microseconds. */
-	int probe_timing;
+	/* Delay after "enter/exit ID mode" commands in microseconds.
+	 * NB: negative values have special meanings, see TIMING_* below.
+	 */
+	signed int probe_timing;
 
 	/*
 	 * Erase blocks and associated erase function. Any chip erase function
@@ -160,8 +162,8 @@
 
 	int (*printlock) (struct flashchip *flash);
 	int (*unlock) (struct flashchip *flash);
-	int (*write) (struct flashchip *flash, uint8_t *buf, int start, int len);
-	int (*read) (struct flashchip *flash, uint8_t *buf, int start, int len);
+	int (*write) (struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
+	int (*read) (struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
 	struct {
 		uint16_t min;
 		uint16_t max;
@@ -221,7 +223,7 @@
 extern const char flashrom_version[];
 extern char *chip_to_probe;
 void map_flash_registers(struct flashchip *flash);
-int read_memmapped(struct flashchip *flash, uint8_t *buf, int start, int len);
+int read_memmapped(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
 int erase_flash(struct flashchip *flash);
 int probe_flash(int startchip, struct flashchip *fill_flash, int force);
 int read_flash_to_file(struct flashchip *flash, const char *filename);
@@ -229,30 +231,47 @@
 int max(int a, int b);
 void tolower_string(char *str);
 char *extract_param(char **haystack, const char *needle, const char *delim);
-int verify_range(struct flashchip *flash, uint8_t *cmpbuf, int start, int len, const char *message);
-int need_erase(uint8_t *have, uint8_t *want, int len, enum write_granularity gran);
+int verify_range(struct flashchip *flash, uint8_t *cmpbuf, unsigned int start, unsigned int len, const char *message);
+int need_erase(uint8_t *have, uint8_t *want, unsigned int len, enum write_granularity gran);
 char *strcat_realloc(char *dest, const char *src);
 void print_version(void);
 void print_banner(void);
 void list_programmers_linebreak(int startcol, int cols, int paren);
 int selfcheck(void);
-int doit(struct flashchip *flash, int force, const char *filename, int read_it, int write_it, int erase_it, int verify_it);
+int doit(struct flashchip *flash, int force, const char *filename, int read_it, int write_it, int erase_it, int verify_it, const char *diff_file);
 int read_buf_from_file(unsigned char *buf, unsigned long size, const char *filename);
 int write_buf_to_file(unsigned char *buf, unsigned long size, const char *filename);
 
 #define OK 0
 #define NT 1    /* Not tested */
 
-/* Something happened that shouldn't happen, but we can go on */
+/* what to do in case of an error */
+enum error_action {
+	error_fail,	/* fail immediately */
+	error_ignore,	/* non-fatal error; continue */
+};
+
+/* Something happened that shouldn't happen, but we can go on. */
 #define ERROR_NONFATAL 0x100
 
+/* Something happened that shouldn't happen, we'll abort. */
+#define ERROR_FATAL -0xee
+
+/* Operation failed due to access restriction set in programmer or flash chip */
+#define ACCESS_DENIED -7
+extern enum error_action access_denied_action;
+
+/* convenience function for checking return codes */
+extern int ignore_error(int x);
+
 /* cli_output.c */
 /* 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
-#define MSG_BARF	3
+#define MSG_DEBUG2	3
+#define MSG_BARF	4
 #define msg_gerr(...)	print(MSG_ERROR, __VA_ARGS__)	/* general errors */
 #define msg_perr(...)	print(MSG_ERROR, __VA_ARGS__)	/* programmer errors */
 #define msg_cerr(...)	print(MSG_ERROR, __VA_ARGS__)	/* chip errors */
@@ -262,14 +281,13 @@
 #define msg_gdbg(...)	print(MSG_DEBUG, __VA_ARGS__)	/* general debug */
 #define msg_pdbg(...)	print(MSG_DEBUG, __VA_ARGS__)	/* programmer debug */
 #define msg_cdbg(...)	print(MSG_DEBUG, __VA_ARGS__)	/* chip debug */
+#define msg_gdbg2(...)	print(MSG_DEBUG2, __VA_ARGS__)	/* general debug2 */
+#define msg_pdbg2(...)	print(MSG_DEBUG2, __VA_ARGS__)	/* programmer debug2 */
+#define msg_cdbg2(...)	print(MSG_DEBUG2, __VA_ARGS__)	/* chip debug2 */
 #define msg_gspew(...)	print(MSG_BARF, __VA_ARGS__)	/* general debug barf  */
 #define msg_pspew(...)	print(MSG_BARF, __VA_ARGS__)	/* programmer debug barf  */
 #define msg_cspew(...)	print(MSG_BARF, __VA_ARGS__)	/* chip debug barf  */
 
-/* cli_classic.c */
-int cli_classic(int argc, char *argv[]);
-int cli_mfg(int argc, char *argv[]);
-
 /* cli_mfg.c */
 extern int set_ignore_fmap;
 
@@ -286,16 +304,17 @@
 int handle_partial_read(
     struct flashchip *flash,
     uint8_t *buf,
-    int (*read) (struct flashchip *flash, uint8_t *buf, int start, int len),
-                 int write_to_file);
+    int (*read) (struct flashchip *flash, uint8_t *buf,
+                 unsigned int start, unsigned int len),
+    int write_to_file);
     /* RETURN: the number of partitions that have beenpartial read.
     *         ==0 means no partition is specified.
     *         < 0 means writing file error. */
 int handle_partial_verify(
     struct flashchip *flash,
     uint8_t *buf,
-    int (*verify) (struct flashchip *flash, uint8_t *buf, int start, int len,
-                   const char* message));
+    int (*verify) (struct flashchip *flash, uint8_t *buf, unsigned int start,
+                   unsigned int len, const char* message));
     /* RETURN: ==0 means all identical.
                !=0 means buf and flash are different. */
 
diff --git a/flashchips.c b/flashchips.c
index 06f8c5b..9df3330 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -61,7 +61,7 @@
 	{
 		.vendor		= "AMD",
 		.name		= "Am29F010A/B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29F010B,	/* Same as Am29F010A */
 		.total_size	= 128,
@@ -88,7 +88,7 @@
 	{
 		.vendor		= "AMD",
 		.name		= "Am29F002(N)BB",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29F002BB,
 		.total_size	= 256,
@@ -114,13 +114,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
-		.voltage	= {4750, 5250}, /* -55 speed is +-5%, all others +-10% */
+		.voltage	= {4750, 5250}, /* 4.75-5.25V for type -55, others 4.5-5.5V */
 	},
 
 	{
 		.vendor		= "AMD",
 		.name		= "Am29F002(N)BT",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29F002BT,
 		.total_size	= 256,
@@ -146,12 +146,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {4750, 5250}, /* 4.75-5.25V for type -55, others 4.5-5.5V */
 	},
 
 	{
 		.vendor		= "AMD",
 		.name		= "Am29F016D",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29F016D,
 		.total_size	= 2 * 1024,
@@ -178,7 +179,7 @@
 	{
 		.vendor		= "AMD",
 		.name		= "Am29F040B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29F040B,
 		.total_size	= 512,
@@ -205,7 +206,7 @@
 	{
 		.vendor		= "AMD",
 		.name		= "Am29F080B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29F080B,
 		.total_size	= 1024,
@@ -232,7 +233,7 @@
 	{
 		.vendor		= "AMD",
 		.name		= "Am29LV001BB",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29LV001BB,
 		.total_size	= 128,
@@ -257,12 +258,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* 3.0-3.6V for type -45R, others 2.7-3.6V */
 	},
 
 	{
 		.vendor		= "AMD",
 		.name		= "Am29LV001BT",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29LV001BT,
 		.total_size	= 128,
@@ -287,12 +289,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* 3.0-3.6V for type -45R, others 2.7-3.6V */
 	},
 
 	{
 		.vendor		= "AMD",
 		.name		= "Am29LV002BB",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29LV002BB,
 		.total_size	= 256,
@@ -318,12 +321,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* 3.0-3.6V for type -55, others 2.7-3.6V */
 	},
 
 	{
 		.vendor		= "AMD",
 		.name		= "Am29LV002BT",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29LV002BT,
 		.total_size	= 256,
@@ -349,12 +353,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* 3.0-3.6V for type -55, others 2.7-3.6V */
 	},
 
 	{
 		.vendor		= "AMD",
 		.name		= "Am29LV004BB",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29LV004BB,
 		.total_size	= 512,
@@ -380,12 +385,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "AMD",
 		.name		= "Am29LV004BT",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29LV004BT,
 		.total_size	= 512,
@@ -411,18 +417,19 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "AMD",
 		.name		= "Am29LV008BB",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29LV008BB,
 		.total_size	= 1024,
 		.page_size	= 64 * 1024, /* unused */
 		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
-		.tested		= TEST_UNTESTED,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_jedec,
 		.probe_timing	= TIMING_ZERO,
 		.block_erasers	=
@@ -442,12 +449,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {3000, 3600} /* 3.0-3.6V for type -70R, others 2.7-3.6V */
 	},
 
 	{
 		.vendor		= "AMD",
 		.name		= "Am29LV008BT",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29LV008BT,
 		.total_size	= 1024,
@@ -473,12 +481,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {3000, 3600} /* 3.0-3.6V for type -70R, others 2.7-3.6V */
 	},
 
 	{
 		.vendor		= "AMD",
 		.name		= "Am29LV040B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29LV040B,
 		.total_size	= 512,
@@ -499,13 +508,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
-		.voltage	= {2700, 3600},
+		.voltage	= {3000, 3600}, /* 3.0-3.6V for type -60R, others 2.7-3.6V*/
 	},
 
 	{
 		.vendor		= "AMD",
 		.name		= "Am29LV081B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMD_ID,
 		.model_id	= AMD_AM29LV080B,
 		.total_size	= 1024,
@@ -526,13 +535,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
-		.voltage	= {2700, 3600},
+		.voltage	= {3000, 3600}, /* 3.0-3.6V for type -70R, others 2.7-3.6V */
 	},
 
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L05PT",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID,
 		.model_id	= AMIC_A25L05PT,
 		.total_size	= 64,
@@ -565,7 +574,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L05PU",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID,
 		.model_id	= AMIC_A25L05PU,
 		.total_size	= 64,
@@ -592,12 +601,13 @@
 		.unlock		= spi_disable_blockprotect,
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L10PT",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID,
 		.model_id	= AMIC_A25L10PT,
 		.total_size	= 128,
@@ -631,7 +641,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L10PU",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID,
 		.model_id	= AMIC_A25L10PU,
 		.total_size	= 128,
@@ -665,7 +675,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L20PT",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID,
 		.model_id	= AMIC_A25L20PT,
 		.total_size	= 256,
@@ -699,7 +709,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L20PU",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID,
 		.model_id	= AMIC_A25L20PU,
 		.total_size	= 256,
@@ -738,7 +748,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L40PT",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID,
 		.model_id	= AMIC_A25L40PT,
 		.total_size	= 512,
@@ -772,7 +782,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L40PU",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID,
 		.model_id	= AMIC_A25L40PU,
 		.total_size	= 512,
@@ -806,7 +816,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L80P",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID,
 		.model_id	= AMIC_A25L80P,
 		.total_size	= 1024,
@@ -840,7 +850,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L16PT",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID,
 		.model_id	= AMIC_A25L16PT,
 		.total_size	= 2048,
@@ -877,7 +887,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L16PU",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID,
 		.model_id	= AMIC_A25L16PU,
 		.total_size	= 2048,
@@ -914,7 +924,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L512",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID_NOPREFIX,
 		.model_id	= AMIC_A25L512,
 		.total_size	= 64,
@@ -946,7 +956,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L010",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID_NOPREFIX,
 		.model_id	= AMIC_A25L010,
 		.total_size	= 128,
@@ -978,7 +988,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L020",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID_NOPREFIX,
 		.model_id	= AMIC_A25L020,
 		.total_size	= 256,
@@ -1010,7 +1020,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L040",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID_NOPREFIX,
 		.model_id	= AMIC_A25L040,
 		.total_size	= 512,
@@ -1042,7 +1052,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L080",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID_NOPREFIX,
 		.model_id	= AMIC_A25L080,
 		.total_size	= 1024,
@@ -1074,7 +1084,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L016",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID_NOPREFIX,
 		.model_id	= AMIC_A25L016,
 		.total_size	= 2048,
@@ -1106,7 +1116,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25L032",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID_NOPREFIX,
 		.model_id	= AMIC_A25L032,
 		.total_size	= 4096,
@@ -1144,7 +1154,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A25LQ032",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID_NOPREFIX,
 		.model_id	= AMIC_A25LQ032,
 		.total_size	= 4096,
@@ -1182,7 +1192,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A29002B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMIC_ID_NOPREFIX,
 		.model_id	= AMIC_A29002B,
 		.total_size	= 256,
@@ -1214,13 +1224,13 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A29002T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_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_PR,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_jedec,
 		.probe_timing	= TIMING_ZERO,
 		.block_erasers	=
@@ -1246,7 +1256,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A29040B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= AMIC_ID_NOPREFIX,
 		.model_id	= AMIC_A29040B,
 		.total_size	= 512,
@@ -1273,7 +1283,7 @@
 	{
 		.vendor		= "AMIC",
 		.name		= "A49LF040A",
-		.bustype	= CHIP_BUSTYPE_LPC,
+		.bustype	= BUS_LPC,
 		.manufacture_id	= AMIC_ID_NOPREFIX,
 		.model_id	= AMIC_A49LF040A,
 		.total_size	= 512,
@@ -1301,7 +1311,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT25DF021",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT25DF021,
 		.total_size	= 256,
@@ -1333,13 +1343,13 @@
 		.unlock		= spi_disable_blockprotect_at25df,
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
-		.voltage	= {2300, 3600}, /* Datasheet says 2.3-3.6V or 2.7-3.6V */
+		.voltage	= {2700, 3600}, /* 2.3-3.6V & 2.7-3.6V models available */
 	},
 
 	{
 		.vendor		= "Atmel",
 		.name		= "AT25DF041A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT25DF041A,
 		.total_size	= 512,
@@ -1371,13 +1381,13 @@
 		.unlock		= spi_disable_blockprotect_at25df,
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
-		.voltage	= {2300, 3600}, /* Datasheet says 2.3-3.6V or 2.7-3.6V */
+		.voltage	= {2700, 3600}, /* 2.3-3.6V & 2.7-3.6V models available */
 	},
 
 	{
 		.vendor		= "Atmel",
 		.name		= "AT25DF081",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT25DF081,
 		.total_size	= 1024,
@@ -1415,7 +1425,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT25DF081A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT25DF081A,
 		.total_size	= 1024,
@@ -1447,12 +1457,13 @@
 		.unlock		= spi_disable_blockprotect_at25df_sec,
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "Atmel",
 		.name		= "AT25DF161",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT25DF161,
 		.total_size	= 2048,
@@ -1490,7 +1501,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT25DF321",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT25DF321,
 		.total_size	= 4096,
@@ -1528,13 +1539,13 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT25DF321A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT25DF321A,
 		.total_size	= 4096,
 		.page_size	= 256,
 		.feature_bits	= FEATURE_WRSR_WREN,
-		.tested		= TEST_UNTESTED,
+		.tested		= TEST_OK_PROBE,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
 		.block_erasers	=
@@ -1566,7 +1577,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT25DF641",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT25DF641,
 		.total_size	= 8192,
@@ -1604,7 +1615,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT25DQ161",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT25DQ161,
 		.total_size	= 2048,
@@ -1642,7 +1653,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT25F512B",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT25F512B,
 		.total_size	= 64,
@@ -1680,7 +1691,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT25FS010",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT25FS010,
 		.total_size	= 128,
@@ -1720,7 +1731,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT25FS040",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT25FS040,
 		.total_size	= 512,
@@ -1757,7 +1768,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT26DF041",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT26DF041,
 		.total_size	= 512,
@@ -1774,13 +1785,13 @@
 		},
 		.write		= NULL /* Incompatible Page write */,
 		.read		= spi_chip_read,
-		.voltage	= {2700, 3600}, /* Datasheet says 3.0-3.6 V or 2.7-3.6 V */
+		.voltage	= {2700, 3600}, /* 3.0-3.6V for higher speed, 2.7-3.6V normal */
 	},
 
 	{
 		.vendor		= "Atmel",
 		.name		= "AT26DF081A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT26DF081A,
 		.total_size	= 1024,
@@ -1818,7 +1829,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT26DF161",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT26DF161,
 		.total_size	= 2048,
@@ -1855,7 +1866,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT26DF161A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT26DF161A,
 		.total_size	= 2048,
@@ -1893,7 +1904,7 @@
 	/*{
 		.vendor		= "Atmel",
 		.name		= "AT26DF321",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT26DF321,
 		.total_size	= 4096,
@@ -1910,7 +1921,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT26F004",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT26F004,
 		.total_size	= 512,
@@ -1945,7 +1956,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT29C512",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT29C512,
 		.total_size	= 64,
@@ -1969,7 +1980,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT29C010A",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT29C010A,
 		.total_size	= 128,
@@ -1993,7 +2004,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT29C020",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT29C020,
 		.total_size	= 256,
@@ -2017,7 +2028,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT29C040A",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT29C040A,
 		.total_size	= 512,
@@ -2041,7 +2052,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT45CS1282",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT45CS1282,
 		.total_size	= 16896 /* No power of two sizes */,
@@ -2057,7 +2068,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT45DB011D",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT45DB011D,
 		.total_size	= 128 /* Size can only be determined from status register */,
@@ -2073,7 +2084,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT45DB021D",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT45DB021D,
 		.total_size	= 256 /* Size can only be determined from status register */,
@@ -2089,7 +2100,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT45DB041D",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT45DB041D,
 		.total_size	= 512 /* Size can only be determined from status register */,
@@ -2099,13 +2110,13 @@
 		.probe_timing	= TIMING_ZERO,
 		.write		= NULL,
 		.read		= NULL,
-		.voltage	= {2500, 3600}, /* Datasheet says 2.5-3.6 V or 2.7-3.6 V */
+		.voltage	= {2500, 3600}, /* 2.5-3.6V & 2.7-3.6V models available */
 	},
 
 	{
 		.vendor		= "Atmel",
 		.name		= "AT45DB081D",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT45DB081D,
 		.total_size	= 1024 /* Size can only be determined from status register */,
@@ -2115,13 +2126,13 @@
 		.probe_timing	= TIMING_ZERO,
 		.write		= NULL,
 		.read		= NULL,
-		.voltage	= {2500, 3600}, /* Datasheet says 2.5-3.6 V or 2.7-3.6 V */
+		.voltage	= {2700, 3600}, /* 2.5-3.6V & 2.7-3.6V models available */
 	},
 
 	{
 		.vendor		= "Atmel",
 		.name		= "AT45DB161D",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT45DB161D,
 		.total_size	= 2048 /* Size can only be determined from status register */,
@@ -2131,13 +2142,13 @@
 		.probe_timing	= TIMING_ZERO,
 		.write		= NULL,
 		.read		= NULL,
-		.voltage	= {2500, 3600}, /* Datasheet says 2.5-3.6 V or 2.7-3.6 V */
+		.voltage	= {2700, 3600}, /* 2.5-3.6V & 2.7-3.6V models available */
 	},
 
 	{
 		.vendor		= "Atmel",
 		.name		= "AT45DB321C",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT45DB321C,
 		.total_size	= 4224 /* No power of two sizes */,
@@ -2153,7 +2164,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT45DB321D",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT45DB321D,
 		.total_size	= 4096 /* Size can only be determined from status register */,
@@ -2169,7 +2180,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT45DB642D",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT45DB642D,
 		.total_size	= 8192 /* Size can only be determined from status register */,
@@ -2185,7 +2196,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT49BV512",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT49BV512,
 		.total_size	= 64,
@@ -2209,7 +2220,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT49F020",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT49F020,
 		.total_size	= 256,
@@ -2233,7 +2244,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT49F002(N)",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT49F002N,
 		.total_size	= 256,
@@ -2265,7 +2276,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "AT49F002(N)T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= ATMEL_AT49F002NT,
 		.total_size	= 256,
@@ -2295,9 +2306,69 @@
 	},
 
 	{
+		.vendor		= "Atmel",
+		.name		= "AT49LH002",
+		.bustype	= BUS_LPC | BUS_FWH, /* A/A Mux */
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT49LH002,
+		.total_size	= 256,
+		.page_size	= 0, /* unused */
+		.feature_bits	= FEATURE_REGISTERMAP, /* TODO: LPC OK too? */
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab, /* TODO: 0xff cmd not documented? */
+		.probe_timing	= TIMING_FIXME,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 3},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_block_82802ab,
+			}, {
+				.eraseblocks = {
+					{64 * 1024, 4},
+				},
+				.block_erase = NULL, /* TODO: Implement. */
+			},
+		},
+		.printlock	= NULL, /* TODO */
+		.unlock		= NULL, /* unlock_82802ab() not correct(?) */
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "Catalyst",
+		.name		= "CAT28F512",
+		.bustype	= BUS_PARALLEL,
+		.manufacture_id	= CATALYST_ID,
+		.model_id	= CATALYST_CAT28F512,
+		.total_size	= 64,
+		.page_size	= 0, /* unused */
+		.feature_bits	= 0,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_jedec, /* FIXME! */
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = NULL, /* TODO */
+			},
+		},
+		.write		= NULL, /* TODO */
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
 		.vendor		= "Bright",
 		.name		= "BM29F040",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= BRIGHT_ID,
 		.model_id	= BRIGHT_BM29F040,
 		.total_size	= 512,
@@ -2318,12 +2389,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
 	},
 
 	{
 		.vendor		= "Bright",
 		.name		= "BM29F040",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= BRIGHT_ID,
 		.model_id	= BRIGHT_BM29F040,
 		.total_size	= 512,
@@ -2349,7 +2421,7 @@
 	{
 		.vendor		= "EMST",
 		.name		= "F49B002UA",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= EMST_ID,
 		.model_id	= EMST_F49B002UA,
 		.total_size	= 256,
@@ -2381,7 +2453,7 @@
 	{
 		.vendor		= "EMST",
 		.name		= "F25L008A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EMST_ID,
 		.model_id	= EMST_F25L008A,
 		.total_size	= 1024,
@@ -2414,7 +2486,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B05",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B05,
 		.total_size	= 64,
@@ -2446,7 +2518,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B05T",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B05,
 		.total_size	= 64,
@@ -2478,7 +2550,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B10",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B10,
 		.total_size	= 128,
@@ -2510,7 +2582,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B10T",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B10,
 		.total_size	= 128,
@@ -2542,7 +2614,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B20",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B20,
 		.total_size	= 256,
@@ -2575,7 +2647,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B20T",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B20,
 		.total_size	= 256,
@@ -2608,7 +2680,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B40",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B40,
 		.total_size	= 512,
@@ -2641,7 +2713,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B40T",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B40,
 		.total_size	= 512,
@@ -2674,7 +2746,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B80",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B80,
 		.total_size	= 1024,
@@ -2707,7 +2779,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B80T",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B80,
 		.total_size	= 1024,
@@ -2740,7 +2812,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B16",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B16,
 		.total_size	= 2048,
@@ -2773,7 +2845,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B16T",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B16,
 		.total_size	= 2048,
@@ -2806,7 +2878,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B32",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B32,
 		.total_size	= 4096,
@@ -2839,7 +2911,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B32T",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B32,
 		.total_size	= 4096,
@@ -2872,7 +2944,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B64",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B64,
 		.total_size	= 8192,
@@ -2906,7 +2978,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25B64T",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25B64,
 		.total_size	= 8192,
@@ -2941,7 +3013,7 @@
 		   but has different write protection capabilities */
 		.vendor		= "Eon",
 		.name		= "EN25D16",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25D16,
 		.total_size	= 2048,
@@ -2978,7 +3050,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25F05",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25F05,
 		.total_size	= 64,
@@ -3015,7 +3087,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25F10",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25F10,
 		.total_size	= 128,
@@ -3052,7 +3124,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25F20",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25F20,
 		.total_size	= 256,
@@ -3089,7 +3161,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25F40",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25F40,
 		.total_size	= 512,
@@ -3124,13 +3196,13 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25F80",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25F80,
 		.total_size	= 1024,
 		.page_size	= 256,
 		.feature_bits	= FEATURE_WRSR_WREN,
-		.tested		= TEST_OK_PRE,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
 		.block_erasers	=
@@ -3158,13 +3230,13 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25F16",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25F16,
 		.total_size	= 2048,
 		.page_size	= 256,
 		.feature_bits	= FEATURE_WRSR_WREN,
-		.tested		= TEST_UNTESTED,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
 		.block_erasers	=
@@ -3192,7 +3264,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25F32",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25F32,
 		.total_size	= 4096,
@@ -3225,7 +3297,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25Q40",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25Q40,
 		.total_size	= 512,
@@ -3259,7 +3331,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25Q80(A)",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25Q80,
 		.total_size	= 1024,
@@ -3293,7 +3365,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25Q32(A)(B)",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25Q32,
 		.total_size	= 4096,
@@ -3328,7 +3400,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25Q64",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25Q64,
 		.total_size	= 8192,
@@ -3362,7 +3434,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN25Q128",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= EON_EN25Q128,
 		.total_size	= 16384,
@@ -3395,8 +3467,219 @@
 
 	{
 		.vendor		= "Eon",
+		.name		= "EN25Q40",
+		.bustype	= BUS_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25Q40,
+		.total_size	= 512,
+		.page_size	= 256,
+		/* TODO: chip features 256-byte one-time programmable region */
+		.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_60,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage        = {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25Q80(A)",
+		.bustype	= BUS_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25Q80,
+		.total_size	= 1024,
+		.page_size	= 256,
+		/* TODO: chip features 256-byte one-time programmable region */
+		.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_60,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage        = {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25Q32(A/B)",
+		.bustype	= BUS_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25Q32,
+		.total_size	= 4096,
+		.page_size	= 256,
+		/* TODO: chip features 512-byte one-time programmable region */
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 1024} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 64} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage        = {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25Q64",
+		.bustype	= BUS_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25Q64,
+		.total_size	= 8192,
+		.page_size	= 256,
+		/* TODO: chip features 512-byte one-time programmable region */
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 2048} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 128} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage        = {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25Q128",
+		.bustype	= BUS_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25Q128,
+		.total_size	= 16384,
+		.page_size	= 256,
+		/* TODO: chip features 512-byte one-time programmable region */
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 4096} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 256} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {16 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {16 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25QH16",
+		.bustype	= BUS_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25QH16,
+		.total_size	= 2048,
+		.page_size	= 256,
+		/* TODO: chip features 512-byte one-time programmable region
+		 * and supports SFDP.
+		 */
+		.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 = { {1024 * 2048, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {1024 * 2048, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage        = {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
 		.name		= "EN29F010",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= EON_ID,
 		.model_id	= EON_EN29F010,
 		.total_size	= 128,
@@ -3424,13 +3707,13 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN29F002(A)(N)B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= EON_ID,
 		.model_id	= EON_EN29F002B,
 		.total_size	= 256,
 		.page_size	= 256,
 		.feature_bits	= FEATURE_ADDR_AAA | FEATURE_EITHER_RESET,
-		.tested		= TEST_OK_PR,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_jedec,
 		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
 		.block_erasers	=
@@ -3456,7 +3739,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "EN29F002(A)(N)T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= EON_ID,
 		.model_id	= EON_EN29F002T,
 		.total_size	= 256,
@@ -3488,7 +3771,7 @@
 	{
 		.vendor		= "Fujitsu",
 		.name		= "MBM29F004BC",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= FUJITSU_ID,
 		.model_id	= FUJITSU_MBM29F004BC,
 		.total_size	= 512,
@@ -3520,7 +3803,7 @@
 	{
 		.vendor		= "Fujitsu",
 		.name		= "MBM29F004TC",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= FUJITSU_ID,
 		.model_id	= FUJITSU_MBM29F004TC,
 		.total_size	= 512,
@@ -3553,7 +3836,7 @@
 		/* FIXME: this has WORD/BYTE sequences; 2AA for word, 555 for byte */
 		.vendor		= "Fujitsu",
 		.name		= "MBM29F400BC",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= FUJITSU_ID,
 		.model_id	= FUJITSU_MBM29F400BC,
 		.total_size	= 512,
@@ -3579,13 +3862,13 @@
 		},
 		.write		= write_m29f400bt,
 		.read		= read_memmapped,
-		.voltage	= {4750, 5250}, /* 5.0V +-5% for -55 model, +-10% for rest */
+		.voltage	= {4750, 5250}, /* 4.75-5.25V for type -55, others 4.5-5.5V */
 	},
 
 	{
 		.vendor		= "Fujitsu",
 		.name		= "MBM29F400TC",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= FUJITSU_ID,
 		.model_id	= FUJITSU_MBM29F400TC,
 		.total_size	= 512,
@@ -3611,13 +3894,13 @@
 		},
 		.write		= write_m29f400bt,
 		.read		= read_memmapped,
-		.voltage	= {4750, 5250}, /* 5.0V +-5% for -55 model, +-10% for rest */
+		.voltage	= {4750, 5250}, /* 4.75-5.25V for type -55, others 4.5-5.5V */
 	},
 
 	{
 		.vendor		= "Hyundai",
 		.name		= "HY29F002T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= HYUNDAI_ID,
 		.model_id	= HYUNDAI_HY29F002T,
 		.total_size	= 256,
@@ -3643,13 +3926,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
-		.voltage	= {4750, 5250}, /* 5.0V +-5% for -45 model, +-10% for rest */
+		.voltage	= {4750, 5250}, /* 4.75-5.25V for type -45, others 4.5-5.5V */
 	},
 
 	{
 		.vendor		= "Hyundai",
 		.name		= "HY29F002B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= HYUNDAI_ID,
 		.model_id	= HYUNDAI_HY29F002B,
 		.total_size	= 256,
@@ -3675,13 +3958,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
-		.voltage	= {4750, 5250}, /* 5.0V +-5% for -45 model, +-10% for rest */
+		.voltage	= {4750, 5250}, /* 4.75-5.25V for type -45, others 4.5-5.5V */
 	},
 
 	{
 		.vendor		= "Hyundai",
 		.name		= "HY29F040A",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= HYUNDAI_ID,
 		.model_id	= HYUNDAI_HY29F040A,
 		.total_size	= 512,
@@ -3702,12 +3985,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
 	},
 
 	{
 		.vendor		= "Intel",
 		.name		= "28F001BN/BX-B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= INTEL_ID,
 		.model_id	= INTEL_28F001B,
 		.total_size	= 128,
@@ -3734,18 +4018,18 @@
 	{
 		.vendor		= "Intel",
 		.name		= "28F001BN/BX-T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= INTEL_ID,
 		.model_id	= INTEL_28F001T,
 		.total_size	= 128,
 		.page_size	= 128 * 1024, /* 112k + 2x4k + 8k */
-		.tested		= TEST_UNTESTED,
+		.tested		= TEST_OK_PR,
 		.probe		= probe_jedec,
 		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
 		.block_erasers	=
 		{
 			{
-				.eraseblocks = { 
+				.eraseblocks = {
 					{112 * 1024, 1},
 					{4 * 1024, 2},
 					{8 * 1024, 1},
@@ -3761,7 +4045,7 @@
 	{
 		.vendor		= "Intel",
 		.name		= "28F002BC/BL/BV/BX-T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= INTEL_ID,
 		.model_id	= INTEL_28F002T,
 		.total_size	= 256,
@@ -3783,13 +4067,12 @@
 		},
 		.write		= write_82802ab,
 		.read		= read_memmapped,
-		.voltage	= {4500, 5500}, /* 5.0V +-10% read, 12V fast program & erase- +-5% standard, +-10% option */
 	},
 
 	{
 		.vendor		= "Intel",
 		.name		= "28F008S3/S5/SC",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= INTEL_ID,
 		.model_id	= INTEL_28F004S3,
 		.total_size	= 512,
@@ -3812,7 +4095,7 @@
 	{
 		.vendor		= "Intel",
 		.name		= "28F004B5/BE/BV/BX-B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= INTEL_ID,
 		.model_id	= INTEL_28F004B,
 		.total_size	= 512,
@@ -3839,7 +4122,7 @@
 	{
 		.vendor		= "Intel",
 		.name		= "28F004B5/BE/BV/BX-T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= INTEL_ID,
 		.model_id	= INTEL_28F004T,
 		.total_size	= 512,
@@ -3866,7 +4149,7 @@
 	{
 		.vendor		= "Intel",
 		.name		= "28F400BV/BX/CE/CV-B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= INTEL_ID,
 		.model_id	= INTEL_28F400B,
 		.total_size	= 512,
@@ -3894,7 +4177,7 @@
 	{
 		.vendor		= "Intel",
 		.name		= "28F400BV/BX/CE/CV-T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= INTEL_ID,
 		.model_id	= INTEL_28F400T,
 		.total_size	= 512,
@@ -3922,7 +4205,7 @@
 	{
 		.vendor		= "Intel",
 		.name		= "82802AB",
-		.bustype	= CHIP_BUSTYPE_FWH,
+		.bustype	= BUS_FWH,
 		.manufacture_id	= INTEL_ID,
 		.model_id	= INTEL_82802AB,
 		.total_size	= 512,
@@ -3947,7 +4230,7 @@
 	{
 		.vendor		= "Intel",
 		.name		= "82802AC",
-		.bustype	= CHIP_BUSTYPE_FWH,
+		.bustype	= BUS_FWH,
 		.manufacture_id	= INTEL_ID,
 		.model_id	= INTEL_82802AC,
 		.total_size	= 1024,
@@ -3972,7 +4255,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX25L512",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX25L512,
 		.total_size	= 64,
@@ -4009,7 +4292,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX25L1005",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX25L1005,
 		.total_size	= 128,
@@ -4044,7 +4327,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX25L2005",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX25L2005,
 		.total_size	= 256,
@@ -4082,7 +4365,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX25L4005",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX25L4005,
 		.total_size	= 512,
@@ -4120,7 +4403,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX25L8005",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX25L8005,
 		.total_size	= 1024,
@@ -4158,7 +4441,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX25L1605",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX25L1605,
 		.total_size	= 2048,
@@ -4196,7 +4479,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX25L1635D",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX25L1635D,
 		.total_size	= 2048,
@@ -4230,7 +4513,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX25L1635E",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX25L1635E,
 		.total_size	= 2048,
@@ -4258,12 +4541,13 @@
 		.unlock		= spi_disable_blockprotect,
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "Macronix",
 		.name		= "MX25L3205",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX25L3205,
 		.total_size	= 4096,
@@ -4298,7 +4582,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX25L3235D",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX25L3235D,
 		.total_size	= 4096,
@@ -4333,7 +4617,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX25L6405",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX25L6405,
 		.total_size	= 8192,
@@ -4371,7 +4655,7 @@
 		 * ID bytes but different block erase capabilities */
 		.vendor		= "Macronix",
 		.name		= "MX25L6406E",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX25L6405,
 		.total_size	= 8192,
@@ -4409,7 +4693,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX25L12805",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX25L12805,
 		.total_size	= 16384,
@@ -4443,7 +4727,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX29F001B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX29F001B,
 		.total_size	= 128,
@@ -4476,7 +4760,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX29F001T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX29F001T,
 		.total_size	= 128,
@@ -4508,8 +4792,8 @@
 
 	{
 		.vendor		= "Macronix",
-		.name		= "MX29F002B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.name		= "MX29F002(N)B",
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX29F002B,
 		.total_size	= 256,
@@ -4540,14 +4824,14 @@
 
 	{
 		.vendor		= "Macronix",
-		.name		= "MX29F002T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.name		= "MX29F002(N)T",
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX29F002T,
 		.total_size	= 256,
 		.page_size	= 64 * 1024,
 		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
-		.tested		= TEST_OK_PR,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_jedec,
 		.probe_timing	= TIMING_ZERO,
 		.block_erasers	=
@@ -4573,7 +4857,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX29F040",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX29F040,
 		.total_size	= 512,
@@ -4594,12 +4878,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
 	},
 
 	{
 		.vendor		= "Macronix",
 		.name		= "MX29F040",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX29F040,
 		.total_size	= 512,
@@ -4625,7 +4910,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "MX29LV040",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= MACRONIX_MX29LV040,
 		.total_size	= 512,
@@ -4652,7 +4937,7 @@
 	{
 		.vendor		= "MoselVitelic",
 		.name		= "V29C51000B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SYNCMOS_MVC_ID,
 		.model_id	= MVC_V29C51000B,
 		.total_size	= 64,
@@ -4679,7 +4964,7 @@
 	{
 		.vendor		= "MoselVitelic",
 		.name		= "V29C51000T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SYNCMOS_MVC_ID,
 		.model_id	= MVC_V29C51000T,
 		.total_size	= 64,
@@ -4706,7 +4991,7 @@
 	{
 		.vendor		= "MoselVitelic",
 		.name		= "V29C51400B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SYNCMOS_MVC_ID,
 		.model_id	= MVC_V29C51400B,
 		.total_size	= 512,
@@ -4733,7 +5018,7 @@
 	{
 		.vendor		= "MoselVitelic",
 		.name		= "V29C51400T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SYNCMOS_MVC_ID,
 		.model_id	= MVC_V29C51400T,
 		.total_size	= 512,
@@ -4760,7 +5045,7 @@
 	{
 		.vendor		= "MoselVitelic",
 		.name		= "V29LC51000",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SYNCMOS_MVC_ID,
 		.model_id	= MVC_V29LC51000,
 		.total_size	= 64,
@@ -4787,7 +5072,7 @@
 	{
 		.vendor		= "MoselVitelic",
 		.name		= "V29LC51001",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SYNCMOS_MVC_ID,
 		.model_id	= MVC_V29LC51001,
 		.total_size	= 128,
@@ -4814,7 +5099,7 @@
 	{
 		.vendor		= "MoselVitelic",
 		.name		= "V29LC51002",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SYNCMOS_MVC_ID,
 		.model_id	= MVC_V29LC51002,
 		.total_size	= 256,
@@ -4841,7 +5126,7 @@
 	{
 		.vendor		= "Numonyx",
 		.name		= "M25PE10",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25PE10,
 		.total_size	= 128,
@@ -4871,7 +5156,7 @@
 	{
 		.vendor		= "Numonyx",
 		.name		= "M25PE20",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25PE20,
 		.total_size	= 256,
@@ -4901,7 +5186,7 @@
 	{
 		.vendor		= "Numonyx",
 		.name		= "M25PE40",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25PE40,
 		.total_size	= 512,
@@ -4931,7 +5216,7 @@
 	{
 		.vendor		= "Numonyx",
 		.name		= "M25PE80",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25PE80,
 		.total_size	= 1024,
@@ -4961,7 +5246,7 @@
 	{
 		.vendor		= "Numonyx",
 		.name		= "M25PE16",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25PE16,
 		.total_size	= 2048,
@@ -4991,7 +5276,7 @@
 	{
 		.vendor		= "PMC",
 		.name		= "Pm25LV010",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= PMC_ID,
 		.model_id	= PMC_PM25LV010,
 		.total_size	= 128,
@@ -5021,7 +5306,7 @@
 	{
 		.vendor		= "PMC",
 		.name		= "Pm25LV016B",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= PMC_ID,
 		.model_id	= PMC_PM25LV016B,
 		.total_size	= 2048,
@@ -5051,12 +5336,13 @@
 		.unlock		= spi_disable_blockprotect,
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "PMC",
 		.name		= "Pm25LV020",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= PMC_ID,
 		.model_id	= PMC_PM25LV020,
 		.total_size	= 256,
@@ -5086,12 +5372,12 @@
 	{
 		.vendor		= "PMC",
 		.name		= "Pm25LV040",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= PMC_ID,
 		.model_id	= PMC_PM25LV040,
 		.total_size	= 512,
 		.page_size	= 256,
-		.tested		= TEST_UNTESTED,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
 		.block_erasers	=
@@ -5116,7 +5402,7 @@
 	{
 		.vendor		= "PMC",
 		.name		= "Pm25LV080B",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= PMC_ID,
 		.model_id	= PMC_PM25LV080B,
 		.total_size	= 1024,
@@ -5152,7 +5438,7 @@
 	{
 		.vendor		= "PMC",
 		.name		= "Pm25LV512",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= PMC_ID,
 		.model_id	= PMC_PM25LV512,
 		.total_size	= 64,
@@ -5176,12 +5462,13 @@
 		.unlock		= spi_disable_blockprotect,
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "PMC",
 		.name		= "Pm29F002T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= PMC_ID_NOPREFIX,
 		.model_id	= PMC_PM29F002T,
 		.total_size	= 256,
@@ -5213,7 +5500,7 @@
 	{
 		.vendor		= "PMC",
 		.name		= "Pm29F002B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= PMC_ID_NOPREFIX,
 		.model_id	= PMC_PM29F002B,
 		.total_size	= 256,
@@ -5245,13 +5532,13 @@
 	{
 		.vendor		= "PMC",
 		.name		= "Pm39LV010",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= PMC_ID_NOPREFIX,
 		.model_id	= PMC_PM39F010,	/* Pm39LV010 and Pm39F010 have identical IDs but different voltage */
 		.total_size	= 128,
 		.page_size	= 4096,
 		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
-		.tested		= TEST_OK_PRE,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_jedec,
 		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
 		.block_erasers	=
@@ -5275,7 +5562,7 @@
 	{
 		.vendor		= "PMC",
 		.name		= "Pm39LV020",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= PMC_ID_NOPREFIX,
 		.model_id	= PMC_PM39LV020,
 		.total_size	= 256,
@@ -5305,13 +5592,13 @@
 	{
 		.vendor		= "PMC",
 		.name		= "Pm39LV040",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= PMC_ID_NOPREFIX,
 		.model_id	= PMC_PM39LV040,
 		.total_size	= 512,
 		.page_size	= 4096,
 		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
-		.tested		= TEST_UNTESTED,
+		.tested		= TEST_OK_PR,
 		.probe		= probe_jedec,
 		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
 		.block_erasers =
@@ -5335,7 +5622,7 @@
 	{
 		.vendor		= "PMC",
 		.name		= "Pm49FL002",
-		.bustype	= CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.bustype	= BUS_LPC | BUS_FWH, /* A/A Mux */
 		.manufacture_id	= PMC_ID_NOPREFIX,
 		.model_id	= PMC_PM49FL002,
 		.total_size	= 256,
@@ -5366,7 +5653,7 @@
 	{
 		.vendor		= "PMC",
 		.name		= "Pm49FL004",
-		.bustype	= CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.bustype	= BUS_LPC | BUS_FWH, /* A/A Mux */
 		.manufacture_id	= PMC_ID_NOPREFIX,
 		.model_id	= PMC_PM49FL004,
 		.total_size	= 512,
@@ -5397,7 +5684,7 @@
 	{
 		.vendor		= "Sanyo",
 		.name		= "LF25FW203A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SANYO_ID,
 		.model_id	= SANYO_LE25FW203A,
 		.total_size	= 2048,
@@ -5422,8 +5709,38 @@
 
 	{
 		.vendor		= "Sharp",
+		.name		= "LH28F008BJT-BTLZ1",
+		.bustype	= BUS_PARALLEL,
+		.manufacture_id	= SHARP_ID,
+		.model_id	= SHARP_LH28F008BJxxPB,
+		.total_size	= 1024,
+		.page_size	= 64 * 1024,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{8 * 1024, 8},
+					{64 * 1024, 15}
+				 },
+				.block_erase = erase_block_82802ab,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = erase_sector_49lfxxxc,
+			}
+		},
+		.unlock		= unlock_lh28f008bjt,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Sharp",
 		.name		= "LHF00L04",
-		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.bustype	= BUS_FWH, /* A/A Mux */
 		.manufacture_id	= SHARP_ID,
 		.model_id	= SHARP_LHF00L04,
 		.total_size	= 1024,
@@ -5456,7 +5773,7 @@
 	{
 		.vendor		= "Spansion",
 		.name		= "S25FL004A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SPANSION_ID,
 		.model_id	= SPANSION_S25FL004A,
 		.total_size	= 512,
@@ -5477,12 +5794,13 @@
 		.unlock		= spi_disable_blockprotect,
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "Spansion",
 		.name		= "S25FL004A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SPANSION_ID,
 		.model_id	= SPANSION_S25FL004A,
 		.total_size	= 512,
@@ -5508,7 +5826,7 @@
 	{
 		.vendor		= "Spansion",
 		.name		= "S25FL008A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SPANSION_ID,
 		.model_id	= SPANSION_S25FL008A,
 		.total_size	= 1024,
@@ -5535,7 +5853,7 @@
 	{
 		.vendor		= "Spansion",
 		.name		= "S25FL016A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SPANSION_ID,
 		.model_id	= SPANSION_S25FL016A,
 		.total_size	= 2048,
@@ -5562,7 +5880,7 @@
 	{
 		.vendor		= "Spansion",
 		.name		= "S25FL032A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SPANSION_ID,
 		.model_id	= SPANSION_S25FL032A,
 		.total_size	= 4096,
@@ -5583,12 +5901,13 @@
 		.unlock		= spi_disable_blockprotect,
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "Spansion",
 		.name		= "S25FL064A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SPANSION_ID,
 		.model_id	= SPANSION_S25FL064A,
 		.total_size	= 8192,
@@ -5609,12 +5928,73 @@
 		.unlock		= spi_disable_blockprotect,
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "SST",
-		.name		= "SST25VF010.REMS",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.name		= "SST25LF040A",
+		.bustype	= BUS_SPI,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST25VF040_REMS,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_OK_PREW,
+		.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, /* AAI supported, but opcode is 0xAF */
+		.read		= spi_chip_read,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST25LF080A",
+		.bustype	= BUS_SPI,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST25VF080_REMS,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_res2,
+		.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 = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			},
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_1, /* AAI supported, but opcode is 0xAF */
+		.read		= spi_chip_read,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST25VF010",
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST25VF010_REMS,
 		.total_size	= 128,
@@ -5636,14 +6016,15 @@
 			},
 		},
 		.unlock		= spi_disable_blockprotect,
-		.write		= spi_chip_write_1,
+		.write		= spi_aai_write,
 		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "Spansion",
 		.name		= "S25FL032A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SPANSION_ID,
 		.model_id	= SPANSION_S25FL032A,
 		.total_size	= 4096,
@@ -5669,7 +6050,7 @@
 	{
 		.vendor		= "Spansion",
 		.name		= "S25FL064A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SPANSION_ID,
 		.model_id	= SPANSION_S25FL064A,
 		.total_size	= 8192,
@@ -5695,7 +6076,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST25VF010.REMS",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST25VF010_REMS,
 		.total_size	= 128,
@@ -5724,7 +6105,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST25VF016B",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST25VF016B,
 		.total_size	= 2048,
@@ -5752,7 +6133,7 @@
 			},
 		},
 		.unlock		= spi_disable_blockprotect,
-		.write		= spi_chip_write_1,
+		.write		= spi_chip_write_1, /* AAI supported, but opcode is 0xAF */
 		.read		= spi_chip_read,
 		.voltage	= {2700, 3600},
 	},
@@ -5760,7 +6141,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST25VF032B",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST25VF032B,
 		.total_size	= 4096,
@@ -5796,7 +6177,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST25VF064C",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST25VF064C,
 		.total_size	= 8192,
@@ -5831,8 +6212,8 @@
 
 	{
 		.vendor		= "SST",
-		.name		= "SST25VF040.REMS",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.name		= "SST25VF040",
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST25VF040_REMS,
 		.total_size	= 512,
@@ -5854,14 +6235,15 @@
 			},
 		},
 		.unlock		= spi_disable_blockprotect,
-		.write		= spi_chip_write_1,
+		.write		= spi_aai_write,
 		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "SST",
 		.name		= "SST25VF040B",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST25VF040B,
 		.total_size	= 512,
@@ -5889,44 +6271,15 @@
 			},
 		},
 		.unlock		= spi_disable_blockprotect,
-		.write		= spi_chip_write_1,
+		.write		= spi_aai_write,
 		.read		= spi_chip_read,
 		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "SST",
-		.name		= "SST25LF040A.RES",
-		.bustype	= CHIP_BUSTYPE_SPI,
-		.manufacture_id	= SST_ID,
-		.model_id	= SST_SST25VF040_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,
-	},
-
-	{
-		.vendor		= "SST",
 		.name		= "SST25VF040B.REMS",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST25VF040B_REMS,
 		.total_size	= 512,
@@ -5954,14 +6307,15 @@
 			},
 		},
 		.unlock		= spi_disable_blockprotect,
-		.write		= spi_chip_write_1,
+		.write		= spi_aai_write,
 		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "SST",
 		.name		= "SST25VF080B",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST25VF080B,
 		.total_size	= 1024,
@@ -5997,7 +6351,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST28SF040A",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST28SF040,
 		.total_size	= 512,
@@ -6025,7 +6379,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST29EE010",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST29EE010,
 		.total_size	= 128,
@@ -6049,7 +6403,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST29LE010",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST29LE010,
 		.total_size	= 128,
@@ -6073,7 +6427,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST29EE020A",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST29EE020A,
 		.total_size	= 256,
@@ -6097,7 +6451,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST29LE020",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST29LE020,
 		.total_size	= 256,
@@ -6121,7 +6475,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST39SF512",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST39SF512,
 		.total_size	= 64,
@@ -6148,7 +6502,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST39SF010A",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST39SF010,
 		.total_size	= 128,
@@ -6175,7 +6529,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST39SF020A",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST39SF020,
 		.total_size	= 256,
@@ -6202,13 +6556,13 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST39SF040",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST39SF040,
 		.total_size	= 512,
 		.page_size	= 4096,
 		.feature_bits	= FEATURE_EITHER_RESET,
-		.tested		= TEST_OK_PRE,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_jedec,
 		.probe_timing	= 1,			/* 150 ns */
 		.block_erasers	=
@@ -6229,7 +6583,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST39VF512",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST39VF512,
 		.total_size	= 64,
@@ -6256,7 +6610,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST39VF010",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST39VF010,
 		.total_size	= 128,
@@ -6283,7 +6637,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST39VF020",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST39VF020,
 		.total_size	= 256,
@@ -6310,7 +6664,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST39VF040",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST39VF040,
 		.total_size	= 512,
@@ -6337,7 +6691,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST39VF080",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST39VF080,
 		.total_size	= 1024,
@@ -6367,7 +6721,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST49LF002A/B",
-		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.bustype	= BUS_FWH, /* A/A Mux */
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST49LF002A,
 		.total_size	= 256,
@@ -6399,7 +6753,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST49LF003A/B",
-		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.bustype	= BUS_FWH, /* A/A Mux */
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST49LF003A,
 		.total_size	= 384,
@@ -6434,7 +6788,7 @@
 		 */
 		.vendor		= "SST",
 		.name		= "SST49LF004A/B",
-		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.bustype	= BUS_FWH, /* A/A Mux */
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST49LF004A,
 		.total_size	= 512,
@@ -6466,7 +6820,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST49LF004C",
-		.bustype	= CHIP_BUSTYPE_FWH,
+		.bustype	= BUS_FWH,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST49LF004C,
 		.total_size	= 512,
@@ -6499,13 +6853,13 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST49LF008A",
-		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.bustype	= BUS_FWH, /* A/A Mux */
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST49LF008A,
 		.total_size	= 1024,
 		.page_size	= 64 * 1024,
 		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
-		.tested		= TEST_OK_PRE,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_jedec,
 		.probe_timing	= 1,		/* 150 ns */
 		.block_erasers	=
@@ -6531,7 +6885,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST49LF008C",
-		.bustype	= CHIP_BUSTYPE_FWH,
+		.bustype	= BUS_FWH,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST49LF008C,
 		.total_size	= 1024,
@@ -6564,7 +6918,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST49LF016C",
-		.bustype	= CHIP_BUSTYPE_FWH,
+		.bustype	= BUS_FWH,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST49LF016C,
 		.total_size	= 2048,
@@ -6597,7 +6951,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST49LF020",
-		.bustype	= CHIP_BUSTYPE_LPC,
+		.bustype	= BUS_LPC,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST49LF020,
 		.total_size	= 256,
@@ -6627,7 +6981,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST49LF020A",
-		.bustype	= CHIP_BUSTYPE_LPC,
+		.bustype	= BUS_LPC,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST49LF020A,
 		.total_size	= 256,
@@ -6657,7 +7011,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST49LF040",
-		.bustype	= CHIP_BUSTYPE_LPC,
+		.bustype	= BUS_LPC,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST49LF040,
 		.total_size	= 512,
@@ -6687,13 +7041,13 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST49LF040B",
-		.bustype	= CHIP_BUSTYPE_LPC, /* A/A Mux */
+		.bustype	= BUS_LPC, /* A/A Mux */
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST49LF040B,
 		.total_size	= 512,
 		.page_size	= 64 * 1024,
 		.feature_bits	= FEATURE_EITHER_RESET | FEATURE_REGISTERMAP,
-		.tested		= TEST_OK_PRE,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_jedec,
 		.probe_timing	= 1,		/* 150ns */
 		.block_erasers	=
@@ -6718,7 +7072,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST49LF080A",
-		.bustype	= CHIP_BUSTYPE_LPC, /* A/A Mux */
+		.bustype	= BUS_LPC, /* A/A Mux */
 		.manufacture_id	= SST_ID,
  		.model_id	= SST_SST49LF080A,
 		.total_size	= 1024,
@@ -6748,7 +7102,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "SST49LF160C",
-		.bustype	= CHIP_BUSTYPE_LPC,
+		.bustype	= BUS_LPC,
 		.manufacture_id	= SST_ID,
 		.model_id	= SST_SST49LF160C,
 		.total_size	= 2048,
@@ -6781,7 +7135,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M25P05-A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25P05A,
 		.total_size	= 64,
@@ -6812,8 +7166,8 @@
 	 */
 	{
 		.vendor		= "ST",
-		.name		= "M25P05.RES",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.name		= "M25P05",
+		.bustype	= BUS_SPI,
 		.manufacture_id	= 0, /* Not used. */
 		.model_id	= ST_M25P05_RES,
 		.total_size	= 64,
@@ -6840,7 +7194,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M25P10-A",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25P10A,
 		.total_size	= 128,
@@ -6867,8 +7221,8 @@
 	/* The ST M25P10 has the same problem as the M25P05. */
 	{
 		.vendor		= "ST",
-		.name		= "M25P10.RES",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.name		= "M25P10",
+		.bustype	= BUS_SPI,
 		.manufacture_id	= 0, /* Not used. */
 		.model_id	= ST_M25P10_RES,
 		.total_size	= 128,
@@ -6895,7 +7249,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M25P20",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25P20,
 		.total_size	= 256,
@@ -6920,9 +7274,9 @@
 	},
 
 	{
-		.vendor		= "ST",
+		.vendor		= "ST", /* Numonyx */
 		.name		= "M25P40",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25P40,
 		.total_size	= 512,
@@ -6949,7 +7303,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M25P40-old",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= 0, /* Not used. */
 		.model_id	= ST_M25P40_RES,
 		.total_size	= 512,
@@ -6975,7 +7329,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M25P80",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25P80,
 		.total_size	= 1024,
@@ -7002,7 +7356,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M25P16",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25P16,
 		.total_size	= 2048,
@@ -7029,7 +7383,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M25P32",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25P32,
 		.total_size	= 4096,
@@ -7056,7 +7410,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M25P64",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25P64,
 		.total_size	= 8192,
@@ -7083,7 +7437,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M25P128",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25P128,
 		.total_size	= 16384,
@@ -7110,7 +7464,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M25PX16",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25PX16,
 		.total_size	= 2048,
@@ -7139,7 +7493,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M25PX32",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25PX32,
 		.total_size	= 4096,
@@ -7163,12 +7517,13 @@
 		.unlock		= spi_disable_blockprotect,
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
 	},
 
 	{
 		.vendor		= "ST",
 		.name		= "M25PX64",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25PX64,
 		.total_size	= 8192,
@@ -7197,7 +7552,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M25PX32",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25PX32,
 		.total_size	= 4096,
@@ -7226,7 +7581,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M25PX64",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M25PX64,
 		.total_size	= 8192,
@@ -7255,7 +7610,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M29F002B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M29F002B,
 		.total_size	= 256,
@@ -7281,13 +7636,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
-		.voltage	= {4750, 5250}, /* Datasheet says some are only 4.75-5.25 V */
+		.voltage	= {4750, 5250}, /* 4.75-5.25V for type -X, others 4.5-5.5V */
 	},
 
 	{
 		.vendor		= "ST",
 		.name		= "M29F002T/NT",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M29F002T,
 		.total_size	= 256,
@@ -7313,13 +7668,13 @@
 		},
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
-		.voltage	= {4750, 5250}, /* Datasheet says some are only 4.75-5.25 V */
+		.voltage	= {4750, 5250}, /* 4.75-5.25V for type -X, others 4.5-5.5V */
 	},
 
 	{
 		.vendor		= "ST",
 		.name		= "M29F040B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M29F040B,
 		.total_size	= 512,
@@ -7347,7 +7702,7 @@
 		/* FIXME: this has WORD/BYTE sequences; 2AA for word, 555 for byte */
 		.vendor		= "ST",
 		.name		= "M29F400BB",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M29F400BB,
 		.total_size	= 512,
@@ -7379,7 +7734,7 @@
 		/* FIXME: this has WORD/BYTE sequences; 2AA for word, 555 for byte */
 		.vendor		= "ST",
 		.name		= "M29F400BT",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M29F400BT,
 		.total_size	= 512,
@@ -7411,7 +7766,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M29W010B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M29W010B,
 		.total_size	= 128,
@@ -7438,7 +7793,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M29W040B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M29W040B,
 		.total_size	= 512,
@@ -7465,7 +7820,7 @@
         {
                 .vendor         = "ST",
                 .name           = "M29W512B",
-                .bustype        = CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
                 .manufacture_id = ST_ID,
                 .model_id       = ST_M29W512B,
                 .total_size     = 64,
@@ -7489,7 +7844,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M50FLW040A",
-		.bustype	= CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_LPC, /* A/A Mux */
+		.bustype	= BUS_FWH | BUS_LPC, /* A/A Mux */
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M50FLW040A,
 		.total_size	= 512,
@@ -7522,7 +7877,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M50FLW040B",
-		.bustype	= CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_LPC, /* A/A Mux */
+		.bustype	= BUS_FWH | BUS_LPC, /* A/A Mux */
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M50FLW040B,
 		.total_size	= 512,
@@ -7555,7 +7910,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M50FLW080A",
-		.bustype	= CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_LPC, /* A/A Mux */
+		.bustype	= BUS_FWH | BUS_LPC, /* A/A Mux */
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M50FLW080A,
 		.total_size	= 1024,
@@ -7588,7 +7943,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M50FLW080B",
-		.bustype	= CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_LPC, /* A/A Mux */
+		.bustype	= BUS_FWH | BUS_LPC, /* A/A Mux */
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M50FLW080B,
 		.total_size	= 1024,
@@ -7621,7 +7976,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M50FW002",
-		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.bustype	= BUS_FWH, /* A/A Mux */
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M50FW002,
 		.total_size	= 256,
@@ -7651,7 +8006,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M50FW016",
-		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.bustype	= BUS_FWH, /* A/A Mux */
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M50FW016,
 		.total_size	= 2048,
@@ -7676,7 +8031,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M50FW040",
-		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.bustype	= BUS_FWH, /* A/A Mux */
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M50FW040,
 		.total_size	= 512,
@@ -7701,7 +8056,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M50FW080",
-		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.bustype	= BUS_FWH, /* A/A Mux */
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M50FW080,
 		.total_size	= 1024,
@@ -7726,7 +8081,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "M50LPW116",
-		.bustype	= CHIP_BUSTYPE_LPC, /* A/A Mux */
+		.bustype	= BUS_LPC, /* A/A Mux */
 		.manufacture_id	= ST_ID,
 		.model_id	= ST_M50LPW116,
 		.total_size	= 2048,
@@ -7757,7 +8112,7 @@
 	{
 		.vendor		= "SyncMOS/MoselVitelic",
 		.name		= "{F,S,V}29C51001B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SYNCMOS_MVC_ID,
 		.model_id	= SM_MVC_29C51001B,
 		.total_size	= 128,
@@ -7784,7 +8139,7 @@
 	{
 		.vendor		= "SyncMOS/MoselVitelic",
 		.name		= "{F,S,V}29C51001T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SYNCMOS_MVC_ID,
 		.model_id	= SM_MVC_29C51001T,
 		.total_size	= 128,
@@ -7811,7 +8166,7 @@
 	{
 		.vendor		= "SyncMOS/MoselVitelic",
 		.name		= "{F,S,V}29C51002B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SYNCMOS_MVC_ID,
 		.model_id	= SM_MVC_29C51002B,
 		.total_size	= 256,
@@ -7837,13 +8192,13 @@
 	{
 		.vendor		= "SyncMOS/MoselVitelic",
 		.name		= "{F,S,V}29C51002T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_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_PRE,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_jedec,
 		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
 		.block_erasers	=
@@ -7863,7 +8218,7 @@
 	{
 		.vendor		= "SyncMOS/MoselVitelic",
 		.name		= "{F,S,V}29C51004B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SYNCMOS_MVC_ID,
 		.model_id	= SM_MVC_29C51004B,
 		.total_size	= 512,
@@ -7890,7 +8245,7 @@
 	{
 		.vendor		= "SyncMOS/MoselVitelic",
 		.name		= "{F,S,V}29C51004T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SYNCMOS_MVC_ID,
 		.model_id	= SM_MVC_29C51004T,
 		.total_size	= 512,
@@ -7917,7 +8272,7 @@
 	{
 		.vendor		= "SyncMOS/MoselVitelic",
 		.name		= "{S,V}29C31004B",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SYNCMOS_MVC_ID,
 		.model_id	= SM_MVC_29C31004B,
 		.total_size	= 512,
@@ -7944,7 +8299,7 @@
 	{
 		.vendor		= "SyncMOS/MoselVitelic",
 		.name		= "{S,V}29C31004T",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= SYNCMOS_MVC_ID,
 		.model_id	= SM_MVC_29C31004T,
 		.total_size	= 512,
@@ -7971,7 +8326,7 @@
 	{
 		.vendor		= "TI",
 		.name		= "TMS29F002RB",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= TI_OLD_ID,
 		.model_id	= TI_TMS29F002RB,
 		.total_size	= 256,
@@ -8003,7 +8358,7 @@
 	{
 		.vendor		= "TI",
 		.name		= "TMS29F002RT",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= TI_OLD_ID,
 		.model_id	= TI_TMS29F002RT,
 		.total_size	= 256,
@@ -8035,7 +8390,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W25Q80",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= WINBOND_NEX_ID,
 		.model_id	= WINBOND_NEX_W25Q80,
 		.total_size	= 1024,
@@ -8073,7 +8428,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W25Q16",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= WINBOND_NEX_ID,
 		.model_id	= WINBOND_NEX_W25Q16,
 		.total_size	= 2048,
@@ -8111,7 +8466,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W25Q32",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= WINBOND_NEX_ID,
 		.model_id	= WINBOND_NEX_W25Q32,
 		.total_size	= 4096,
@@ -8149,7 +8504,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W25Q64",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= WINBOND_NEX_ID,
 		.model_id	= WINBOND_NEX_W25Q64,
 		.total_size	= 8192,
@@ -8186,7 +8541,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W25Q128",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= WINBOND_NEX_ID,
 		.model_id	= WINBOND_NEX_W25Q128,
 		.total_size	= 16384,
@@ -8222,7 +8577,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W25X10",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= WINBOND_NEX_ID,
 		.model_id	= WINBOND_NEX_W25X10,
 		.total_size	= 128,
@@ -8254,7 +8609,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W25X20",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= WINBOND_NEX_ID,
 		.model_id	= WINBOND_NEX_W25X20,
 		.total_size	= 256,
@@ -8286,7 +8641,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W25X40",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= WINBOND_NEX_ID,
 		.model_id	= WINBOND_NEX_W25X40,
 		.total_size	= 512,
@@ -8318,7 +8673,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W25X80",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= WINBOND_NEX_ID,
 		.model_id	= WINBOND_NEX_W25X80,
 		.total_size	= 1024,
@@ -8350,7 +8705,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W25X16",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= WINBOND_NEX_ID,
 		.model_id	= WINBOND_NEX_W25X16,
 		.total_size	= 2048,
@@ -8387,7 +8742,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W25X32",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= WINBOND_NEX_ID,
 		.model_id	= WINBOND_NEX_W25X32,
 		.total_size	= 4096,
@@ -8424,7 +8779,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W25X64",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= WINBOND_NEX_ID,
 		.model_id	= WINBOND_NEX_W25X64,
 		.total_size	= 8192,
@@ -8460,8 +8815,31 @@
 
 	{
 		.vendor		= "Winbond",
+		.name		= "W29C010(M)/W29C011A/W29EE011/W29EE012-old",
+		.bustype	= BUS_PARALLEL,
+		.manufacture_id	= WINBOND_ID,
+		.model_id	= WINBOND_W29C010,
+		.total_size	= 128,
+		.page_size	= 128,
+		.feature_bits	= FEATURE_LONG_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_w29ee011,
+		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (w29ee011.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec,
+		.read		= read_memmapped,
+	},
+
+	{/* W29EE011, W29EE012, W29C010M, W29C011A do not support probe_jedec according to the datasheet, but it works for newer(?) steppings. */
+		.vendor		= "Winbond",
 		.name		= "W29C010(M)/W29C011A/W29EE011/W29EE012",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W29C010,
 		.total_size	= 128,
@@ -8484,7 +8862,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W29C020(C)/W29C022",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W29C020,
 		.total_size	= 256,
@@ -8508,7 +8886,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W29C040/P",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W29C040,
 		.total_size	= 512,
@@ -8531,31 +8909,8 @@
 
 	{
 		.vendor		= "Winbond",
-		.name		= "W29C010(M)/W29C011A/W29EE011/W29EE012",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
-		.manufacture_id	= WINBOND_ID,
-		.model_id	= WINBOND_W29C010,
-		.total_size	= 128,
-		.page_size	= 128,
-		.feature_bits	= FEATURE_LONG_RESET,
-		.tested		= TEST_OK_PRE,
-		.probe		= probe_w29ee011,
-		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (w29ee011.c) */
-		.block_erasers	=
-		{
-			{
-				.eraseblocks = { {128 * 1024, 1} },
-				.block_erase = erase_chip_block_jedec,
-			}
-		},
-		.write		= write_jedec,
-		.read		= read_memmapped,
-	},
-
-	{
-		.vendor		= "Winbond",
 		.name		= "W39L040",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W39L040,
 		.total_size	= 512,
@@ -8580,20 +8935,21 @@
 		.printlock	= printlock_w39l040,
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
 	},
 
 	{
 		.vendor		= "Winbond",
 		.name		= "W39V040A",
-		.bustype	= CHIP_BUSTYPE_LPC,
+		.bustype	= BUS_LPC,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W39V040A,
 		.total_size	= 512,
 		.page_size	= 64 * 1024,
 		.feature_bits	= FEATURE_EITHER_RESET,
-		.tested		= TEST_OK_PR,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_jedec,
-		.probe_timing	= 10, 
+		.probe_timing	= 10,
 		.block_erasers	=
 		{
 			{
@@ -8613,15 +8969,15 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W39V040B",
-		.bustype	= CHIP_BUSTYPE_LPC,
+		.bustype	= BUS_LPC,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W39V040B,
 		.total_size	= 512,
 		.page_size	= 64 * 1024,
 		.feature_bits	= FEATURE_EITHER_RESET,
-		.tested		= TEST_OK_PRE,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_jedec,
-		.probe_timing	= 10, 
+		.probe_timing	= 10,
 		.block_erasers	=
 		{
 			{
@@ -8641,13 +8997,13 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W39V040C",
-		.bustype	= CHIP_BUSTYPE_LPC,
+		.bustype	= BUS_LPC,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W39V040C,
 		.total_size	= 512,
 		.page_size	= 64 * 1024,
 		.feature_bits	= FEATURE_EITHER_RESET,
-		.tested		= TEST_UNTESTED,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_jedec,
 		.probe_timing	= 10,
 		.block_erasers	=
@@ -8669,7 +9025,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W39V040FA",
-		.bustype	= CHIP_BUSTYPE_FWH,
+		.bustype	= BUS_FWH,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W39V040FA,
 		.total_size	= 512,
@@ -8701,7 +9057,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W39V040FB",
-		.bustype	= CHIP_BUSTYPE_FWH,
+		.bustype	= BUS_FWH,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W39V040B,
 		.total_size	= 512,
@@ -8724,12 +9080,13 @@
 		.unlock		= unlock_w39v040fb,
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* Also has 12V fast program */
 	},
 
 	{
 		.vendor		= "Winbond",
 		.name		= "W39V040FC",
-		.bustype	= CHIP_BUSTYPE_FWH,
+		.bustype	= BUS_FWH,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W39V040C,
 		.total_size	= 512,
@@ -8751,12 +9108,13 @@
 		.printlock	= printlock_w39v040fc,
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* Also has 12V fast program */
 	},
 
 	{
 		.vendor		= "Winbond",
 		.name		= "W39V040FB",
-		.bustype	= CHIP_BUSTYPE_FWH,
+		.bustype	= BUS_FWH,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W39V040B,
 		.total_size	= 512,
@@ -8784,7 +9142,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W39V040FC",
-		.bustype	= CHIP_BUSTYPE_FWH,
+		.bustype	= BUS_FWH,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W39V040C,
 		.total_size	= 512,
@@ -8811,7 +9169,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W39V080A",
-		.bustype	= CHIP_BUSTYPE_LPC,
+		.bustype	= BUS_LPC,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W39V080A,
 		.total_size	= 1024,
@@ -8839,13 +9197,13 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W49F002U/N",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W49F002U,
 		.total_size	= 256,
 		.page_size	= 128,
 		.feature_bits	= FEATURE_EITHER_RESET,
-		.tested		= TEST_OK_PRE,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_jedec,
 		.probe_timing	= 10,
 		.block_erasers	=
@@ -8871,7 +9229,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W49F020",
-		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.bustype	= BUS_PARALLEL,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W49F020,
 		.total_size	= 256,
@@ -8895,7 +9253,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W49V002A",
-		.bustype	= CHIP_BUSTYPE_LPC,
+		.bustype	= BUS_LPC,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W49V002A,
 		.total_size	= 256,
@@ -8927,7 +9285,7 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W49V002FA",
-		.bustype	= CHIP_BUSTYPE_FWH,
+		.bustype	= BUS_FWH,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W49V002FA,
 		.total_size	= 256,
@@ -8959,13 +9317,13 @@
 	{
 		.vendor		= "Winbond",
 		.name		= "W39V080FA",
-		.bustype	= CHIP_BUSTYPE_FWH,
+		.bustype	= BUS_FWH,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W39V080FA,
 		.total_size	= 1024,
 		.page_size	= 64 * 1024,
 		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
-		.tested		= TEST_OK_PRE,
+		.tested		= TEST_OK_PREW,
 		.probe		= probe_jedec,
 		.probe_timing	= 10,
 		.block_erasers	=
@@ -8982,13 +9340,13 @@
 		.unlock		= unlock_w39v080fa,
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
-		.voltage	= {3000, 3600}, /* 12 V fast program mode */
+		.voltage	= {3000, 3600}, /* Also has 12V fast program */
 	},
 
 	{
 		.vendor		= "Winbond",
 		.name		= "W39V080FA (dual mode)",
-		.bustype	= CHIP_BUSTYPE_FWH,
+		.bustype	= BUS_FWH,
 		.manufacture_id	= WINBOND_ID,
 		.model_id	= WINBOND_W39V080FA_DM,
 		.total_size	= 512,
@@ -9010,32 +9368,35 @@
 		.printlock	= printlock_w39v080fa_dual,
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* Also has 12V fast program */
 	},
-#if defined(CONFIG_INTERNAL) && (defined(__i386__) || defined(__x86_64__))
+
 	{
-		.vendor		= "Intel",
-		.name		= "Hardware Sequencing",
-		.bustype	= CHIP_BUSTYPE_SPI,
-		.manufacture_id	= INTEL_ID,
-		.model_id	= INTEL_HWSEQ,
+		.vendor		= "Programmer",
+		.name		= "Opaque flash chip",
+		.bustype	= BUS_PROG,
+		.manufacture_id	= PROGMANUF_ID,
+		.model_id	= PROGDEV_ID,
 		.total_size	= 0,
 		.page_size	= 256,
-		.tested		= TEST_OK_PREW,
-		.probe		= ich_hwseq_probe,
+		/* probe is assumed to work, rest will be filled in by probe */
+		.tested		= TEST_OK_PROBE,
+		.probe		= probe_opaque,
+		/* eraseblock sizes will be set by the probing function */
 		.block_erasers	=
 		{
-			{ /* erase blocks will be set by the probing function */
-				.block_erase = ich_hwseq_block_erase,
+			{
+				.block_erase = erase_opaque,
 			}
 		},
-		.write		= ich_hwseq_write_256,
-		.read		= ich_hwseq_read,
+		.write		= write_opaque,
+		.read		= read_opaque,
 	},
-#endif // defined(CONFIG_INTERNAL) && (defined(__i386__) || defined(__x86_64__))
+
 	{
 		.vendor		= "AMIC",
 		.name		= "unknown AMIC SPI chip",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= AMIC_ID,
 		.model_id	= GENERIC_DEVICE_ID,
 		.total_size	= 0,
@@ -9050,7 +9411,7 @@
 	{
 		.vendor		= "Atmel",
 		.name		= "unknown Atmel SPI chip",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= GENERIC_DEVICE_ID,
 		.total_size	= 0,
@@ -9065,7 +9426,7 @@
 	{
 		.vendor		= "Eon",
 		.name		= "unknown Eon SPI chip",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= EON_ID_NOPREFIX,
 		.model_id	= GENERIC_DEVICE_ID,
 		.total_size	= 0,
@@ -9080,7 +9441,7 @@
 	{
 		.vendor		= "Macronix",
 		.name		= "unknown Macronix SPI chip",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= MACRONIX_ID,
 		.model_id	= GENERIC_DEVICE_ID,
 		.total_size	= 0,
@@ -9095,7 +9456,7 @@
 	{
 		.vendor		= "PMC",
 		.name		= "unknown PMC SPI chip",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= PMC_ID,
 		.model_id	= GENERIC_DEVICE_ID,
 		.total_size	= 0,
@@ -9110,7 +9471,7 @@
 	{
 		.vendor		= "SST",
 		.name		= "unknown SST SPI chip",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SST_ID,
 		.model_id	= GENERIC_DEVICE_ID,
 		.total_size	= 0,
@@ -9125,7 +9486,7 @@
 	{
 		.vendor		= "ST",
 		.name		= "unknown ST SPI chip",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= ST_ID,
 		.model_id	= GENERIC_DEVICE_ID,
 		.total_size	= 0,
@@ -9140,7 +9501,7 @@
 	{
 		.vendor		= "Sanyo",
 		.name		= "unknown Sanyo SPI chip",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= SANYO_ID,
 		.model_id	= GENERIC_DEVICE_ID,
 		.total_size	= 0,
@@ -9155,7 +9516,7 @@
 	{
 		.vendor		= "Generic",
 		.name		= "Variable Size SPI chip",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= VARIABLE_SIZE_MANUF_ID,
 		.model_id	= VARIABLE_SIZE_DEVICE_ID,
 		.total_size	= 64,  /* This size is set temporarily */
@@ -9179,7 +9540,7 @@
 	{
 		.vendor		= "Generic",
 		.name		= "unknown SPI chip (RDID)",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= GENERIC_MANUF_ID,
 		.model_id	= GENERIC_DEVICE_ID,
 		.total_size	= 0,
@@ -9192,7 +9553,7 @@
 	{
 		.vendor		= "Generic",
 		.name		= "unknown SPI chip (REMS)",
-		.bustype	= CHIP_BUSTYPE_SPI,
+		.bustype	= BUS_SPI,
 		.manufacture_id	= GENERIC_MANUF_ID,
 		.model_id	= GENERIC_DEVICE_ID,
 		.total_size	= 0,
diff --git a/flashchips.h b/flashchips.h
index b68d628..b6f9861 100644
--- a/flashchips.h
+++ b/flashchips.h
@@ -187,6 +187,7 @@
 #define ATMEL_AT49F020		0x0B
 #define ATMEL_AT49F002N		0x07	/* for AT49F002(N)  */
 #define ATMEL_AT49F002NT		0x08	/* for AT49F002(N)T */
+#define ATMEL_AT49LH002		0xE9
 
 /* Bright Microelectronics has the same manufacturer ID as Hyundai... */
 #define BRIGHT_ID		0xAD	/* Bright Microelectronics */
@@ -195,6 +196,7 @@
 #define BRIGHT_BM29F400T	0xAD
 
 #define CATALYST_ID		0x31	/* Catalyst */
+#define CATALYST_CAT28F512	0xB8
 
 #define EMST_ID			0x8C	/* EMST / EFST Elite Flash Storage */
 #define EMST_F25L008A		0x2014
@@ -249,6 +251,13 @@
 #define EON_EN25F80		0x3114
 #define EON_EN25F16		0x3115
 #define EON_EN25F32		0x3116
+#define EON_EN25Q40		0x3013
+#define EON_EN25Q80		0x3014
+#define EON_EN25Q16		0x3015	/* Same as EN25D16 */
+#define EON_EN25Q32		0x3016	/* Same as EN25Q32A and EN25Q32B */
+#define EON_EN25Q64		0x3017
+#define EON_EN25Q128		0x3018
+#define EON_EN25QH16		0x7015
 #define EON_EN29F512		0x7F21
 #define EON_EN29F010		0x20
 #define EON_EN29F040A		0x7F04
@@ -374,8 +383,8 @@
 #define MACRONIX_MX25L3235D	0x5E16	/* MX25L3225D/MX25L3235D/MX25L3237D */
 #define MACRONIX_MX29F001B	0x19
 #define MACRONIX_MX29F001T	0x18
-#define MACRONIX_MX29F002B	0x34	/* Same as MX29F002NB */
-#define MACRONIX_MX29F002T	0xB0	/* Same as MX29F002NT */
+#define MACRONIX_MX29F002B	0x34	/* Same as MX29F002NB; N has reset pin n/c. */
+#define MACRONIX_MX29F002T	0xB0	/* Same as MX29F002NT; N has reset pin n/c. */
 #define MACRONIX_MX29F004B	0x46
 #define MACRONIX_MX29F004T	0x45
 #define MACRONIX_MX29F022T	0x36	/* Same as MX29F022NT */
@@ -479,7 +488,7 @@
 #define SST_SST25VF040_REMS	0x44	/* REMS or RES opcode, same as SST25LF040A */
 #define SST_SST25VF040B		0x258D
 #define SST_SST25VF040B_REMS	0x8D	/* REMS or RES opcode */
-#define SST_SST25VF080_REMS	0x80	/* REMS or RES opcode */
+#define SST_SST25VF080_REMS	0x80	/* REMS or RES opcode, same as SST25LF080A */
 #define SST_SST25VF080B		0x258E
 #define SST_SST25VF080B_REMS	0x8E	/* REMS or RES opcode */
 #define SST_SST25VF016B		0x2541
@@ -531,7 +540,7 @@
  * ST25P chips are SPI, first byte of device ID is memory type, second
  * byte of device ID is related to log(bitsize) at least for some chips.
  */
-#define ST_ID			0x20	/* ST / SGS/Thomson */
+#define ST_ID			0x20	/* ST / SGS/Thomson / Numonyx (later acquired by Micron) */
 #define ST_M25P05A		0x2010
 #define ST_M25P05_RES		0x10	/* Same code as M25P10. */
 #define ST_M25P10A		0x2011
@@ -649,4 +658,7 @@
 #define WINBOND_W49V002A	0xB0
 #define WINBOND_W49V002FA	0x32
 
+#define PROGMANUF_ID		0xFFFE	/* dummy ID for opaque chips behind a programmer */
+#define PROGDEV_ID		0x01	/* dummy ID for opaque chips behind a programmer */
+
 #endif /* !FLASHCHIPS_H */
diff --git a/flashrom.8 b/flashrom.8
index 5b11f6e..66cde4f 100644
--- a/flashrom.8
+++ b/flashrom.8
@@ -1,4 +1,4 @@
-.TH FLASHROM 8 "Jun 14, 2011"
+.TH FLASHROM 8 "Jul 25, 2011"
 .SH NAME
 flashrom \- detect, read, write, verify and erase flash chips
 .SH SYNOPSIS
@@ -12,13 +12,14 @@
 .B flashrom
 is a utility for detecting, reading, writing, verifying and erasing flash
 chips. It's often used to flash BIOS/EFI/coreboot/firmware images in-system
-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.
+using a supported mainboard. However, it also supports various external
+PCI/USB/parallel-port/serial-port based devices which can program flash chips,
+including some network cards (NICs), SATA/IDE controller cards, graphics cards,
+the Bus Pirate device, various FTDI FT2232/FT4232H based USB devices, and more.
 .PP
 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.
+TSOP48, and BGA 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
@@ -30,7 +31,7 @@
 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
+in probe-only mode and check the output. Also you are advised to make a
 backup of your current ROM contents with
 .B \-r
 before you try to write a new image.
@@ -38,6 +39,7 @@
 .B "\-r, \-\-read <file>"
 Read flash ROM contents and save them into the given
 .BR <file> .
+If the file already exists, it will be overwritten.
 .TP
 .B "\-w, \-\-write <file>"
 Write
@@ -45,6 +47,13 @@
 into flash ROM. This will first automatically
 .B erase
 the chip, then write to it.
+.sp
+In the process the chip is also read several times. First an in-memory backup
+is made for disaster recovery and to be able to skip regions that are
+already equal to the image file. This copy is updated along with the write
+operation. In case of erase errors it is even re-read completely. After
+writing has finished and if verification is enabled, the whole flash chip is
+read out and compared with the input image.
 .TP
 .B "\-n, \-\-noverify"
 Skip the automatic verification of flash ROM contents after writing. Using this
@@ -68,8 +77,8 @@
 .TP
 .B "\-V, \-\-verbose"
 More verbose output. This option can be supplied multiple times
-(max. 2 times, i.e.
-.BR \-VV )
+(max. 3 times, i.e.
+.BR \-VVV )
 for even more debug output.
 .TP
 .B "\-c, \-\-chip" <chipname>
@@ -137,7 +146,8 @@
 from flash layout.
 .TP
 .B "\-L, \-\-list\-supported"
-List the flash chips, chipsets, mainboards, and PCI card "programmers"
+List the flash chips, chipsets, mainboards, and external programmers
+(including PCI, USB, parallel port, and serial port based devices)
 supported by flashrom.
 .sp
 There are many unlisted boards which will work out of the box, without
@@ -153,15 +163,16 @@
 Same as
 .BR \-\-list\-supported ,
 but outputs the supported hardware in MediaWiki syntax, so that it can be
-easily pasted into the wiki page at http://www.flashrom.org/. Please note
-that MediaWiki output is not compiled in by default.
+easily pasted into the wiki page at
+.BR http://www.flashrom.org/ .
+Please note that MediaWiki output is not compiled in by default.
 .TP
 .B "\-p, \-\-programmer <name>[:parameter[,parameter[,parameter]]]"
 Specify the programmer device. Currently supported are:
 .sp
 .BR "* internal" " (default, for in-system flashing in the mainboard)"
 .sp
-.BR "* dummy" " (just prints all operations and accesses)"
+.BR "* dummy" " (virtual programmer for testing flashrom)"
 .sp
 .BR "* nic3com" " (for flash ROMs on 3COM network cards)"
 .sp
@@ -172,8 +183,7 @@
 .BR "* nicnatsemi" " (for flash ROMs on National Semiconductor DP838* network \
 cards)"
 .sp
-.BR "* nicintel" " (for parallel flash ROMs attached to Intel 10/100Mbit \
-network cards)
+.BR "* nicintel" " (for parallel flash ROMs on Intel 10/100Mbit network cards)
 .sp
 .BR "* gfxnvidia" " (for flash ROMs on NVIDIA graphics cards)"
 .sp
@@ -185,26 +195,28 @@
 .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)"
-.sp
 .BR "* ft2232_spi" " (for SPI flash ROMs attached to an FT2232/FT4232H family \
-based USB SPI programmer)"
+based USB SPI programmer), including the DLP Design DLP-USB1232H, \
+FTDI FT2232H Mini-Module, FTDI FT4232H Mini-Module, openbiosprog-spi, Amontec \
+JTAGkey/JTAGkey-tiny/JTAGkey-2, Dangerous Prototypes Bus Blaster, \
+Olimex ARM-USB-TINY/-H, Olimex ARM-USB-OCD/-H, TIAO/DIYGADGET USB
+Multi-Protocol Adapter (TUMPA), and GOEPEL PicoTAP.
 .sp
-.BR "* serprog" " (for flash ROMs attached to a programmer speaking serprog)"
+.BR "* serprog" " (for flash ROMs attached to a programmer speaking serprog), \
+including AVR flasher by Urja Rannikko, AVR flasher by eightdot, \
+Arduino Mega flasher by fritz, InSystemFlasher by Juhana Helovuo, and \
+atmegaXXu2-flasher by Stefan Tauner."
 .sp
 .BR "* buspirate_spi" " (for SPI flash ROMs attached to a Bus Pirate)"
 .sp
 .BR "* dediprog" " (for SPI flash ROMs attached to a Dediprog SF100)"
 .sp
-.BR "* rayer_spi" " (for SPI flash ROMs attached to a RayeR parport \
-based programmer)"
+.BR "* rayer_spi" " (for SPI flash ROMs attached to a RayeR parport "
+or Xilinx DLC5 compatible cable)
 .sp
-.BR "* nicintel_spi" " (for SPI flash ROMs attached to an Intel Gigabit \
-network cards)"
+.BR "* nicintel_spi" " (for SPI flash ROMs on Intel Gigabit network cards)"
 .sp
-.BR "* ogp_spi" " (for SPI flash ROMs attached to an Open Graphics Project \
-graphics card)"
+.BR "* ogp_spi" " (for SPI flash ROMs on Open Graphics Project graphics card)"
 .sp
 Some programmers have optional or mandatory parameters which are described
 in detail in the
@@ -279,17 +291,54 @@
 .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 the
+translation, flashrom should autodetect that configuration. If you want to
+set the I/O base port of the IT87 series SPI controller manually instead of
+using the value provided by the BIOS, use the 
 .sp
 .B "  flashrom \-p internal:it87spiport=portnum"
 .sp
-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
-.B it87spi
-programmer.
+syntax where
+.B portnum
+is the I/O port number (must be a multiple of 8). In the unlikely case
+flashrom doesn't detect an active IT87 LPC<->SPI bridge, please send a bug
+report so we can diagnose the problem.
+.sp
+If you have an Intel chipset with an ICH8 or later southbridge with SPI flash
+attached, and if a valid descriptor was written to it (e.g. by the vendor), the
+chipset provides an alternative way to access the flash chip(s) named
+.BR "Hardware Sequencing" .
+It is much simpler than the normal access method (called
+.BR "Software Sequencing" "),"
+but does not allow the software to choose the SPI commands to be sent.
+You can use the
+.sp
+.B "  flashrom \-p internal:ich_spi_mode=value"
+.sp
+syntax where value can be
+.BR auto ", " swseq " or " hwseq .
+By default
+.RB "(or when setting " ich_spi_mode=auto )
+the module tries to use swseq and only activates hwseq if need be (e.g. if
+important opcodes are inaccessible due to lockdown; or if more than one flash
+chip is attached). The other options (swseq, hwseq) select the respective mode
+(if possible).
+.sp
+If you have an Intel chipset with an ICH6 or later southbridge and if you want
+to set specific IDSEL values for a non-default flash chip or an embedded
+controller (EC), you can use the
+.sp
+.B "  flashrom \-p internal:fwh_idsel=value"
+.sp
+syntax where value is the 48-bit hexadecimal raw value to be written in the
+IDSEL registers of the Intel southbridge. The upper 32 bits use one hex digit
+each per 512 kB range between 0xffc00000 and 0xffffffff, and the lower 16 bits
+use one hex digit each per 1024 kB range between 0xff400000 and 0xff7fffff.
+The rightmost hex digit corresponds with the lowest address range. All address
+ranges have a corresponding sister range 4 MB below with identical IDSEL
+settings. The default value for ICH7 is given in the example below.
+.sp
+Example:
+.B "flashrom \-p internal:fwh_idsel=0x001122334567"
 .sp
 Using flashrom on laptops is dangerous and may easily make your hardware
 unusable (see also the
@@ -313,9 +362,18 @@
 dumb idea.
 .TP
 .BR "dummy " programmer
+The dummy programmer operates on a buffer in memory only. It provides a safe
+and fast way to test various aspects of flashrom and is mainly used in
+development and while debugging.
+.sp
+It is able to emulate some chips to a certain degree (basic
+identify/read/erase/write operations work).
+.sp
 An optional parameter specifies the bus types it
 should support. For that you have to use the
-.B "flashrom \-p dummy:bus=[type[+type[+type]]]"
+.sp
+.B "  flashrom \-p dummy:bus=[type[+type[+type]]]"
+.sp
 syntax where
 .B type
 can be
@@ -325,6 +383,52 @@
 .sp
 Example:
 .B "flashrom \-p dummy:bus=lpc+fwh"
+.sp
+The dummy programmer supports flash chip emulation for automated self-tests
+without hardware access. If you want to emulate a flash chip, use the
+.sp
+.B "  flashrom \-p dummy:emulate=chip"
+.sp
+syntax where
+.B chip
+is one of the following chips (please specify only the chip name, not the
+vendor):
+.sp
+.RB "* ST " M25P10.RES " SPI flash chip (RES, page write)"
+.sp
+.RB "* SST " SST25VF040.REMS " SPI flash chip (REMS, byte write)"
+.sp
+.RB "* SST " SST25VF032B " SPI flash chip (RDID, AAI write)"
+.sp
+Example:
+.B "flashrom -p dummy:emulate=SST25VF040.REMS"
+.sp
+If you use flash chip emulation, flash image persistence is available as well
+by using the
+.sp
+.B "  flashrom \-p dummy:emulate=chip,image=image.rom"
+.sp
+syntax where
+.B image.rom
+is the file where the simulated chip contents are read on flashrom startup and
+where the chip contents on flashrom shutdown are written to.
+.sp
+Example:
+.B "flashrom -p dummy:emulate=M25P10.RES,image=dummy.bin"
+.sp
+If you use SPI flash chip emulation for a chip which supports SPI page write
+with the default opcode, you can set the maximum allowed write chunk size with
+the
+.sp
+.B "  flashrom \-p dummy:emulate=chip,spi_write_256_chunksize=size"
+.sp
+syntax where
+.B size
+is the number of bytes (min. 1, max. 256).
+.sp
+Example:
+.sp
+.B "  flashrom -p dummy:emulate=M25P10.RES,spi_write_256_chunksize=5"
 .TP
 .BR "nic3com" , " nicrealtek" , " nicsmc1211" , " nicnatsemi" , " nicintel\
 " , " nicintel_spi" , " gfxnvidia" , " ogp_spi" , " drkaiser" , " satasii\
@@ -332,7 +436,9 @@
 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
-.BR "flashrom \-p xxxx:pci=bb:dd.f" ,
+.sp
+.BR "  flashrom \-p xxxx:pci=bb:dd.f" ,
+.sp
 where
 .B xxxx
 is the name of the programmer
@@ -346,19 +452,6 @@
 Example:
 .B "flashrom \-p nic3com:pci=05:04.0"
 .TP
-.BR "it87spi " programmer
-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
-.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 "ft2232_spi " programmer
 An optional parameter specifies the controller
 type and interface/port it should support. For that you have to use the
@@ -368,8 +461,9 @@
 syntax where
 .B model
 can be
-.BR 2232H ", " 4232H ", " jtagkey ", " openmoko ", " arm-usb-tiny ", " \
-arm-usb-tiny-h ", " arm-usb-ocd " or " arm-usb-ocd-h
+.BR 2232H ", " 4232H ", " jtagkey ", " busblaster ", " openmoko ", " \
+arm-usb-tiny ", " arm-usb-tiny-h ", " arm-usb-ocd ", " arm-usb-ocd-h \
+", " tumpa ", or " picotap
 and
 .B interface
 can be
@@ -440,8 +534,22 @@
 is base I/O port address of the parallel port, which must be a multiple of
 four. Make sure to not forget the "0x" prefix for hexadecimal port addresses.
 .sp
-More information about the hardware is available at
-http://rayer.ic.cz/elektro/spipgm.htm
+The default cable type is the RayeR cable. You can use the optional
+.B type
+parameter to specify the cable type with the
+.sp
+.B "  flashrom \-p rayer_spi:type=model"
+.sp
+syntax where
+.B model
+can be
+.BR rayer " for the RayeR cable or " xilinx " for the Xilinx Parallel Cable III
+(DLC 5).
+.sp
+More information about the RayeR hardware is available at
+.BR "http://rayer.ic.cz/elektro/spipgm.htm " .
+The schematic of the Xilinx DLC 5 was published at
+.BR "http://www.xilinx.com/itp/xilinx4/data/docs/pac/appendixb.html " .
 .TP
 .BR "ogp_spi " programmer
 The flash ROM chip to access must be specified with the 
@@ -469,7 +577,7 @@
 section above.
 .sp
 More information about the hardware is available at
-http://wiki.opengraphics.org
+.BR http://wiki.opengraphics.org .
 .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.
@@ -480,9 +588,6 @@
 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
@@ -498,6 +603,10 @@
 .B satasii
 needs PCI configuration space read access and raw memory access.
 .sp
+.B satamv
+needs PCI configuration space read access, raw I/O port access and raw memory
+access.
+.sp
 .B serprog
 needs TCP access to the network or userspace access to a serial port.
 .sp
@@ -510,8 +619,8 @@
 .B dummy
 needs no access permissions at all.
 .sp
-.BR internal ", " it87spi ", " nic3com ", " nicrealtek ", " nicsmc1211 ", "
-.BR nicnatsemi ", " "gfxnvidia" ", " drkaiser ", " satasii " and " atahpt
+.BR internal ", " nic3com ", " nicrealtek ", " nicsmc1211 ", " nicnatsemi ", "
+.BR gfxnvidia ", " drkaiser ", " satasii ", " satamv " and " atahpt
 have to be run as superuser/root, and need additional raw access permission.
 .sp
 .BR serprog ", " buspirate_spi ", " dediprog " and " ft2232_spi
@@ -522,8 +631,10 @@
 needs PCI configuration space read access and raw memory access.
 .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.
+.B "securelevel=-1"
+in
+.B "/etc/rc.securelevel"
+and rebooting, or rebooting into single user mode.
 .SH BUGS
 Please report any bugs at
 .sp
@@ -541,7 +652,11 @@
 backup. This is caused by the embedded controller (EC) present in many laptops,
 which interacts badly with any flash attempts. This is a hardware limitation
 and flashrom will attempt to detect it and abort immediately for safety reasons.
-.SH LICENCE
+.sp
+More information about flashrom on laptops is available from
+.sp
+.B "  http://www.flashrom.org/Laptops"
+.SH LICENSE
 .B flashrom
 is covered by the GNU General Public License (GPL), version 2. Some files are
 additionally available under the GPL (version 2, or any later version).
@@ -623,6 +738,6 @@
 .br
 All authors can be reached via email at <flashrom@flashrom.org>.
 .PP
-This manual page was written by Uwe Hermann <uwe@hermann-uwe.de>
-and Carl-Daniel Hailfinger.
+This manual page was written by Uwe Hermann <uwe@hermann-uwe.de>,
+Carl-Daniel Hailfinger and others.
 It is licensed under the terms of the GNU GPL (version 2 or later).
diff --git a/flashrom.c b/flashrom.c
index cc1a068..74c6714 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -42,70 +42,25 @@
 char *chip_to_probe = NULL;
 int verbose = 0;
 
-#if CONFIG_INTERNAL == 1
-enum programmer programmer = PROGRAMMER_INTERNAL;
-#elif CONFIG_DUMMY == 1
-enum programmer programmer = PROGRAMMER_DUMMY;
-#else
-/* If neither internal nor dummy are selected, we must pick a sensible default.
- * Since there is no reason to prefer a particular external programmer, we fail
- * if more than one of them is selected. If only one is selected, it is clear
- * that the user wants that one to become the default.
- */
-#if 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+CONFIG_NICINTEL_SPI+CONFIG_OGP_SPI+CONFIG_SATAMV > 1
-#error Please enable either CONFIG_DUMMY or CONFIG_INTERNAL or disable support for all programmers except one.
-#endif
-enum programmer programmer =
-#if CONFIG_NIC3COM == 1
-	PROGRAMMER_NIC3COM
-#endif
-#if CONFIG_NICREALTEK == 1
-	PROGRAMMER_NICREALTEK
-#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_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 == 1
-	PROGRAMMER_NICINTEL
-#endif
-#if CONFIG_NICINTEL_SPI == 1
-	PROGRAMMER_NICINTEL_SPI
-#endif
-#if CONFIG_OGP_SPI == 1
-	PROGRAMMER_OGP_SPI
-#endif
-#if CONFIG_SATAMV == 1
-	PROGRAMMER_SATAMV
-#endif
-;
-#endif
+/* error handling stuff */
+enum error_action access_denied_action = error_ignore;
+
+int ignore_error(int err) {
+	int rc = 0;
+
+	switch(err) {
+	case ACCESS_DENIED:
+		if (access_denied_action == error_ignore)
+			rc = 1;
+		break;
+	default:
+		break;
+	}
+
+	return rc;
+}
+
+static enum programmer programmer = PROGRAMMER_INVALID;
 
 static char *programmer_param = NULL;
 
@@ -131,14 +86,6 @@
 		.init			= internal_init,
 		.map_flash_region	= physmap,
 		.unmap_flash_region	= physunmap,
-		.chip_readb		= internal_chip_readb,
-		.chip_readw		= internal_chip_readw,
-		.chip_readl		= internal_chip_readl,
-		.chip_readn		= internal_chip_readn,
-		.chip_writeb		= internal_chip_writeb,
-		.chip_writew		= internal_chip_writew,
-		.chip_writel		= internal_chip_writel,
-		.chip_writen		= fallback_chip_writen,
 		.delay			= internal_delay,
 	},
 #endif
@@ -149,14 +96,6 @@
 		.init			= dummy_init,
 		.map_flash_region	= dummy_map,
 		.unmap_flash_region	= dummy_unmap,
-		.chip_readb		= dummy_chip_readb,
-		.chip_readw		= dummy_chip_readw,
-		.chip_readl		= dummy_chip_readl,
-		.chip_readn		= dummy_chip_readn,
-		.chip_writeb		= dummy_chip_writeb,
-		.chip_writew		= dummy_chip_writew,
-		.chip_writel		= dummy_chip_writel,
-		.chip_writen		= dummy_chip_writen,
 		.delay			= internal_delay,
 	},
 #endif
@@ -167,14 +106,6 @@
 		.init			= nic3com_init,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
-		.chip_readb		= nic3com_chip_readb,
-		.chip_readw		= fallback_chip_readw,
-		.chip_readl		= fallback_chip_readl,
-		.chip_readn		= fallback_chip_readn,
-		.chip_writeb		= nic3com_chip_writeb,
-		.chip_writew		= fallback_chip_writew,
-		.chip_writel		= fallback_chip_writel,
-		.chip_writen		= fallback_chip_writen,
 		.delay			= internal_delay,
 	},
 #endif
@@ -182,38 +113,22 @@
 #if CONFIG_NICREALTEK == 1
 	{
 		/* This programmer works for Realtek RTL8139 and SMC 1211. */
-		.name                   = "nicrealtek",
-		//.name                   = "nicsmc1211",
-		.init                   = nicrealtek_init,
-		.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			= "nicrealtek",
+		//.name			= "nicsmc1211",
+		.init			= nicrealtek_init,
+		.map_flash_region	= fallback_map,
+		.unmap_flash_region	= fallback_unmap,
+		.delay			= internal_delay,
 	},
 #endif
 
 #if CONFIG_NICNATSEMI == 1
 	{
-		.name                   = "nicnatsemi",
-		.init                   = nicnatsemi_init,
-		.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,
+		.name			= "nicnatsemi",
+		.init			= nicnatsemi_init,
+		.map_flash_region	= fallback_map,
+		.unmap_flash_region	= fallback_unmap,
+		.delay			= internal_delay,
 	},
 #endif
 
@@ -223,14 +138,6 @@
 		.init			= gfxnvidia_init,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
-		.chip_readb		= gfxnvidia_chip_readb,
-		.chip_readw		= fallback_chip_readw,
-		.chip_readl		= fallback_chip_readl,
-		.chip_readn		= fallback_chip_readn,
-		.chip_writeb		= gfxnvidia_chip_writeb,
-		.chip_writew		= fallback_chip_writew,
-		.chip_writel		= fallback_chip_writel,
-		.chip_writen		= fallback_chip_writen,
 		.delay			= internal_delay,
 	},
 #endif
@@ -241,14 +148,6 @@
 		.init			= drkaiser_init,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
-		.chip_readb		= drkaiser_chip_readb,
-		.chip_readw		= fallback_chip_readw,
-		.chip_readl		= fallback_chip_readl,
-		.chip_readn		= fallback_chip_readn,
-		.chip_writeb		= drkaiser_chip_writeb,
-		.chip_writew		= fallback_chip_writew,
-		.chip_writel		= fallback_chip_writel,
-		.chip_writen		= fallback_chip_writen,
 		.delay			= internal_delay,
 	},
 #endif
@@ -259,14 +158,6 @@
 		.init			= satasii_init,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
-		.chip_readb		= satasii_chip_readb,
-		.chip_readw		= fallback_chip_readw,
-		.chip_readl		= fallback_chip_readl,
-		.chip_readn		= fallback_chip_readn,
-		.chip_writeb		= satasii_chip_writeb,
-		.chip_writew		= fallback_chip_writew,
-		.chip_writel		= fallback_chip_writel,
-		.chip_writen		= fallback_chip_writen,
 		.delay			= internal_delay,
 	},
 #endif
@@ -277,14 +168,6 @@
 		.init			= atahpt_init,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
-		.chip_readb		= atahpt_chip_readb,
-		.chip_readw		= fallback_chip_readw,
-		.chip_readl		= fallback_chip_readl,
-		.chip_readn		= fallback_chip_readn,
-		.chip_writeb		= atahpt_chip_writeb,
-		.chip_writew		= fallback_chip_writew,
-		.chip_writel		= fallback_chip_writel,
-		.chip_writen		= fallback_chip_writen,
 		.delay			= internal_delay,
 	},
 #endif
@@ -295,14 +178,6 @@
 		.init			= ft2232_spi_init,
 		.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
@@ -313,14 +188,6 @@
 		.init			= serprog_init,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
-		.chip_readb		= serprog_chip_readb,
-		.chip_readw		= fallback_chip_readw,
-		.chip_readl		= fallback_chip_readl,
-		.chip_readn		= serprog_chip_readn,
-		.chip_writeb		= serprog_chip_writeb,
-		.chip_writew		= fallback_chip_writew,
-		.chip_writel		= fallback_chip_writel,
-		.chip_writen		= fallback_chip_writen,
 		.delay			= serprog_delay,
 	},
 #endif
@@ -331,14 +198,6 @@
 		.init			= buspirate_spi_init,
 		.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
@@ -349,14 +208,6 @@
 		.init			= dediprog_init,
 		.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
@@ -367,14 +218,6 @@
 		.init			= rayer_spi_init,
 		.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
@@ -385,51 +228,27 @@
 		.init			= nicintel_init,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
-		.chip_readb		= nicintel_chip_readb,
-		.chip_readw		= fallback_chip_readw,
-		.chip_readl		= fallback_chip_readl,
-		.chip_readn		= fallback_chip_readn,
-		.chip_writeb		= nicintel_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,
-		.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,
+		.name			= "nicintel_spi",
+		.init			= nicintel_spi_init,
+		.map_flash_region	= fallback_map,
+		.unmap_flash_region	= fallback_unmap,
+		.delay			= internal_delay,
 	},
 #endif
 
 #if CONFIG_OGP_SPI == 1
 	{
-		.name = "ogp_spi",
-		.init = ogp_spi_init,
-		.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,
+		.name			= "ogp_spi",
+		.init			= ogp_spi_init,
+		.map_flash_region	= fallback_map,
+		.unmap_flash_region	= fallback_unmap,
+		.delay			= internal_delay,
 	},
 #endif
 
@@ -439,14 +258,16 @@
 		.init			= satamv_init,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
-		.chip_readb		= satamv_chip_readb,
-		.chip_readw		= fallback_chip_readw,
-		.chip_readl		= fallback_chip_readl,
-		.chip_readn		= fallback_chip_readn,
-		.chip_writeb		= satamv_chip_writeb,
-		.chip_writew		= fallback_chip_writew,
-		.chip_writel		= fallback_chip_writel,
-		.chip_writen		= fallback_chip_writen,
+		.delay			= internal_delay,
+	},
+#endif
+
+#if CONFIG_LINUX_SPI == 1
+	{
+		.name			= "linux_spi",
+		.init			= linux_spi_init,
+		.map_flash_region	= fallback_map,
+		.unmap_flash_region	= fallback_unmap,
 		.delay			= internal_delay,
 	},
 #endif
@@ -520,18 +341,24 @@
 	return 0;
 }
 
-int programmer_init(char *param)
+int programmer_init(enum programmer prog, char *param)
 {
 	int ret;
+
+	if (prog >= PROGRAMMER_INVALID) {
+		msg_perr("Invalid programmer specified!\n");
+		return -1;
+	}
+	programmer = prog;
 	/* 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
+		.spi		= 0xffffffff,
 	};
-	buses_supported = CHIP_BUSTYPE_NONE;
+	buses_supported = BUS_NONE;
 	/* Default to top aligned flash at 4 GB. */
 	flashbase = 0;
 	/* Registering shutdown functions is now allowed. */
@@ -591,42 +418,42 @@
 
 void chip_writeb(uint8_t val, chipaddr addr)
 {
-	programmer_table[programmer].chip_writeb(val, addr);
+	par_programmer->chip_writeb(val, addr);
 }
 
 void chip_writew(uint16_t val, chipaddr addr)
 {
-	programmer_table[programmer].chip_writew(val, addr);
+	par_programmer->chip_writew(val, addr);
 }
 
 void chip_writel(uint32_t val, chipaddr addr)
 {
-	programmer_table[programmer].chip_writel(val, addr);
+	par_programmer->chip_writel(val, addr);
 }
 
 void chip_writen(uint8_t *buf, chipaddr addr, size_t len)
 {
-	programmer_table[programmer].chip_writen(buf, addr, len);
+	par_programmer->chip_writen(buf, addr, len);
 }
 
 uint8_t chip_readb(const chipaddr addr)
 {
-	return programmer_table[programmer].chip_readb(addr);
+	return par_programmer->chip_readb(addr);
 }
 
 uint16_t chip_readw(const chipaddr addr)
 {
-	return programmer_table[programmer].chip_readw(addr);
+	return par_programmer->chip_readw(addr);
 }
 
 uint32_t chip_readl(const chipaddr addr)
 {
-	return programmer_table[programmer].chip_readl(addr);
+	return par_programmer->chip_readl(addr);
 }
 
 void chip_readn(uint8_t *buf, chipaddr addr, size_t len)
 {
-	programmer_table[programmer].chip_readn(buf, addr, len);
+	par_programmer->chip_readn(buf, addr, len);
 }
 
 void programmer_delay(int usecs)
@@ -642,10 +469,10 @@
 	flash->virtual_registers = (chipaddr)programmer_map_flash_region("flash chip registers", (0xFFFFFFFF - 0x400000 - size + 1), size);
 }
 
-int read_memmapped(struct flashchip *flash, uint8_t *buf, int start, int len)
+int read_memmapped(struct flashchip *flash, uint8_t *buf, unsigned int start, int unsigned len)
 {
 	chip_readn(buf, flash->virtual_memory + start, len);
-		
+
 	return 0;
 }
 
@@ -724,7 +551,7 @@
 		param_pos++;
 		param_pos = strstr(param_pos, needle);
 	} while (1);
-	
+
 	if (param_pos) {
 		/* Get the string after needle and '='. */
 		opt_pos = param_pos + needlelen + 1;
@@ -765,7 +592,7 @@
 }
 
 /* start is an offset to the base address of the flash chip */
-int check_erased_range(struct flashchip *flash, int start, int len)
+int check_erased_range(struct flashchip *flash, unsigned int start, unsigned int len)
 {
 	int ret;
 	uint8_t *cmpbuf = malloc(len);
@@ -782,17 +609,19 @@
 
 /*
  * @cmpbuf	buffer to compare against, cmpbuf[0] is expected to match the
-		flash content at location start
+ *		flash content at location start
  * @start	offset to the base address of the flash chip
  * @len		length of the verified area
  * @message	string to print in the "FAILED" message
  * @return	0 for success, -1 for failure
  */
-int verify_range(struct flashchip *flash, uint8_t *cmpbuf, int start, int len, const char *message)
+int verify_range(struct flashchip *flash, uint8_t *cmpbuf, unsigned int start, unsigned int len,
+		 const char *message)
 {
-	int i, ret = 0;
+	unsigned int i;
 	uint8_t *readbuf = malloc(len);
-	int failcount = 0;
+	int ret = 0, failcount = 0;
+	unsigned int chunksize;
 
 	if (!len)
 		goto out_free;
@@ -815,27 +644,39 @@
 	}
 	if (!message)
 		message = "VERIFY";
-	
-	ret = flash->read(flash, readbuf, start, len);
-	if (ret) {
-		msg_gerr("Verification impossible because read failed "
-			 "at 0x%x (len 0x%x)\n", start, len);
-		return ret;
+
+	chunksize = min(flash->page_size, len);
+	for (i = 0; i < len; i += chunksize) {
+		int tmp, j;
+
+		tmp = flash->read(flash, readbuf + i, start + i, chunksize);
+
+		if (tmp) {
+			ret = tmp;
+			if (ignore_error(tmp)) {
+				chunksize = min(flash->page_size, len - i);
+				continue;
+			} else {
+				goto out_free;
+			}
+		}
+
+		for (j = 0; j < chunksize; j++) {
+			if (cmpbuf[i + j] != readbuf[i + j]) {
+				/* Only print the first failure. */
+				if (!failcount++)
+					msg_cerr("%s FAILED at 0x%08x! "
+						 "Expected=0x%02x, Read=0x%02x,",
+						 message, start + i + j, cmpbuf[i + j],
+						 readbuf[j]);
+			}
+		}
+		chunksize = min(flash->page_size, len - i);
 	}
 
-	for (i = 0; i < len; i++) {
-		if (cmpbuf[i] != readbuf[i]) {
-			/* Only print the first failure. */
-			if (!failcount++)
-				msg_cerr("%s FAILED at 0x%08x! "
-					 "Expected=0x%02x, Read=0x%02x,",
-					 message, start + i, cmpbuf[i],
-					 readbuf[i]);
-		}
-	}
 	if (failcount) {
 		msg_cerr(" failed byte count from 0x%08x-0x%08x: 0x%x\n",
-			start, start + len - 1, failcount);
+			 start, start + len - 1, failcount);
 		ret = -1;
 	}
 
@@ -868,10 +709,10 @@
  * @gran	write granularity (enum, not count)
  * @return      0 if no erase is needed, 1 otherwise
  */
-int need_erase(uint8_t *have, uint8_t *want, int len, enum write_granularity gran)
+int need_erase(uint8_t *have, uint8_t *want, unsigned int len, enum write_granularity gran)
 {
 	int result = 0;
-	int i, j, limit;
+	unsigned int i, j, limit;
 
 	switch (gran) {
 	case write_gran_1bit:
@@ -934,11 +775,13 @@
  * in relation to the max write length of the programmer and the max write
  * length of the chip.
  */
-static int get_next_write(uint8_t *have, uint8_t *want, int len,
-			  int *first_start, enum write_granularity gran)
+static unsigned int get_next_write(uint8_t *have, uint8_t *want, unsigned int len,
+			  unsigned int *first_start,
+			  enum write_granularity gran)
 {
-	int need_write = 0, rel_start = 0, first_len = 0;
-	int i, limit, stride;
+	int need_write = 0;
+	unsigned int rel_start = 0, first_len = 0;
+	unsigned int i, limit, stride;
 
 	switch (gran) {
 	case write_gran_1bit:
@@ -1119,38 +962,38 @@
 int check_max_decode(enum chipbustype buses, uint32_t size)
 {
 	int limitexceeded = 0;
-	if ((buses & CHIP_BUSTYPE_PARALLEL) &&
-	    (max_rom_decode.parallel < size)) {
+
+	if ((buses & BUS_PARALLEL) && (max_rom_decode.parallel < size)) {
 		limitexceeded++;
 		msg_pdbg("Chip size %u kB is bigger than supported "
-			     "size %u kB of chipset/board/programmer "
-			     "for %s interface, "
-			     "probe/read/erase/write may fail. ", size / 1024,
-			     max_rom_decode.parallel / 1024, "Parallel");
+			 "size %u kB of chipset/board/programmer "
+			 "for %s interface, "
+			 "probe/read/erase/write may fail. ", size / 1024,
+			 max_rom_decode.parallel / 1024, "Parallel");
 	}
-	if ((buses & CHIP_BUSTYPE_LPC) && (max_rom_decode.lpc < size)) {
+	if ((buses & BUS_LPC) && (max_rom_decode.lpc < size)) {
 		limitexceeded++;
 		msg_pdbg("Chip size %u kB is bigger than supported "
-			     "size %u kB of chipset/board/programmer "
-			     "for %s interface, "
-			     "probe/read/erase/write may fail. ", size / 1024,
-			     max_rom_decode.lpc / 1024, "LPC");
+			 "size %u kB of chipset/board/programmer "
+			 "for %s interface, "
+			 "probe/read/erase/write may fail. ", size / 1024,
+			 max_rom_decode.lpc / 1024, "LPC");
 	}
-	if ((buses & CHIP_BUSTYPE_FWH) && (max_rom_decode.fwh < size)) {
+	if ((buses & BUS_FWH) && (max_rom_decode.fwh < size)) {
 		limitexceeded++;
 		msg_pdbg("Chip size %u kB is bigger than supported "
-			     "size %u kB of chipset/board/programmer "
-			     "for %s interface, "
-			     "probe/read/erase/write may fail. ", size / 1024,
-			     max_rom_decode.fwh / 1024, "FWH");
+			 "size %u kB of chipset/board/programmer "
+			 "for %s interface, "
+			 "probe/read/erase/write may fail. ", size / 1024,
+			 max_rom_decode.fwh / 1024, "FWH");
 	}
-	if ((buses & CHIP_BUSTYPE_SPI) && (max_rom_decode.spi < size)) {
+	if ((buses & BUS_SPI) && (max_rom_decode.spi < size)) {
 		limitexceeded++;
 		msg_pdbg("Chip size %u kB is bigger than supported "
-			     "size %u kB of chipset/board/programmer "
-			     "for %s interface, "
-			     "probe/read/erase/write may fail. ", size / 1024,
-			     max_rom_decode.spi / 1024, "SPI");
+			 "size %u kB of chipset/board/programmer "
+			 "for %s interface, "
+			 "probe/read/erase/write may fail. ", size / 1024,
+			 max_rom_decode.spi / 1024, "SPI");
 	}
 	if (!limitexceeded)
 		return 0;
@@ -1160,8 +1003,8 @@
 	if (bitcount(buses) > limitexceeded)
 		/* FIXME: This message is designed towards CLI users. */
 		msg_pdbg("There is at least one common chip/programmer "
-			     "interface which can support a chip of this size. "
-			     "You can try --force at your own risk.\n");
+			 "interface which can support a chip of this size. "
+			 "You can try --force at your own risk.\n");
 	return 1;
 }
 
@@ -1240,9 +1083,9 @@
 		snprintf(location, sizeof(location), "on %s", programmer_table[programmer].name);
 
 	tmp = flashbuses_to_text(flash->bustype);
-	msg_cdbg("%s chip \"%s %s\" (%d kB, %s) %s.\n",
-		  force ? "Assuming" : "Found", fill_flash->vendor,
-		  fill_flash->name, fill_flash->total_size, tmp, location);
+	msg_cdbg("%s %s flash chip \"%s\" (%d kB, %s) %s.\n",
+		 force ? "Assuming" : "Found", fill_flash->vendor,
+		 fill_flash->name, fill_flash->total_size, tmp, location);
 	free(tmp);
 
 	/* Flash registers will not be mapped if the chip was forced. Lock info
@@ -1259,7 +1102,7 @@
 int verify_flash(struct flashchip *flash, uint8_t *buf, int verify_it)
 {
 	int ret;
-	int total_size = flash->total_size * 1024;
+	unsigned int total_size = flash->total_size * 1024;
 
 	msg_cinfo("Verifying flash... ");
 
@@ -1269,13 +1112,24 @@
 		ret = verify_range(flash, buf, 0, total_size, NULL);
 	}
 
+	if (ret == ACCESS_DENIED) {
+		msg_gdbg("Could not fully verify due to access error, ");
+		if (access_denied_action == error_ignore) {
+			msg_gdbg("ignoring\n");
+			ret = 0;
+		} else {
+			msg_gdbg("aborting\n");
+		}
+	}
+
 	if (!ret)
 		msg_cinfo("VERIFIED.          \n");
 
 	return ret;
 }
 
-int read_buf_from_file(unsigned char *buf, unsigned long size, const char *filename)
+int read_buf_from_file(unsigned char *buf, unsigned long size,
+		       const char *filename)
 {
 	unsigned long numbytes;
 	FILE *image;
@@ -1308,7 +1162,8 @@
 	return 0;
 }
 
-int write_buf_to_file(unsigned char *buf, unsigned long size, const char *filename)
+int write_buf_to_file(unsigned char *buf, unsigned long size,
+		      const char *filename)
 {
 	unsigned long numbytes;
 	FILE *image;
@@ -1364,10 +1219,18 @@
 		goto out_free;
 	} else if (ret > 0) {
 		/* Partial read has been handled, pass the whole flash read. */
-	} else if (flash->read(flash, buf, 0, size)) {
-		msg_cerr("Read operation failed!\n");
-		ret = 1;
-		goto out_free;
+	} else {
+		int tmp = flash->read(flash, buf, 0, size);
+
+		if (!tmp) {
+			ret = tmp;
+		} else if (ignore_error(tmp)) {
+			ret = 0;
+		} else {
+			msg_cerr("Read operation failed!\n");
+			ret = 1;
+			goto out_free;
+		}
 	}
 
 	ret = write_buf_to_file(buf, size, filename);
@@ -1418,7 +1281,7 @@
 		/* Empty eraseblock definition with erase function.  */
 		if (!done && eraser.block_erase)
 			msg_gspew("Strange: Empty eraseblock definition with "
-				"non-empty erase function. Not an error.\n");
+				  "non-empty erase function. Not an error.\n");
 		if (!done)
 			continue;
 		if (done != flash->total_size * 1024) {
@@ -1457,11 +1320,8 @@
 							unsigned int addr,
 							unsigned int len))
 {
-	int starthere = 0;
-	int lenhere = 0;
-	int ret = 0;
-	int skip = 1;
-	int writecount = 0;
+	unsigned int starthere = 0, lenhere = 0;
+	int ret = 0, skip = 1, writecount = 0;
 	enum write_granularity gran = write_gran_256bytes; /* FIXME */
 
 	/* curcontents and newcontents are opaque to walk_eraseregions, and
@@ -1474,8 +1334,14 @@
 	if (need_erase(curcontents, newcontents, len, gran)) {
 		msg_cdbg("E");
 		ret = erasefn(flash, start, len);
-		if (ret)
+		if (ret) {
+			if (ret == ACCESS_DENIED)
+				msg_cdbg("D");
+			else
+				msg_cerr("ERASE FAILED!\n");
 			return ret;
+		}
+
 		if (check_erased_range(flash, start, len)) {
 			msg_cerr("ERASE FAILED!\n");
 			return -1;
@@ -1493,8 +1359,11 @@
 		/* Needs the partial write function signature. */
 		ret = flash->write(flash, newcontents + starthere,
 				   start + starthere, lenhere);
-		if (ret)
+		if (ret) {
+			if (ret == ACCESS_DENIED)
+				msg_cdbg("D");
 			return ret;
+		}
 		starthere += lenhere;
 		skip = 0;
 	}
@@ -1515,10 +1384,11 @@
 							unsigned int len)),
 			     void *param1, void *param2)
 {
-	int i, j;
+	int i, j, rc = 0;
 	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.
@@ -1530,16 +1400,19 @@
 				msg_cdbg(", ");
 			msg_cdbg("0x%06x-0x%06x", start,
 				     start + len - 1);
-			if (do_something(flash, start, len, param1, param2,
-					 eraser.block_erase)) {
-				msg_cdbg("\n");
-				return 1;
+			rc = do_something(flash, start, len, param1, param2,
+			                  eraser.block_erase);
+			if (rc) {
+				if (ignore_error(rc))
+					rc = 0;
+				else
+					return rc;
 			}
 			start += len;
 		}
 	}
 	msg_cdbg("\n");
-	return 0;
+	return rc;
 }
 
 static int check_block_eraser(const struct flashchip *flash, int k, int log)
@@ -1566,47 +1439,58 @@
 	return 0;
 }
 
-int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents)
+int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents,
+			  uint8_t *newcontents)
 {
-	int k, ret = 0;
+	int k, ret = 1;
 	uint8_t *curcontents;
 	unsigned long size = flash->total_size * 1024;
 	unsigned int usable_erasefunctions = count_usable_erasers(flash);
 
 	msg_cinfo("Erasing and writing flash chip... ");
-	curcontents = (uint8_t *) malloc(size);
+	curcontents = malloc(size);
+	if (!curcontents) {
+		msg_gerr("Out of memory!\n");
+		exit(1);
+	}
 	/* Copy oldcontents to curcontents to avoid clobbering oldcontents. */
 	memcpy(curcontents, oldcontents, size);
 
 	for (k = 0; k < NUM_ERASEFUNCTIONS; k++) {
-		msg_cdbg("Looking at blockwise erase function %i... ", k);
-		if (check_block_eraser(flash, k, 1) && usable_erasefunctions) {
+		if (k != 0)
 			msg_cdbg("Looking for another erase function.\n");
-			continue;
+		if (!usable_erasefunctions) {
+			msg_cdbg("No usable erase functions left.\n");
+			break;
 		}
+		msg_cdbg("Trying erase function %i... ", k);
+		if (check_block_eraser(flash, k, 1))
+			continue;
 		usable_erasefunctions--;
-		msg_cdbg("trying... ");
-		ret = walk_eraseregions(flash, k, &erase_and_write_block_helper, curcontents, newcontents);
-		msg_cdbg("\n");
+		ret = walk_eraseregions(flash, k, &erase_and_write_block_helper,
+					curcontents, newcontents);
 		/* If everything is OK, don't try another erase function. */
 		if (!ret)
 			break;
 		/* Write/erase failed, so try to find out what the current chip
-		 * contents are. If no usable erase functions remain, we could
-		 * abort the loop instead of continuing, the effect is the same.
-		 * The only difference is whether the reason for other unusable
-		 * functions is printed or not. If in doubt, verbosity wins.
+		 * contents are. If no usable erase functions remain, we can
+		 * skip this: the next iteration will break immediately anyway.
 		 */
 		if (!usable_erasefunctions)
 			continue;
+		/* Reading the whole chip may take a while, inform the user even
+		 * in non-verbose mode.
+		 */
+		msg_cinfo("Reading current flash chip contents... ");
 		if (flash->read(flash, curcontents, 0, size)) {
 			/* Now we are truly screwed. Read failed as well. */
-			msg_cerr("Can't read anymore!\n");
+			msg_cerr("Can't read anymore! Aborting.\n");
 			/* We have no idea about the flash chip contents, so
 			 * retrying with another erase function is pointless.
 			 */
 			break;
 		}
+		msg_cdbg("done. ");
 	}
 	/* Free the scratchpad. */
 	free(curcontents);
@@ -1643,7 +1527,7 @@
 		"DO NOT REBOOT OR POWEROFF!\n");
 }
 
-/* The way to go if you want a delimited list of programmers*/
+/* The way to go if you want a delimited list of programmers */
 void list_programmers(const char *delim)
 {
 	enum programmer p;
@@ -1659,8 +1543,7 @@
 {
 	const char *pname;
 	int pnamelen;
-	int remaining = 0;
-	int firstline = 1;
+	int remaining = 0, firstline = 1;
 	enum programmer p;
 	int i;
 
@@ -1751,7 +1634,7 @@
 void print_banner(void)
 {
 	msg_ginfo("flashrom is free software, get the source code at "
-		    "http://www.flashrom.org\n");
+		  "http://www.flashrom.org\n");
 	msg_ginfo("\n");
 }
 
@@ -1767,8 +1650,8 @@
 		msg_gerr("Programmer table miscompilation!\n");
 		ret = 1;
 	}
-	/* It would be favorable if we could also check for correct terminaion
-	 * of the follwing arrays, but we don't know their size in here...
+	/* It would be favorable if we could also check for correct termination
+	 * of the following arrays, but we don't know their sizes in here...
 	 * For 'flashchips' we check the first element to be non-null. In the
 	 * other cases there exist use cases where the first element can be
 	 * null. */
@@ -1785,7 +1668,7 @@
 		msg_gerr("Chipset enables table does not exist!\n");
 		ret = 1;
 	}
-	if (board_pciid_enables == NULL) {
+	if (board_matches == NULL) {
 		msg_gerr("Board enables table does not exist!\n");
 		ret = 1;
 	}
@@ -1797,7 +1680,7 @@
 		msg_gerr("Known laptops table does not exist!\n");
 		ret = 1;
 	}
-#endif // CONFIG_INTERNAL == 1
+#endif
 	return ret;
 }
 
@@ -1850,14 +1733,6 @@
 	}
 }
 
-int main(int argc, char *argv[])
-{
-	/* FIXME: this should eventually be a build option controlled
-	   via a USE flag */
-//	return cli_classic(argc, argv);
-	return cli_mfg(argc, argv);
-}
-
 /* FIXME: This function signature needs to be improved once doit() has a better
  * function signature.
  */
@@ -1922,7 +1797,7 @@
  * but right now it allows us to split off the CLI code.
  * Besides that, the function itself is a textbook example of abysmal code flow.
  */
-int doit(struct flashchip *flash, int force, const char *filename, int read_it, int write_it, int erase_it, int verify_it)
+int doit(struct flashchip *flash, int force, const char *filename, int read_it, int write_it, int erase_it, int verify_it, const char *diff_file)
 {
 	uint8_t *oldcontents;
 	uint8_t *newcontents;
@@ -1959,10 +1834,18 @@
 		goto out_nofree;
 	}
 
-	oldcontents = (uint8_t *) malloc(size);
+	oldcontents = malloc(size);
+	if (!oldcontents) {
+		msg_gerr("Out of memory!\n");
+		exit(1);
+	}
 	/* Assume worst case: All bits are 0. */
 	memset(oldcontents, 0x00, size);
-	newcontents = (uint8_t *) malloc(size);
+	newcontents = malloc(size);
+	if (!newcontents) {
+		msg_gerr("Out of memory!\n");
+		exit(1);
+	}
 	/* Assume best case: All bits should be 1. */
 	memset(newcontents, 0xff, size);
 	/* Side effect of the assumptions above: Default write action is erase
@@ -1997,17 +1880,33 @@
 #endif
 	}
 
-	/* Read the whole chip to be able to check whether regions need to be
-	 * erased and to give better diagnostics in case write fails.
+	/* Obtain a reference image so that we can check whether regions need
+	 * to be erased and to give better diagnostics in case write fails.
 	 * The alternative would be to read only the regions which are to be
 	 * preserved, but in that case we might perform unneeded erase which
 	 * takes time as well.
 	 */
-	msg_cdbg("Reading old flash chip contents... ");
-	if (flash->read(flash, oldcontents, 0, size)) {
-		ret = 1;
-		msg_cdbg("FAILED.\n");
-		goto out;
+	if (diff_file) {
+		msg_cdbg("Reading old contents from file... ");
+		if (read_buf_from_file(oldcontents, size, diff_file)) {
+			ret = 1;
+			msg_cdbg("FAILED.\n");
+			goto out;
+		}
+	} else {
+		int tmp;
+
+		msg_cdbg("Reading old contents from flash chip... ");
+		tmp = flash->read(flash, oldcontents, 0, size);
+		if (tmp) {
+			if (ignore_error(tmp)) {
+				msg_gdbg("ignoring access error.\n");
+			} else {
+				ret = 1;
+				msg_cdbg("FAILED.\n");
+				goto out;
+			}
+		}
 	}
 	msg_cdbg("done.\n");
 
diff --git a/fmap.c b/fmap.c
index 806a278..1a113e3 100644
--- a/fmap.c
+++ b/fmap.c
@@ -143,14 +143,20 @@
 		for (offset = flash->total_size * 1024 - stride;
 		     offset > 0;
 		     offset -= stride) {
+			int tmp;
+
 			if (offset % (stride * 2) == 0)
 					continue;
-
-			if (flash->read(flash, (uint8_t *)&tmp64,
-			                offset, sizeof(tmp64))) {
-				msg_gdbg("failed to read flash at "
-				         "offset 0x%lx\n", offset);
-				return -1;
+			tmp = flash->read(flash, (uint8_t *)&tmp64,
+			                  offset, sizeof(tmp64));
+			if (tmp) {
+				if (ignore_error(tmp)) {
+					continue;
+				} else {
+					msg_gdbg("failed to read flash at "
+					         "offset 0x%lx\n", offset);
+					return -1;
+				}
 			}
 
 			if (!memcmp(&tmp64, &sig, sizeof(sig))) {
@@ -166,10 +172,15 @@
 	 * with before going upstream.
 	 */
 	if (!fmap_found) {
+		int tmp;
 		uint8_t *image = malloc(flash->total_size * 1024);
 
 		msg_gdbg("using brute force method to find fmap\n");
-		flash->read(flash, image, 0, flash->total_size * 1024);
+		tmp = flash->read(flash, image, 0, flash->total_size * 1024);
+		if (!ignore_error(tmp)) {
+			msg_gdbg("failed to read flash\n");
+			return -1;
+		}
 		for (offset = flash->total_size * 1024 - sizeof(sig);
 		     offset > 0;
 		     offset--) {
diff --git a/ft2232_spi.c b/ft2232_spi.c
index 0480930..d3ac173 100644
--- a/ft2232_spi.c
+++ b/ft2232_spi.c
@@ -25,16 +25,21 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include "flash.h"
-#include "chipdrivers.h"
 #include "programmer.h"
 #include "spi.h"
 #include <ftdi.h>
 
+/* Please keep sorted by vendor ID, then device ID. */
+
 #define FTDI_VID		0x0403
 #define FTDI_FT2232H_PID	0x6010
 #define FTDI_FT4232H_PID	0x6011
+#define TIAO_TUMPA_PID		0x8a98
 #define AMONTEC_JTAGKEY_PID	0xCFF8
 
+#define GOEPEL_VID		0x096C
+#define GOEPEL_PICOTAP_PID	0x1449
+
 #define FIC_VID			0x1457
 #define OPENMOKO_DBGBOARD_PID	0x5118
 
@@ -50,7 +55,9 @@
 const struct usbdev_status devs_ft2232spi[] = {
 	{FTDI_VID, FTDI_FT2232H_PID, OK, "FTDI", "FT2232H"},
 	{FTDI_VID, FTDI_FT4232H_PID, OK, "FTDI", "FT4232H"},
+	{FTDI_VID, TIAO_TUMPA_PID, OK, "TIAO", "USB Multi-Protocol Adapter"},
 	{FTDI_VID, AMONTEC_JTAGKEY_PID, OK, "Amontec", "JTAGkey"},
+	{GOEPEL_VID, GOEPEL_PICOTAP_PID, OK, "GOEPEL", "PicoTAP"},
 	{FIC_VID, OPENMOKO_DBGBOARD_PID, OK, "FIC",
 		"OpenMoko Neo1973 Debug board (V2+)"},
 	{OLIMEX_VID, OLIMEX_ARM_OCD_PID, NT, "Olimex", "ARM-USB-OCD"},
@@ -145,18 +152,19 @@
 		const unsigned char *writearr, unsigned char *readarr);
 
 static const struct spi_programmer spi_programmer_ft2232 = {
-	.type = SPI_CONTROLLER_FT2232,
-	.max_data_read = 64 * 1024,
-	.max_data_write = 256,
-	.command = ft2232_spi_send_command,
-	.multicommand = default_spi_send_multicommand,
-	.read = default_spi_read,
-	.write_256 = default_spi_write_256,
+	.type		= SPI_CONTROLLER_FT2232,
+	.max_data_read	= 64 * 1024,
+	.max_data_write	= 256,
+	.command	= ft2232_spi_send_command,
+	.multicommand	= default_spi_send_multicommand,
+	.read		= default_spi_read,
+	.write_256	= default_spi_write_256,
 };
 
+/* Returns 0 upon success, a negative number upon errors. */
 int ft2232_spi_init(void)
 {
-	int f;
+	int f, ret = 0;
 	struct ftdi_context *ftdic = &ftdic_context;
 	unsigned char buf[512];
 	int ft2232_vid = FTDI_VID;
@@ -176,6 +184,20 @@
 			ft2232_interface = INTERFACE_A;
 			cs_bits = 0x18;
 			pindir = 0x1b;
+		} else if (!strcasecmp(arg, "picotap")) {
+			ft2232_vid = GOEPEL_VID;
+			ft2232_type = GOEPEL_PICOTAP_PID;
+			ft2232_interface = INTERFACE_A;
+		} else if (!strcasecmp(arg, "tumpa")) {
+			/* Interface A is SPI1, B is SPI2. */
+			ft2232_type = TIAO_TUMPA_PID;
+			ft2232_interface = INTERFACE_A;
+		} else if (!strcasecmp(arg, "busblaster")) {
+			/* In its default configuration it is a jtagkey clone */
+			ft2232_type = FTDI_FT2232H_PID;
+			ft2232_interface = INTERFACE_A;
+			cs_bits = 0x18;
+			pindir = 0x1b;
 		} else if (!strcasecmp(arg, "openmoko")) {
 			ft2232_vid = FIC_VID;
 			ft2232_type = OPENMOKO_DBGBOARD_PID;
@@ -203,7 +225,7 @@
 		} else {
 			msg_perr("Error: Invalid device type specified.\n");
 			free(arg);
-			return 1;
+			return -1;
 		}
 	}
 	free(arg);
@@ -219,7 +241,7 @@
 		default:
 			msg_perr("Error: Invalid port/interface specified.\n");
 			free(arg);
-			return 1;
+			return -2;
 		}
 	}
 	free(arg);
@@ -231,7 +253,7 @@
 
 	if (ftdi_init(ftdic) < 0) {
 		msg_perr("ftdi_init failed\n");
-		return EXIT_FAILURE; // TODO
+		return -3;
 	}
 
 	f = ftdi_usb_open(ftdic, ft2232_vid, ft2232_type);
@@ -239,7 +261,7 @@
 	if (f < 0 && f != -5) {
 		msg_perr("Unable to open FTDI device: %d (%s)\n", f,
 				ftdi_get_error_string(ftdic));
-		exit(-1); // TODO
+		return -4;
 	}
 
 	if (ftdic->type != TYPE_2232H && ftdic->type != TYPE_4232H) {
@@ -272,8 +294,10 @@
 	if (clock_5x) {
 		msg_pdbg("Disable divide-by-5 front stage\n");
 		buf[0] = 0x8a;		/* Disable divide-by-5. */
-		if (send_buf(ftdic, buf, 1))
-			return -1;
+		if (send_buf(ftdic, buf, 1)) {
+			ret = -5;
+			goto ftdi_err;
+		}
 		mpsse_clk = 60.0;
 	} else {
 		mpsse_clk = 12.0;
@@ -284,34 +308,48 @@
 	/* valueL/valueH are (desired_divisor - 1) */
 	buf[1] = (DIVIDE_BY - 1) & 0xff;
 	buf[2] = ((DIVIDE_BY - 1) >> 8) & 0xff;
-	if (send_buf(ftdic, buf, 3))
-		return -1;
+	if (send_buf(ftdic, buf, 3)) {
+		ret = -6;
+		goto ftdi_err;
+	}
 
 	msg_pdbg("MPSSE clock: %f MHz divisor: %d "
-		 "SPI clock: %f MHz\n",
-		 mpsse_clk, DIVIDE_BY,
+		 "SPI clock: %f MHz\n", mpsse_clk, DIVIDE_BY,
 		 (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");
 	buf[0] = 0x85;
-	if (send_buf(ftdic, buf, 1))
-		return -1;
+	if (send_buf(ftdic, buf, 1)) {
+		ret = -7;
+		goto ftdi_err;
+	}
 
 	msg_pdbg("Set data bits\n");
 	buf[0] = SET_BITS_LOW;
 	buf[1] = cs_bits;
 	buf[2] = pindir;
-	if (send_buf(ftdic, buf, 3))
-		return -1;
+	if (send_buf(ftdic, buf, 3)) {
+		ret = -8;
+		goto ftdi_err;
+	}
 
 	// msg_pdbg("\nft2232 chosen\n");
 
 	register_spi_programmer(&spi_programmer_ft2232);
 
 	return 0;
+
+ftdi_err:
+	if ((f = ftdi_usb_close(ftdic)) < 0) {
+		msg_perr("Unable to close FTDI device: %d (%s)\n", f,
+		         ftdi_get_error_string(ftdic));
+	}
+
+	return ret;
 }
 
+/* Returns 0 upon success, a negative number upon errors. */
 static int ft2232_spi_send_command(unsigned int writecnt, unsigned int readcnt,
 		const unsigned char *writearr, unsigned char *readarr)
 {
@@ -332,7 +370,8 @@
 		buf = realloc(buf, bufsize);
 		if (!buf) {
 			msg_perr("Out of memory!\n");
-			exit(1);
+			/* TODO: What to do with buf? */
+			return SPI_GENERIC_ERROR;
 		}
 		oldbufsize = bufsize;
 	}
@@ -368,8 +407,7 @@
 		failed = ret;
 		/* We can't abort here, we still have to deassert CS#. */
 		if (ret)
-			msg_perr("send_buf failed before read: %i\n",
-				ret);
+			msg_perr("send_buf failed before read: %i\n", ret);
 		i = 0;
 		if (ret == 0) {
 			/*
diff --git a/gfxnvidia.c b/gfxnvidia.c
index 3f67c72..2601e9a 100644
--- a/gfxnvidia.c
+++ b/gfxnvidia.c
@@ -61,6 +61,17 @@
 	{},
 };
 
+static const struct par_programmer par_programmer_gfxnvidia = {
+		.chip_readb		= gfxnvidia_chip_readb,
+		.chip_readw		= fallback_chip_readw,
+		.chip_readl		= fallback_chip_readl,
+		.chip_readn		= fallback_chip_readn,
+		.chip_writeb		= gfxnvidia_chip_writeb,
+		.chip_writew		= fallback_chip_writew,
+		.chip_writel		= fallback_chip_writel,
+		.chip_writen		= fallback_chip_writen,
+};
+
 static int gfxnvidia_shutdown(void *data)
 {
 	physunmap(nvidia_bar, GFXNVIDIA_MEMMAP_SIZE);
@@ -85,7 +96,7 @@
 
 	nvidia_bar = physmap("NVIDIA", io_base_addr, GFXNVIDIA_MEMMAP_SIZE);
 
-	/* must be done before rpci calls */
+	/* Must be done before rpci calls. */
 	if (register_shutdown(gfxnvidia_shutdown, NULL))
 		return 1;
 
@@ -94,10 +105,9 @@
 	reg32 &= ~(1 << 0);
 	rpci_write_long(pcidev_dev, 0x50, reg32);
 
-	buses_supported = CHIP_BUSTYPE_PARALLEL;
-
 	/* Write/erase doesn't work. */
 	programmer_may_write = 0;
+	register_par_programmer(&par_programmer_gfxnvidia, BUS_PARALLEL);
 
 	return 0;
 }
diff --git a/hwaccess.c b/hwaccess.c
index 73b836b..51e113e 100644
--- a/hwaccess.c
+++ b/hwaccess.c
@@ -240,6 +240,10 @@
 {									\
 	struct undo_mmio_write_data *undo_mmio_write_data;		\
 	undo_mmio_write_data = malloc(sizeof(struct undo_mmio_write_data)); \
+	if (!undo_mmio_write_data) {					\
+		msg_gerr("Out of memory!\n");				\
+		exit(1);						\
+	}								\
 	undo_mmio_write_data->addr = a;					\
 	undo_mmio_write_data->type = mmio_write_type_##c;		\
 	undo_mmio_write_data->c##data = mmio_read##c(a);		\
diff --git a/hwaccess.h b/hwaccess.h
index 1bafb11..0299ecb 100644
--- a/hwaccess.h
+++ b/hwaccess.h
@@ -220,7 +220,7 @@
   #define INW  inportw
   #define INL  inportl
 
-#else 
+#else
 
   #define OUTB outb
   #define OUTW outw
diff --git a/ich_descriptors.c b/ich_descriptors.c
index b41eee4..125077c 100644
--- a/ich_descriptors.c
+++ b/ich_descriptors.c
@@ -19,476 +19,298 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
-#include "ich_descriptors.h"
-
 #if defined(__i386__) || defined(__x86_64__)
 
-#ifdef ICH_DESCRIPTORS_FROM_MMAP_DUMP
-
-#define DESCRIPTOR_MODE_MAGIC 0x0ff0a55a
-#define msg_pdbg printf
-#define msg_perr printf
-#include <stdio.h>
-
-struct flash_strap fisba;
-struct flash_upper_map flumap;
-
-#else
-
+#include "ich_descriptors.h"
 #include "flash.h" /* for msg_* */
+#include "programmer.h"
 
-#endif // ICH_DESCRIPTORS_FROM_MMAP_DUMP
-
-struct flash_descriptor fdbar = { 0 };
-struct flash_component fcba = { {0} };
-struct flash_region frba = { {0} };
-struct flash_master fmba = { {0} };
-
-uint32_t getFCBA(void)
+void prettyprint_ich_reg_vscc(uint32_t reg_val, int verbosity)
 {
-	return (fdbar.FLMAP0 <<  4) & 0x00000ff0;
+	print(verbosity, "BES=0x%x, ",	(reg_val & VSCC_BES)  >> VSCC_BES_OFF);
+	print(verbosity, "WG=%d, ",	(reg_val & VSCC_WG)   >> VSCC_WG_OFF);
+	print(verbosity, "WSR=%d, ",	(reg_val & VSCC_WSR)  >> VSCC_WSR_OFF);
+	print(verbosity, "WEWS=%d, ",	(reg_val & VSCC_WEWS) >> VSCC_WEWS_OFF);
+	print(verbosity, "EO=0x%x, ",	(reg_val & VSCC_EO)   >> VSCC_EO_OFF);
+	print(verbosity, "VCL=%d\n",	(reg_val & VSCC_VCL)  >> VSCC_VCL_OFF);
 }
 
-uint32_t getFRBA(void)
+#define getFCBA(cont)	(((cont)->FLMAP0 <<  4) & 0x00000ff0)
+#define getFRBA(cont)	(((cont)->FLMAP0 >> 12) & 0x00000ff0)
+#define getFMBA(cont)	(((cont)->FLMAP1 <<  4) & 0x00000ff0)
+#define getFISBA(cont)	(((cont)->FLMAP1 >> 12) & 0x00000ff0)
+#define getFMSBA(cont)	(((cont)->FLMAP2 <<  4) & 0x00000ff0)
+
+void prettyprint_ich_descriptors(enum ich_chipset cs, const struct ich_descriptors *desc)
 {
-	return (fdbar.FLMAP0 >> 12) & 0x00000ff0;
+	prettyprint_ich_descriptor_content(&desc->content);
+	prettyprint_ich_descriptor_component(desc);
+	prettyprint_ich_descriptor_region(desc);
+	prettyprint_ich_descriptor_master(&desc->master);
 }
 
-uint32_t getFMBA(void)
+void prettyprint_ich_descriptor_content(const struct ich_desc_content *cont)
 {
-	return (fdbar.FLMAP1 <<  4) & 0x00000ff0;
+	msg_pdbg2("=== Content Section ===\n");
+	msg_pdbg2("FLVALSIG 0x%08x\n", cont->FLVALSIG);
+	msg_pdbg2("FLMAP0   0x%08x\n", cont->FLMAP0);
+	msg_pdbg2("FLMAP1   0x%08x\n", cont->FLMAP1);
+	msg_pdbg2("FLMAP2   0x%08x\n", cont->FLMAP2);
+	msg_pdbg2("\n");
+
+	msg_pdbg2("--- Details ---\n");
+	msg_pdbg2("NR          (Number of Regions):                 %5d\n",
+		  cont->NR + 1);
+	msg_pdbg2("FRBA        (Flash Region Base Address):         0x%03x\n",
+		  getFRBA(cont));
+	msg_pdbg2("NC          (Number of Components):              %5d\n",
+		  cont->NC + 1);
+	msg_pdbg2("FCBA        (Flash Component Base Address):      0x%03x\n",
+		  getFCBA(cont));
+	msg_pdbg2("ISL         (ICH/PCH Strap Length):              %5d\n",
+		  cont->ISL);
+	msg_pdbg2("FISBA/FPSBA (Flash ICH/PCH Strap Base Address):  0x%03x\n",
+		  getFISBA(cont));
+	msg_pdbg2("NM          (Number of Masters):                 %5d\n",
+		  cont->NM + 1);
+	msg_pdbg2("FMBA        (Flash Master Base Address):         0x%03x\n",
+		  getFMBA(cont));
+	msg_pdbg2("MSL/PSL     (MCH/PROC Strap Length):             %5d\n",
+		  cont->MSL);
+	msg_pdbg2("FMSBA       (Flash MCH/PROC Strap Base Address): 0x%03x\n",
+		  getFMSBA(cont));
+	msg_pdbg2("\n");
 }
 
-uint32_t getFMSBA(void)
+void prettyprint_ich_descriptor_component(const struct ich_descriptors *desc)
 {
-	return (fdbar.FLMAP2 <<  4) & 0x00000ff0;
+	static const char * const freq_str[8] = {
+		"20 MHz",	/* 000 */
+		"33 MHz",	/* 001 */
+		"reserved",	/* 010 */
+		"reserved",	/* 011 */
+		"50 MHz",	/* 100 */
+		"reserved",	/* 101 */
+		"reserved",	/* 110 */
+		"reserved"	/* 111 */
+	};
+	static const char * const size_str[8] = {
+		"512 kB",	/* 000 */
+		"  1 MB",	/* 001 */
+		"  2 MB",	/* 010 */
+		"  4 MB",	/* 011 */
+		"  8 MB",	/* 100 */
+		" 16 MB",	/* 101 */
+		"reserved",	/* 110 */
+		"reserved",	/* 111 */
+	};
+
+	msg_pdbg2("=== Component Section ===\n");
+	msg_pdbg2("FLCOMP   0x%08x\n", desc->component.FLCOMP);
+	msg_pdbg2("FLILL    0x%08x\n", desc->component.FLILL );
+	msg_pdbg2("\n");
+
+	msg_pdbg2("--- Details ---\n");
+	msg_pdbg2("Component 1 density:           %s\n",
+		  size_str[desc->component.comp1_density]);
+	if (desc->content.NC)
+		msg_pdbg2("Component 2 density:           %s\n",
+			  size_str[desc->component.comp2_density]);
+	else
+		msg_pdbg2("Component 2 is not used.\n");
+	msg_pdbg2("Read Clock Frequency:           %s\n",
+		  freq_str[desc->component.freq_read]);
+	msg_pdbg2("Read ID and Status Clock Freq.: %s\n",
+		  freq_str[desc->component.freq_read_id]);
+	msg_pdbg2("Write and Erase Clock Freq.:    %s\n",
+		  freq_str[desc->component.freq_write]);
+	msg_pdbg2("Fast Read is %ssupported.\n",
+		  desc->component.fastread ? "" : "not ");
+	if (desc->component.fastread)
+		msg_pdbg2("Fast Read Clock Frequency:      %s\n",
+			  freq_str[desc->component.freq_fastread]);
+	if (desc->component.FLILL == 0)
+		msg_pdbg2("No forbidden opcodes.\n");
+	else {
+		msg_pdbg2("Invalid instruction 0:          0x%02x\n",
+			  desc->component.invalid_instr0);
+		msg_pdbg2("Invalid instruction 1:          0x%02x\n",
+			  desc->component.invalid_instr1);
+		msg_pdbg2("Invalid instruction 2:          0x%02x\n",
+			  desc->component.invalid_instr2);
+		msg_pdbg2("Invalid instruction 3:          0x%02x\n",
+			  desc->component.invalid_instr3);
+	}
+	msg_pdbg2("\n");
 }
 
-uint32_t getFLREG_limit(uint32_t flreg)
+static void pprint_freg(const struct ich_desc_region *reg, uint32_t i)
 {
-	return (flreg >>  4) & 0x01fff000;
+	static const char *const region_names[5] = {
+		"Descr.", "BIOS", "ME", "GbE", "Platf."
+	};
+	if (i >= 5) {
+		msg_pdbg2("%s: region index too high.\n", __func__);
+		return;
+	}
+	uint32_t base = ICH_FREG_BASE(reg->FLREGs[i]);
+	uint32_t limit = ICH_FREG_LIMIT(reg->FLREGs[i]);
+	msg_pdbg2("Region %d (%-6s) ", i, region_names[i]);
+	if (base > limit)
+		msg_pdbg2("is unused.\n");
+	else
+		msg_pdbg2("0x%08x - 0x%08x\n", base, limit | 0x0fff);
 }
 
-uint32_t getFLREG_base(uint32_t flreg)
+void prettyprint_ich_descriptor_region(const struct ich_descriptors *desc)
 {
-	return (flreg << 12) & 0x01fff000;
+	uint8_t i;
+	uint8_t nr = desc->content.NR + 1;
+	msg_pdbg2("=== Region Section ===\n");
+	if (nr >= 5) {
+		msg_pdbg2("%s: number of regions too high (%d).\n", __func__,
+			 nr);
+		return;
+	}
+	for (i = 0; i <= nr; i++)
+		msg_pdbg2("FLREG%d   0x%08x\n", i, desc->region.FLREGs[i]);
+	msg_pdbg2("\n");
+
+	msg_pdbg2("--- Details ---\n");
+	for (i = 0; i <= nr; i++)
+		pprint_freg(&desc->region, i);
+	msg_pdbg2("\n");
 }
 
-uint32_t getFISBA(void)
+void prettyprint_ich_descriptor_master(const struct ich_desc_master *mstr)
 {
-	return (fdbar.FLMAP1 >> 12) & 0x00000ff0;
+	msg_pdbg2("=== Master Section ===\n");
+	msg_pdbg2("FLMSTR1  0x%08x\n", mstr->FLMSTR1);
+	msg_pdbg2("FLMSTR2  0x%08x\n", mstr->FLMSTR2);
+	msg_pdbg2("FLMSTR3  0x%08x\n", mstr->FLMSTR3);
+	msg_pdbg2("\n");
+
+	msg_pdbg2("--- Details ---\n");
+	msg_pdbg2("      Descr. BIOS ME GbE Platf.\n");
+	msg_pdbg2("BIOS    %c%c    %c%c  %c%c  %c%c   %c%c\n",
+	(mstr->BIOS_descr_r)	?'r':' ', (mstr->BIOS_descr_w)	?'w':' ',
+	(mstr->BIOS_BIOS_r)	?'r':' ', (mstr->BIOS_BIOS_w)	?'w':' ',
+	(mstr->BIOS_ME_r)	?'r':' ', (mstr->BIOS_ME_w)	?'w':' ',
+	(mstr->BIOS_GbE_r)	?'r':' ', (mstr->BIOS_GbE_w)	?'w':' ',
+	(mstr->BIOS_plat_r)	?'r':' ', (mstr->BIOS_plat_w)	?'w':' ');
+	msg_pdbg2("ME      %c%c    %c%c  %c%c  %c%c   %c%c\n",
+	(mstr->ME_descr_r)	?'r':' ', (mstr->ME_descr_w)	?'w':' ',
+	(mstr->ME_BIOS_r)	?'r':' ', (mstr->ME_BIOS_w)	?'w':' ',
+	(mstr->ME_ME_r)		?'r':' ', (mstr->ME_ME_w)	?'w':' ',
+	(mstr->ME_GbE_r)	?'r':' ', (mstr->ME_GbE_w)	?'w':' ',
+	(mstr->ME_plat_r)	?'r':' ', (mstr->ME_plat_w)	?'w':' ');
+	msg_pdbg2("GbE     %c%c    %c%c  %c%c  %c%c   %c%c\n",
+	(mstr->GbE_descr_r)	?'r':' ', (mstr->GbE_descr_w)	?'w':' ',
+	(mstr->GbE_BIOS_r)	?'r':' ', (mstr->GbE_BIOS_w)	?'w':' ',
+	(mstr->GbE_ME_r)	?'r':' ', (mstr->GbE_ME_w)	?'w':' ',
+	(mstr->GbE_GbE_r)	?'r':' ', (mstr->GbE_GbE_w)	?'w':' ',
+	(mstr->GbE_plat_r)	?'r':' ', (mstr->GbE_plat_w)	?'w':' ');
+	msg_pdbg2("\n");
 }
 
 /** Returns the integer representation of the component density with index
-comp in bytes or 0 if a correct size can not be determined. */
-int getFCBA_component_density(uint8_t comp)
+idx in bytes or 0 if a correct size can not be determined. */
+int getFCBA_component_density(const struct ich_descriptors *desc, uint8_t idx)
 {
 	uint8_t size_enc;
-	const int dec_mem[6] = {
-		      512 * 1024,
-		 1 * 1024 * 1024,
-		 2 * 1024 * 1024,
-		 4 * 1024 * 1024,
-		 8 * 1024 * 1024,
-		16 * 1024 * 1024,
-	};
-
-	switch(comp) {
+	
+	switch(idx) {
 	case 0:
-		size_enc = fcba.comp1_density;
+		size_enc = desc->component.comp1_density;
 		break;
 	case 1:
-		if (fdbar.NC == 0)
+		if (desc->content.NC == 0)
 			return 0;
-		size_enc = fcba.comp2_density;
+		size_enc = desc->component.comp2_density;
 		break;
 	default:
-		msg_perr("Only component index 0 or 1 are supported yet.\n");
+		msg_perr("Only ICH SPI component index 0 or 1 are supported "
+			 "yet.\n");
 		return 0;
 	}
 	if (size_enc > 5) {
-		msg_perr("Density of component with index %d illegal or "
-			 "unsupported. Encoded density is 0x%x.\n", comp,
-			 size_enc);
+		msg_perr("Density of ICH SPI component with index %d is "
+			 "invalid. Encoded density is 0x%x.\n", idx, size_enc);
 		return 0;
 	}
-	return dec_mem[size_enc];
+	return (1 << (19 + size_enc));
 }
 
-#ifdef ICH_DESCRIPTORS_FROM_MMAP_DUMP
-uint32_t getVTBA(void)
-{	/* The bits in FLUMAP1 describe bits 4-11 out of 24 in total;
-	 * others are 0. */
-	return (flumap.FLUMAP1 << 4) & 0x0ff0;
-}
-#endif
-
-const struct flash_descriptor_addresses desc_addr = {
-	.FCBA = getFCBA,
-	.FRBA = getFRBA,
-	.FMBA = getFMBA,
-	.FMSBA = getFMSBA,
-	.FISBA = getFISBA,
-	.FLREG_limit = getFLREG_limit,
-	.FLREG_base = getFLREG_base,
-#ifdef ICH_DESCRIPTORS_FROM_MMAP_DUMP
-	.VTBA = getVTBA,
-#endif
-};
-
-void prettyprint_ich_descriptors(enum chipset cs)
-{
-	prettyprint_ich_descriptor_map();
-	prettyprint_ich_descriptor_component();
-	prettyprint_ich_descriptor_region();
-	prettyprint_ich_descriptor_master();
-#ifdef ICH_DESCRIPTORS_FROM_MMAP_DUMP
-	if (cs >= CHIPSET_ICH8) {
-		prettyprint_ich_descriptor_upper_map();
-		prettyprint_ich_descriptor_straps(cs);
-	}
-#endif // ICH_DESCRIPTORS_FROM_MMAP_DUMP
-	msg_pdbg("\n");
-}
-
-void prettyprint_ich_descriptor_map(void)
-{
-	msg_pdbg("=== FDBAR ===\n");
-	msg_pdbg("FLVALSIG 0x%8.8x\n", fdbar.FLVALSIG);
-	msg_pdbg("FLMAP0   0x%8.8x\n", fdbar.FLMAP0  );
-	msg_pdbg("FLMAP1   0x%8.8x\n", fdbar.FLMAP1  );
-	msg_pdbg("FLMAP2   0x%8.8x\n", fdbar.FLMAP2  );
-	msg_pdbg("\n");
-	msg_pdbg("--- FDBAR details ---\n");
-	msg_pdbg("0x%2.2x        NR    Number of Regions\n", fdbar.NR   );
-	msg_pdbg("0x%8.8x  FRBA  Flash Region Base Address\n", desc_addr.FRBA() );
-	msg_pdbg("0x%2.2x        NC    Number of Components\n", fdbar.NC   );
-	msg_pdbg("0x%8.8x  FCBA  Flash Component Base Address\n", desc_addr.FCBA() );
-	msg_pdbg("\n");
-	msg_pdbg("0x%2.2x        ISL   ICH Strap Length\n", fdbar.ISL  );
-	msg_pdbg("0x%8.8x  FISBA Flash ICH Strap Base Address\n", desc_addr.FISBA());
-	msg_pdbg("0x%2.2x        NM    Number of Masters\n", fdbar.NM   );
-	msg_pdbg("0x%8.8x  FMBA  Flash Master Base Address\n", desc_addr.FMBA() );
-	msg_pdbg("\n");
-	msg_pdbg("0x%2.2x        MSL   MCH Strap Length\n", fdbar.MSL  );
-	msg_pdbg("0x%8.8x  FMSBA Flash MCH Strap Base Address\n", desc_addr.FMSBA());
-}
-
-void prettyprint_ich_descriptor_component(void)
-{
-	const char * const str_freq[8] = {
-		"20 MHz",		/* 000 */
-		"33 MHz",		/* 001 */
-		"reserved/illegal",	/* 010 */
-		"reserved/illegal",	/* 011 */
-		"50 MHz",		/* 100 */
-		"reserved/illegal",	/* 101 */
-		"reserved/illegal",	/* 110 */
-		"reserved/illegal"	/* 111 */
-	};
-	const char * const str_mem[8] = {
-		"512kB",
-		"1 MB",
-		"2 MB",
-		"4 MB",
-		"8 MB",
-		"16 MB",
-		"undocumented/illegal",
-		"reserved/illegal"
-	};
-
-	msg_pdbg("\n");
-	msg_pdbg("=== FCBA ===\n");
-	msg_pdbg("FLCOMP   0x%8.8x\n", fcba.FLCOMP);
-	msg_pdbg("FLILL    0x%8.8x\n", fcba.FLILL );
-	msg_pdbg("\n");
-	msg_pdbg("--- FCBA details ---\n");
-	msg_pdbg("0x%2.2x    freq_read_id   %s\n",
-		fcba.freq_read_id , str_freq[fcba.freq_read_id ]);
-	msg_pdbg("0x%2.2x    freq_write     %s\n",
-		fcba.freq_write   , str_freq[fcba.freq_write   ]);
-	msg_pdbg("0x%2.2x    freq_fastread  %s\n",
-		fcba.freq_fastread, str_freq[fcba.freq_fastread]);
-	msg_pdbg("0x%2.2x    fastread       %ssupported\n",
-		fcba.fastread, fcba.fastread ? "" : "not ");
-	msg_pdbg("0x%2.2x    freq_read      %s\n",
-		fcba.freq_read, str_freq[fcba.freq_read    ]);
-	msg_pdbg("0x%2.2x    comp 1 density %s\n",
-		fcba.comp1_density, str_mem[fcba.comp1_density]);
-	if (fdbar.NC)
-		msg_pdbg("0x%2.2x    comp 2 density %s\n",
-			fcba.comp2_density, str_mem[fcba.comp2_density]);
-	else
-		msg_pdbg("0x%2.2x    comp 2 is not used (FLMAP0.NC=0)\n",
-			fcba.comp2_density);
-	msg_pdbg("\n");
-	msg_pdbg("0x%2.2x    invalid instr 0\n", fcba.invalid_instr0);
-	msg_pdbg("0x%2.2x    invalid instr 1\n", fcba.invalid_instr1);
-	msg_pdbg("0x%2.2x    invalid instr 2\n", fcba.invalid_instr2);
-	msg_pdbg("0x%2.2x    invalid instr 3\n", fcba.invalid_instr3);
-}
-
-void prettyprint_ich_descriptor_region(void)
-{
-	msg_pdbg("\n");
-	msg_pdbg("=== FRBA ===\n");
-	msg_pdbg("FLREG0   0x%8.8x\n", frba.FLREG0);
-	msg_pdbg("FLREG1   0x%8.8x\n", frba.FLREG1);
-	msg_pdbg("FLREG2   0x%8.8x\n", frba.FLREG2);
-	msg_pdbg("FLREG3   0x%8.8x\n", frba.FLREG3);
-	msg_pdbg("\n");
-	msg_pdbg("--- FRBA details ---\n");
-	msg_pdbg("0x%8.8x  region 0 limit (descr)\n", desc_addr.FLREG_limit(frba.FLREG0));
-	msg_pdbg("0x%8.8x  region 0 base  (descr)\n", desc_addr.FLREG_base(frba.FLREG0));
-	msg_pdbg("0x%8.8x  region 1 limit ( BIOS)\n", desc_addr.FLREG_limit(frba.FLREG1));
-	msg_pdbg("0x%8.8x  region 1 base  ( BIOS)\n", desc_addr.FLREG_base(frba.FLREG1));
-	msg_pdbg("0x%8.8x  region 2 limit ( ME  )\n", desc_addr.FLREG_limit(frba.FLREG2));
-	msg_pdbg("0x%8.8x  region 2 base  ( ME  )\n", desc_addr.FLREG_base(frba.FLREG2));
-	msg_pdbg("0x%8.8x  region 3 limit ( GbE )\n", desc_addr.FLREG_limit(frba.FLREG3));
-	msg_pdbg("0x%8.8x  region 3 base  ( GbE )\n", desc_addr.FLREG_base(frba.FLREG3));
-}
-
-void prettyprint_ich_descriptor_master(void)
-{
-	msg_pdbg("\n");
-	msg_pdbg("=== FMBA ===\n");
-	msg_pdbg("FLMSTR1  0x%8.8x\n", fmba.FLMSTR1);
-	msg_pdbg("FLMSTR2  0x%8.8x\n", fmba.FLMSTR2);
-	msg_pdbg("FLMSTR3  0x%8.8x\n", fmba.FLMSTR3);
-
-	msg_pdbg("\n");
-	msg_pdbg("--- FMBA details ---\n");
-	msg_pdbg("BIOS can %s write GbE\n",   fmba.BIOS_GbE_write   ? "   " : "NOT");
-	msg_pdbg("BIOS can %s write ME\n",    fmba.BIOS_ME_write    ? "   " : "NOT");
-	msg_pdbg("BIOS can %s write BIOS\n",  fmba.BIOS_BIOS_write  ? "   " : "NOT");
-	msg_pdbg("BIOS can %s write descr\n", fmba.BIOS_descr_write ? "   " : "NOT");
-	msg_pdbg("BIOS can %s read  GbE\n",   fmba.BIOS_GbE_read    ? "   " : "NOT");
-	msg_pdbg("BIOS can %s read  ME\n",    fmba.BIOS_ME_read     ? "   " : "NOT");
-	msg_pdbg("BIOS can %s read  BIOS\n",  fmba.BIOS_BIOS_read   ? "   " : "NOT");
-	msg_pdbg("BIOS can %s read  descr\n", fmba.BIOS_descr_read  ? "   " : "NOT");
-	msg_pdbg("ME   can %s write GbE\n",   fmba.ME_GbE_write     ? "   " : "NOT");
-	msg_pdbg("ME   can %s write ME\n",    fmba.ME_ME_write      ? "   " : "NOT");
-	msg_pdbg("ME   can %s write BIOS\n",  fmba.ME_BIOS_write    ? "   " : "NOT");
-	msg_pdbg("ME   can %s write descr\n", fmba.ME_descr_write   ? "   " : "NOT");
-	msg_pdbg("ME   can %s read  GbE\n",   fmba.ME_GbE_read      ? "   " : "NOT");
-	msg_pdbg("ME   can %s read  ME\n",    fmba.ME_ME_read       ? "   " : "NOT");
-	msg_pdbg("ME   can %s read  BIOS\n",  fmba.ME_BIOS_read     ? "   " : "NOT");
-	msg_pdbg("ME   can %s read  descr\n", fmba.ME_descr_read    ? "   " : "NOT");
-	msg_pdbg("GbE  can %s write GbE\n",   fmba.GbE_GbE_write    ? "   " : "NOT");
-	msg_pdbg("GbE  can %s write ME\n",    fmba.GbE_ME_write     ? "   " : "NOT");
-	msg_pdbg("GbE  can %s write BIOS\n",  fmba.GbE_BIOS_write   ? "   " : "NOT");
-	msg_pdbg("GbE  can %s write descr\n", fmba.GbE_descr_write  ? "   " : "NOT");
-	msg_pdbg("GbE  can %s read  GbE\n",   fmba.GbE_GbE_read     ? "   " : "NOT");
-	msg_pdbg("GbE  can %s read  ME\n",    fmba.GbE_ME_read      ? "   " : "NOT");
-	msg_pdbg("GbE  can %s read  BIOS\n",  fmba.GbE_BIOS_read    ? "   " : "NOT");
-	msg_pdbg("GbE  can %s read  descr\n", fmba.GbE_descr_read   ? "   " : "NOT");
-}
-
-void prettyprint_ich9_reg_vscc(uint32_t reg_val)
-{
-	pprint_reg_hex(VSCC, BES, reg_val, ", ");
-	pprint_reg(VSCC, WG, reg_val, ", ");
-	pprint_reg(VSCC, WSR, reg_val, ", ");
-	pprint_reg(VSCC, WEWS, reg_val, ", ");
-	pprint_reg_hex(VSCC, EO, reg_val, ", ");
-	pprint_reg(VSCC, VCL, reg_val, "\n");
-}
-
-
-#ifdef ICH_DESCRIPTORS_FROM_MMAP_DUMP
-void prettyprint_ich_descriptor_straps_ich8(void)
-{
-	const char * const str_GPIO12[4] = {
-		"GPIO12",
-		"LAN PHY Power Control Function (Native Output)",
-		"GLAN_DOCK# (Native Input)",
-		"invalid configuration",
-	};
-
-	msg_pdbg("\n");
-	msg_pdbg("=== FISBA ===\n");
-	msg_pdbg("STRP0    0x%8.8x\n", fisba.ich8.STRP0);
-
-	msg_pdbg("\n");
-	msg_pdbg("--- FISBA details ---\n");
-	msg_pdbg("ME SMBus addr2 0x%2.2x\n", fisba.ich8.ASD2);
-	msg_pdbg("ME SMBus addr1 0x%2.2x\n", fisba.ich8.ASD);
-	msg_pdbg("ME SMBus Controller is connected to %s\n", fisba.ich8.MESM2SEL ? "SMLink pins" : "SMBus pins");
-	msg_pdbg("SPI CS1 is used for %s\n", fisba.ich8.SPICS1_LANPHYPC_SEL ? "LAN PHY Power Control Function" : "SPI Chip Select");
-	msg_pdbg("GPIO12_SEL is used as %s\n", str_GPIO12[fisba.ich8.GPIO12_SEL]);
-	msg_pdbg("PCIe Port 6 is used for %s\n", fisba.ich8.GLAN_PCIE_SEL ? "integrated GLAN" : "PCI Express");
-	msg_pdbg("Intel AMT SMBus Controller 1 is connected to %s\n",   fisba.ich8.BMCMODE ? "SMLink" : "SMBus");
-	msg_pdbg("TCO slave is on %s. Intel AMT SMBus Controller 1 is %sabled\n",
-		fisba.ich8.TCOMODE ? "SMBus" : "SMLink", fisba.ich8.TCOMODE ? "en" : "dis");
-	msg_pdbg("ME A is %sabled\n", fisba.ich8.ME_DISABLE ? "dis" : "en");
-
-	msg_pdbg("\n");
-	msg_pdbg("=== FMSBA ===\n");
-	msg_pdbg("STRP1    0x%8.8x\n", fisba.ich8.STRP1);
-
-	msg_pdbg("\n");
-	msg_pdbg("--- FMSBA details ---\n");
-	msg_pdbg("ME B is %sabled\n", fisba.ich8.ME_disable_B ? "dis" : "en");
-}
-
-void prettyprint_ich_descriptor_straps_ibex(void)
-{
-	int i;
-	msg_pdbg("\n");
-	msg_pdbg("=== FPSBA ===\n");
-	for(i = 0; i <= 15; i++)
-		msg_pdbg("STRP%-2d = 0x%8.8x\n", i, fisba.ibex.STRPs[i]);
-}
-
-void prettyprint_ich_descriptor_straps(enum chipset cs)
-{
-	switch (cs) {
-	case CHIPSET_ICH8:
-		prettyprint_ich_descriptor_straps_ich8();
-		break;
-	case CHIPSET_SERIES_5_IBEX_PEAK:
-		prettyprint_ich_descriptor_straps_ibex();
-		break;
-	case CHIPSET_UNKNOWN:
-		break;
-	default:
-		msg_pdbg("\n");
-		msg_pdbg("The meaning of the descriptor straps are unknown yet.\n");
-		break;
-	}
-}
-
-void prettyprint_rdid(uint32_t reg_val)
-{
-	uint8_t mid = reg_val & 0xFF;
-	uint16_t did = ((reg_val >> 16) & 0xFF) | (reg_val & 0xFF00);
-	msg_pdbg("Manufacturer ID 0x%02x, Device ID 0x%04x\n", mid, did);
-}
-
-void prettyprint_ich_descriptor_upper_map(void)
-{
-	int i;
-	msg_pdbg("\n");
-	msg_pdbg("=== FLUMAP ===\n");
-	msg_pdbg("FLUMAP1  0x%8.8x\n", flumap.FLUMAP1);
-
-	msg_pdbg("\n");
-	msg_pdbg("--- FLUMAP details ---\n");
-	msg_pdbg("VTL  (length)       = %d\n", flumap.VTL);
-	msg_pdbg("VTBA (base address) = 0x%6.6x\n", desc_addr.VTBA());
-	msg_pdbg("\n");
-
-	for (i=0; i < flumap.VTL/2; i++)
-	{
-		uint32_t jid = flumap.vscc_table[i].JID;
-		uint32_t vscc = flumap.vscc_table[i].VSCC;
-		msg_pdbg("  JID%d  = 0x%8.8x\n", i, jid);
-		msg_pdbg("  VSCC%d = 0x%8.8x\n", i, vscc);
-		msg_pdbg("    "); /* indention */
-		prettyprint_rdid(jid);
-		msg_pdbg("    "); /* indention */
-		prettyprint_ich9_reg_vscc(vscc);
-	}
-}
-
-int read_ich_descriptors_from_dump(uint32_t *dump, enum chipset cs)
-{
-	int i;
-	uint8_t pch_bug_offset = 0;
-	if (dump[0] != DESCRIPTOR_MODE_MAGIC) {
-		if (dump[4] == DESCRIPTOR_MODE_MAGIC)
-			pch_bug_offset = 4;
-		else
-			return -1;
-	}
-
-	/* map */
-	fdbar.FLVALSIG	= dump[0 + pch_bug_offset];
-	fdbar.FLMAP0	= dump[1 + pch_bug_offset];
-	fdbar.FLMAP1	= dump[2 + pch_bug_offset];
-	fdbar.FLMAP2	= dump[3 + pch_bug_offset];
-
-	/* component */
-	fcba.FLCOMP	= dump[(desc_addr.FCBA() >> 2) + 0];
-	fcba.FLILL	= dump[(desc_addr.FCBA() >> 2) + 1];
-	fcba.FLPB	= dump[(desc_addr.FCBA() >> 2) + 2];
-
-	/* region */
-	frba.FLREG0 = dump[(desc_addr.FRBA() >> 2) + 0];
-	frba.FLREG1 = dump[(desc_addr.FRBA() >> 2) + 1];
-	frba.FLREG2 = dump[(desc_addr.FRBA() >> 2) + 2];
-	frba.FLREG3 = dump[(desc_addr.FRBA() >> 2) + 3];
-
-	/* master */
-	fmba.FLMSTR1 = dump[(desc_addr.FMBA() >> 2) + 0];
-	fmba.FLMSTR2 = dump[(desc_addr.FMBA() >> 2) + 1];
-	fmba.FLMSTR3 = dump[(desc_addr.FMBA() >> 2) + 2];
-
-	/* upper map */
-	flumap.FLUMAP1 = dump[(0x0efc >> 2) + 0];
-
-	for (i=0; i < flumap.VTL; i++)
-	{
-		flumap.vscc_table[i].JID  = dump[(desc_addr.VTBA() >> 2) + i * 2 + 0];
-		flumap.vscc_table[i].VSCC = dump[(desc_addr.VTBA() >> 2) + i * 2 + 1];
-	}
-	/* straps */
-	/* FIXME: detect chipset correctly */
-	switch (cs) {
-		case CHIPSET_ICH8:
-			fisba.ich8.STRP0 = dump[(desc_addr.FISBA() >> 2) + 0];
-			fisba.ich8.STRP1 = dump[(desc_addr.FMSBA() >> 2) + 0];
-			break;
-		case CHIPSET_SERIES_5_IBEX_PEAK:
-			for(i = 0; i <= 15; i++)
-				fisba.ibex.STRPs[i] = dump[(desc_addr.FISBA() >> 2) + i];
-			break;
-		default:
-			break;
-	}
-
-	return 0;
-}
-#else // ICH_DESCRIPTORS_FROM_MMAP_DUMP
-
 static uint32_t read_descriptor_reg(uint8_t section, uint16_t offset, void *spibar)
 {
 	uint32_t control = 0;
 	control |= (section << FDOC_FDSS_OFF) & FDOC_FDSS;
 	control |= (offset << FDOC_FDSI_OFF) & FDOC_FDSI;
-	*(volatile uint32_t *) (spibar + ICH9_REG_FDOC) = control;
-	return *(volatile uint32_t *)(spibar + ICH9_REG_FDOD);
+	mmio_le_writel(control, spibar + ICH9_REG_FDOC);
+	return mmio_le_readl(spibar + ICH9_REG_FDOD);
 }
 
-void read_ich_descriptors_from_fdo(void *spibar)
+int read_ich_descriptors_via_fdo(void *spibar, struct ich_descriptors *desc)
 {
-	msg_pdbg("Reading flash descriptors "
+	uint8_t i;
+	uint8_t nr;
+	struct ich_desc_region *r = &desc->region;
+
+	/* Test if bit-fields are working as expected.
+	 * FIXME: Replace this with dynamic bitfield fixup
+	 */
+	for (i = 0; i < 4; i++)
+		desc->region.FLREGs[i] = 0x5A << (i * 8);
+	if (r->reg0_base != 0x005A || r->reg0_limit != 0x0000 ||
+	    r->reg1_base != 0x1A00 || r->reg1_limit != 0x0000 ||
+	    r->reg2_base != 0x0000 || r->reg2_limit != 0x005A ||
+	    r->reg3_base != 0x0000 || r->reg3_limit != 0x1A00) {
+		msg_pdbg("The combination of compiler and CPU architecture used"
+			 "does not lay out bit-fields as expected, sorry.\n");
+		msg_pspew("r->reg0_base  = 0x%04X (0x005A)\n", r->reg0_base);
+		msg_pspew("r->reg0_limit = 0x%04X (0x0000)\n", r->reg0_limit);
+		msg_pspew("r->reg1_base  = 0x%04X (0x1A00)\n", r->reg1_base);
+		msg_pspew("r->reg1_limit = 0x%04X (0x0000)\n", r->reg1_limit);
+		msg_pspew("r->reg2_base  = 0x%04X (0x0000)\n", r->reg2_base);
+		msg_pspew("r->reg2_limit = 0x%04X (0x005A)\n", r->reg2_limit);
+		msg_pspew("r->reg3_base  = 0x%04X (0x0000)\n", r->reg3_base);
+		msg_pspew("r->reg3_limit = 0x%04X (0x1A00)\n", r->reg3_limit);
+		return ICH_RET_ERR;
+	}
+
+	msg_pdbg2("Reading flash descriptors "
 		 "mapped by the chipset via FDOC/FDOD...");
-	/* descriptor map section */
-	fdbar.FLVALSIG	= read_descriptor_reg(0, 0, spibar);
-	fdbar.FLMAP0	= read_descriptor_reg(0, 1, spibar);
-	fdbar.FLMAP1	= read_descriptor_reg(0, 2, spibar);
-	fdbar.FLMAP2	= read_descriptor_reg(0, 3, spibar);
+	/* content section */
+	desc->content.FLVALSIG	= read_descriptor_reg(0, 0, spibar);
+	desc->content.FLMAP0	= read_descriptor_reg(0, 1, spibar);
+	desc->content.FLMAP1	= read_descriptor_reg(0, 2, spibar);
+	desc->content.FLMAP2	= read_descriptor_reg(0, 3, spibar);
 
 	/* component section */
-	fcba.FLCOMP	= read_descriptor_reg(1, 0, spibar);
-	fcba.FLILL	= read_descriptor_reg(1, 1, spibar);
-	fcba.FLPB	= read_descriptor_reg(1, 2, spibar);
+	desc->component.FLCOMP	= read_descriptor_reg(1, 0, spibar);
+	desc->component.FLILL	= read_descriptor_reg(1, 1, spibar);
+	desc->component.FLPB	= read_descriptor_reg(1, 2, spibar);
 
 	/* region section */
-	frba.FLREG0 = read_descriptor_reg(2, 0, spibar);
-	frba.FLREG1 = read_descriptor_reg(2, 1, spibar);
-	frba.FLREG2 = read_descriptor_reg(2, 2, spibar);
-	frba.FLREG3 = read_descriptor_reg(2, 3, spibar);
+	nr = desc->content.NR + 1;
+	if (nr >= 5) {
+		msg_pdbg2("%s: number of regions too high (%d) - failed\n",
+			  __func__, nr);
+		return ICH_RET_ERR;
+	}
+	for (i = 0; i <= nr; i++)
+		desc->region.FLREGs[i] = read_descriptor_reg(2, i, spibar);
 
 	/* master section */
-	fmba.FLMSTR1 = read_descriptor_reg(3, 0, spibar);
-	fmba.FLMSTR2 = read_descriptor_reg(3, 1, spibar);
-	fmba.FLMSTR3 = read_descriptor_reg(3, 2, spibar);
+	desc->master.FLMSTR1 = read_descriptor_reg(3, 0, spibar);
+	desc->master.FLMSTR2 = read_descriptor_reg(3, 1, spibar);
+	desc->master.FLMSTR3 = read_descriptor_reg(3, 2, spibar);
 
-	/* accessing strap section and upper map is impossible via FDOC/D(?) */
-	msg_pdbg(" done\n");
+	/* Accessing the strap section via FDOC/D is only possible on ICH8 and
+	 * reading the upper map is impossible on all chipsets, so don't bother.
+	 */
+
+	msg_pdbg2(" done.\n");
+	return ICH_RET_OK;
 }
-
-#endif // ICH_DESCRIPTORS_FROM_MMAP_DUMP
-#endif // defined(__i386__) || defined(__x86_64__)
+#endif /* defined(__i386__) || defined(__x86_64__) */
diff --git a/ich_descriptors.h b/ich_descriptors.h
index ca435f3..714eda9 100644
--- a/ich_descriptors.h
+++ b/ich_descriptors.h
@@ -19,14 +19,19 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
-#include <stdint.h>
-
 #if defined(__i386__) || defined(__x86_64__)
 #ifndef __ICH_DESCRIPTORS_H__
 #define __ICH_DESCRIPTORS_H__ 1
 
-/* should probably be in ichspi.h */
-#define msg_pdbg2 msg_pspew
+#include <stdint.h>
+#include "programmer.h" /* for enum ich_chipset */
+
+/* FIXME: Replace with generic return codes */
+#define ICH_RET_OK	0
+#define ICH_RET_ERR	-1
+#define ICH_RET_WARN	-2
+#define ICH_RET_PARAM	-3
+#define ICH_RET_OOB	-4
 
 #define ICH9_REG_FDOC		0xB0	/* 32 Bits Flash Descriptor Observability Control */
 					/* 0-1: reserved */
@@ -56,394 +61,188 @@
 #define VSCC_VCL			(0x1 << VSCC_VCL_OFF)
 					/* 24-31: reserved */
 
-#define pprint_reg(reg, bit, val, sep) msg_pdbg("%s=%d" sep, #bit, (val & reg##_##bit)>>reg##_##bit##_OFF)
-#define pprint_reg_hex(reg, bit, val, sep) msg_pdbg("%s=0x%x" sep, #bit, (val & reg##_##bit)>>reg##_##bit##_OFF)
-void prettyprint_ich9_reg_vscc(uint32_t reg_val);
+#define ICH_FREG_BASE(flreg)  (((flreg) << 12) & 0x01fff000)
+#define ICH_FREG_LIMIT(flreg) (((flreg) >>  4) & 0x01fff000)
 
-enum chipset {
-	CHIPSET_UNKNOWN,
-	CHIPSET_ICH7 = 7,
-	CHIPSET_ICH8,
-	CHIPSET_ICH9,
-	CHIPSET_ICH10,
-	CHIPSET_SERIES_5_IBEX_PEAK,
-	CHIPSET_SERIES_6_COUGAR_POINT,
-	CHIPSET_SERIES_7_PANTHER_POINT
-};
+void prettyprint_ich_reg_vscc(uint32_t reg_val, int verbosity);
 
-struct flash_descriptor_addresses {
-	uint32_t (*FCBA)(void);
-	uint32_t (*FRBA)(void);
-	uint32_t (*FMBA)(void);
-	uint32_t (*FMSBA)(void);
-	uint32_t (*FLREG_limit)(uint32_t flreg);
-	uint32_t (*FLREG_base)(uint32_t flreg);
-	uint32_t (*FISBA)(void);
-#ifdef ICH_DESCRIPTORS_FROM_MMAP_DUMP
-	uint32_t (*VTBA)(void);
-#endif // ICH_DESCRIPTORS_FROM_MMAP_DUMP
-};
-	
-struct flash_descriptor {
+struct ich_desc_content {
 	uint32_t FLVALSIG;	/* 0x00 */
 	union {			/* 0x04 */
 		uint32_t FLMAP0;
 		struct {
-			uint8_t FCBA	:8;
-			uint8_t NC	:2;
-			unsigned	:6;
-			uint8_t FRBA	:8;
-			uint8_t NR	:3;
-			unsigned	:5;
+			uint32_t FCBA	:8, /* Flash Component Base Address */
+				 NC	:2, /* Number Of Components */
+					:6,
+				 FRBA	:8, /* Flash Region Base Address */
+				 NR	:3, /* Number Of Regions */
+					:5;
 		};
 	};
 	union {			/* 0x08 */
 		uint32_t FLMAP1;
 		struct {
-			uint8_t FMBA	:8;
-			uint8_t NM	:3;
-			unsigned	:5;
-			union {
-				uint8_t FISBA	:8;
-				uint8_t FPSBA	:8;
-			};
-			uint8_t ISL	:8;
+			uint32_t FMBA	:8, /* Flash Master Base Address */
+				 NM	:3, /* Number Of Masters */
+					:5,
+				 FISBA	:8, /* Flash ICH Strap Base Address */
+				 ISL	:8; /* ICH Strap Length */
 		};
 	};
 	union {			/* 0x0c */
 		uint32_t FLMAP2;
 		struct {
-			uint8_t  FMSBA	:8;
-			uint8_t  MSL	:8;
-			unsigned	:16;
+			uint32_t FMSBA	:8, /* Flash (G)MCH Strap Base Addr. */
+				 MSL	:8, /* MCH Strap Length */
+					:16;
 		};
 	};
 };
 
-struct flash_component {
+struct ich_desc_component {
 	union {			/* 0x00 */
-		uint32_t FLCOMP;
+		uint32_t FLCOMP; /* Flash Components Register */
 		struct {
-			uint8_t  comp1_density	:3;
-			uint8_t  comp2_density	:3;
-			unsigned		:11;
-			uint8_t  freq_read	:3;
-			uint8_t  fastread	:1;
-			uint8_t  freq_fastread	:3;
-			uint8_t  freq_write	:3;
-			uint8_t  freq_read_id	:3;
-			unsigned		:2;
+			uint32_t comp1_density	:3,
+				 comp2_density	:3,
+						:11,
+				 freq_read	:3,
+				 fastread	:1,
+				 freq_fastread	:3,
+				 freq_write	:3,
+				 freq_read_id	:3,
+						:2;
 		};
 	};
 	union {			/* 0x04 */
-		uint32_t FLILL;
+		uint32_t FLILL; /* Flash Invalid Instructions Register */
 		struct {
-			uint8_t invalid_instr0;
-			uint8_t invalid_instr1;
-			uint8_t invalid_instr2;
-			uint8_t invalid_instr3;
+			uint32_t invalid_instr0	:8,
+				 invalid_instr1	:8,
+				 invalid_instr2	:8,
+				 invalid_instr3	:8;
 		};
 	};
 	union {			/* 0x08 */
-		uint32_t FLPB;
+		uint32_t FLPB; /* Flash Partition Boundary Register */
 		struct {
-			uint16_t FPBA	:13;
-			unsigned	:19;
+			uint32_t FPBA	:13, /* Flash Partition Boundary Addr */
+					:19;
 		};
 	};
 };
 
-struct flash_region {
-	
+struct ich_desc_region {
 	union {
-		uint32_t FLREG0; /* Flash Descriptor */
+		uint32_t FLREGs[5];
 		struct {
-			uint16_t reg0_base	:13;
-			unsigned		:3;
-			uint16_t reg0_limit	:13;
-			unsigned		:3;
+			struct { /* FLREG0 Flash Descriptor */
+				uint32_t reg0_base	:13,
+							:3,
+					 reg0_limit	:13,
+							:3;
+			};
+			struct { /* FLREG1 BIOS */
+				uint32_t reg1_base	:13,
+							:3,
+					 reg1_limit	:13,
+							:3;
+			};
+			struct { /* FLREG2 ME */
+				uint32_t reg2_base	:13,
+							:3,
+					 reg2_limit	:13,
+							:3;
+			};
+			struct { /* FLREG3 GbE */
+				uint32_t reg3_base	:13,
+							:3,
+					 reg3_limit	:13,
+							:3;
+			};
+			struct { /* FLREG4 Platform */
+				uint32_t reg4_base	:13,
+							:3,
+					 reg4_limit	:13,
+							:3;
+			};
 		};
 	};
-	union {
-		uint32_t FLREG1; /* BIOS */
-		struct {
-			uint16_t reg1_base	:13;
-			unsigned		:3;
-			uint16_t reg1_limit	:13;
-			unsigned		:3;
-		};
-	};
-	union {
-		uint32_t FLREG2; /* ME */
-		struct {
-			uint16_t reg2_base	:13;
-			unsigned		:3;
-			uint16_t reg2_limit	:13;
-			unsigned		:3;
-		};
-	};
-	union {
-		uint32_t FLREG3; /* GbE */
-		struct {
-			uint16_t reg3_base	:13;
-			unsigned		:3;
-			uint16_t reg3_limit	:13;
-			unsigned		:3;
-		};
-	};
-} frba;
+};
 
-struct flash_master {
+struct ich_desc_master {
 	union {
 		uint32_t FLMSTR1;
 		struct {
-			uint16_t BIOS_req_ID		:16;
-			uint8_t  BIOS_descr_read	:1;
-			uint8_t  BIOS_BIOS_read		:1;
-			uint8_t  BIOS_ME_read		:1;
-			uint8_t  BIOS_GbE_read		:1;
-			uint8_t  BIOS_plat_read		:1;
-			unsigned			:3;
-			uint8_t  BIOS_descr_write	:1;
-			uint8_t  BIOS_BIOS_write	:1;
-			uint8_t  BIOS_ME_write		:1;
-			uint8_t  BIOS_GbE_write		:1;
-			uint8_t  BIOS_plat_write	:1;
-			unsigned			:3;
+			uint32_t BIOS_req_ID	:16,
+				 BIOS_descr_r	:1,
+				 BIOS_BIOS_r	:1,
+				 BIOS_ME_r	:1,
+				 BIOS_GbE_r	:1,
+				 BIOS_plat_r	:1,
+						:3,
+				 BIOS_descr_w	:1,
+				 BIOS_BIOS_w	:1,
+				 BIOS_ME_w	:1,
+				 BIOS_GbE_w	:1,
+				 BIOS_plat_w	:1,
+						:3;
 		};
 	};
 	union {
 		uint32_t FLMSTR2;
 		struct {
-			uint16_t ME_req_ID		:16;
-			uint8_t  ME_descr_read	:1;
-			uint8_t  ME_BIOS_read		:1;
-			uint8_t  ME_ME_read		:1;
-			uint8_t  ME_GbE_read		:1;
-			uint8_t  ME_plat_read		:1;
-			unsigned			:3;
-			uint8_t  ME_descr_write		:1;
-			uint8_t  ME_BIOS_write		:1;
-			uint8_t  ME_ME_write		:1;
-			uint8_t  ME_GbE_write		:1;
-			uint8_t  ME_plat_write		:1;
-			unsigned			:3;
+			uint32_t ME_req_ID	:16,
+				 ME_descr_r	:1,
+				 ME_BIOS_r	:1,
+				 ME_ME_r	:1,
+				 ME_GbE_r	:1,
+				 ME_plat_r	:1,
+						:3,
+				 ME_descr_w	:1,
+				 ME_BIOS_w	:1,
+				 ME_ME_w	:1,
+				 ME_GbE_w	:1,
+				 ME_plat_w	:1,
+						:3;
 		};
 	};
 	union {
 		uint32_t FLMSTR3;
 		struct {
-			uint16_t GbE_req_ID		:16;
-			uint8_t  GbE_descr_read		:1;
-			uint8_t  GbE_BIOS_read		:1;
-			uint8_t  GbE_ME_read		:1;
-			uint8_t  GbE_GbE_read		:1;
-			uint8_t  GbE_plat_read		:1;
-			unsigned			:3;
-			uint8_t  GbE_descr_write	:1;
-			uint8_t  GbE_BIOS_write		:1;
-			uint8_t  GbE_ME_write		:1;
-			uint8_t  GbE_GbE_write		:1;
-			uint8_t  GbE_plat_write		:1;
-			unsigned			:3;
+			uint32_t GbE_req_ID	:16,
+				 GbE_descr_r	:1,
+				 GbE_BIOS_r	:1,
+				 GbE_ME_r	:1,
+				 GbE_GbE_r	:1,
+				 GbE_plat_r	:1,
+						:3,
+				 GbE_descr_w	:1,
+				 GbE_BIOS_w	:1,
+				 GbE_ME_w	:1,
+				 GbE_GbE_w	:1,
+				 GbE_plat_w	:1,
+						:3;
 		};
 	};
 };
 
-#ifdef ICH_DESCRIPTORS_FROM_MMAP_DUMP
-struct flash_strap {
-	union {
-		struct {
-			union {
-				uint32_t STRP0;
-				struct {
-					uint8_t  ME_DISABLE		:1;
-					unsigned			:6;
-					uint8_t  TCOMODE		:1;
-					uint8_t  ASD			:7;
-					uint8_t  BMCMODE		:1;
-					unsigned			:3;
-					uint8_t  GLAN_PCIE_SEL		:1;
-					uint8_t  GPIO12_SEL		:2;
-					uint8_t  SPICS1_LANPHYPC_SEL	:1;
-					uint8_t  MESM2SEL		:1;
-					unsigned			:1;
-					uint8_t  ASD2			:7;
-				};
-			};
-			union {
-				uint32_t STRP1;
-				struct {
-					uint8_t  ME_disable_B		:1;
-					unsigned			:31;
-				};
-			};
-		}ich8;
-		union {
-			uint32_t STRPs[15];
-			struct {
-				union {
-					uint32_t STRP0;
-					struct {
-						unsigned			:1;
-						uint8_t  cs_ss2			:1;
-						unsigned			:5;
-						uint8_t  SMB_EN			:1;
-						uint8_t  SML0_EN		:1;
-						uint8_t  SML1_EN		:1;
-						uint8_t  SML1FRQ		:2;
-						uint8_t  SMB0FRQ		:2;
-						uint8_t  SML0FRQ		:2;
-						unsigned			:4;
-						uint8_t  LANPHYPC_GP12_SEL	:1;
-						uint8_t  cs_ss1			:1;
-						unsigned			:2;
-						uint8_t  DMI_REQID_DIS		:1;
-						unsigned			:4;
-						uint8_t  BBBS			:2;
-						unsigned			:1;
-					};
-				};
-				union {
-					uint32_t STRP1;
-					struct {
-					};
-				};
-				union {
-					uint32_t STRP2;
-					struct {
-					};
-				};
-				union {
-					uint32_t STRP3;
-					struct {
-					};
-				};
-				union {
-					uint32_t STRP4;
-					struct {
-					};
-				};
-				union {
-					uint32_t STRP5;
-					struct {
-					};
-				};
-				union {
-					uint32_t STRP6;
-					struct {
-					};
-				};
-				union {
-					uint32_t STRP7;
-					struct {
-					};
-				};
-				union {
-					uint32_t STRP8;
-					struct {
-					};
-				};
-				union {
-					uint32_t STRP9;
-					struct {
-					};
-				};
-				union {
-					uint32_t STRP10;
-					struct {
-					};
-				};
-				union {
-					uint32_t STRP11;
-					struct {
-					};
-				};
-				union {
-					uint32_t STRP12;
-					struct {
-					};
-				};
-				union {
-					uint32_t STRP13;
-					struct {
-					};
-				};
-				union {
-					uint32_t STRP14;
-					struct {
-					};
-				};
-				union {
-					uint32_t STRP15;
-					struct {
-					};
-				};
-			};
-		}ibex;
-	};
+struct ich_descriptors {
+	struct ich_desc_content content;
+	struct ich_desc_component component;
+	struct ich_desc_region region;
+	struct ich_desc_master master;
 };
 
-struct flash_upper_map {
-	union {
-		uint32_t FLUMAP1;
-		struct {
-			uint8_t  VTBA	:8;
-			uint8_t  VTL	:8;
-			unsigned	:16;
-		};
-	};
-	struct {
-		union {
-			uint32_t JID;
-			struct {
-				uint8_t vid	:8;
-				uint8_t cid0	:8;
-				uint8_t cid1	:8;
-				unsigned	:8;
-			};
-		};
-		union {
-			uint32_t VSCC;
-			struct {
-				uint8_t  ubes	:2;
-				uint8_t  uwg	:1;
-				uint8_t  uwsr	:1;
-				uint8_t  uwews	:1;
-				unsigned	:3;
-				uint8_t  ueo	:8;
-				uint8_t  lbes	:2;
-				uint8_t  lwg	:1;
-				uint8_t  lwsr	:1;
-				uint8_t  lwews	:1;
-				unsigned	:3;
-				uint16_t leo	:16;
-			};
-		};
-	}vscc_table[128];
-};
-#endif // ICH_DESCRIPTORS_FROM_MMAP_DUMP
+void prettyprint_ich_descriptors(enum ich_chipset, const struct ich_descriptors *desc);
 
-void prettyprint_ich_descriptors(enum chipset);
+void prettyprint_ich_descriptor_content(const struct ich_desc_content *content);
+void prettyprint_ich_descriptor_component(const struct ich_descriptors *desc);
+void prettyprint_ich_descriptor_region(const struct ich_descriptors *desc);
+void prettyprint_ich_descriptor_master(const struct ich_desc_master *master);
 
-void prettyprint_ich_descriptor_map(void);
-void prettyprint_ich_descriptor_component(void);
-void prettyprint_ich_descriptor_region(void);
-void prettyprint_ich_descriptor_master(void);
+int read_ich_descriptors_via_fdo(void *spibar, struct ich_descriptors *desc);
+int getFCBA_component_density(const struct ich_descriptors *desc, uint8_t idx);
 
-int getFCBA_component_density(uint8_t comp);
-
-#ifdef ICH_DESCRIPTORS_FROM_MMAP_DUMP
-
-void prettyprint_ich_descriptor_upper_map(void);
-void prettyprint_ich_descriptor_straps(enum chipset cs);
-int read_ich_descriptors_from_dump(uint32_t *dump, enum chipset cs);
-
-#else // ICH_DESCRIPTORS_FROM_MMAP_DUMP
-
-void read_ich_descriptors_from_fdo(void *spibar);
-
-#endif // ICH_DESCRIPTORS_FROM_MMAP_DUMP
-
-#endif // __ICH_DESCRIPTORS_H__
-#endif // defined(__i386__) || defined(__x86_64__)
+#endif /* __ICH_DESCRIPTORS_H__ */
+#endif /* defined(__i386__) || defined(__x86_64__) */
diff --git a/ichspi.c b/ichspi.c
index 149920b..96ba5e3 100644
--- a/ichspi.c
+++ b/ichspi.c
@@ -23,24 +23,11 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
-/*
- * This module is designed for supporting the devices
- * ST M25P40
- * ST M25P80
- * ST M25P16
- * ST M25P32 already tested
- * ST M25P64
- * AT 25DF321 already tested
- * ... and many more SPI flash devices
- *
- */
-
-#if defined(CONFIG_INTERNAL) && (defined(__i386__) || defined(__x86_64__))
+#if defined(__i386__) || defined(__x86_64__)
 
 #include <string.h>
+#include <stdlib.h>
 #include "flash.h"
-#include "flashchips.h"
-#include "chipdrivers.h"
 #include "programmer.h"
 #include "spi.h"
 #include "ich_descriptors.h"
@@ -84,10 +71,8 @@
 #define ICH9_REG_FREG0		0x54	/* 32 Bytes Flash Region 0 */
 
 #define ICH9_REG_PR0		0x74	/* 32 Bytes Protected Range 0 */
-#define ICH9_REG_PR1		0x78	/* 32 Bytes Protected Range 1 */
-#define ICH9_REG_PR2		0x7c	/* 32 Bytes Protected Range 2 */
-#define ICH9_REG_PR3		0x80	/* 32 Bytes Protected Range 3 */
-#define ICH9_REG_PR4		0x84	/* 32 Bytes Protected Range 4 */
+#define PR_WP_OFF		31	/* 31: write protection enable */
+#define PR_RP_OFF		15	/* 15: read protection enable */
 
 #define ICH9_REG_SSFS		0x90	/* 08 Bits */
 #define SSFS_SCIP_OFF		0	/* SPI Cycle In Progress */
@@ -134,13 +119,15 @@
 #define ICH9_REG_BBAR		0xA0	/* 32 Bits BIOS Base Address Configuration */
 #define BBAR_MASK	0x00ffff00		/* 8-23: Bottom of System Flash */
 
+#define ICH8_REG_VSCC		0xC1	/* 32 Bits Vendor Specific Component Capabilities */
 #define ICH9_REG_LVSCC		0xC4	/* 32 Bits Host Lower Vendor Specific Component Capabilities */
 #define ICH9_REG_UVSCC		0xC8	/* 32 Bits Host Upper Vendor Specific Component Capabilities */
 /* The individual fields of the VSCC registers are defined in the file
- * ich_descriptors.h. The reason is that the same fields are also used in the
- * flash descriptors to define the properties of the different flash chips
- * supported by the BIOS image. These descriptors are also the source for the
- * registers above. */
+ * ich_descriptors.h. The reason is that the same layout is also used in the
+ * flash descriptor to define the properties of the different flash chips
+ * supported. The BIOS (or the ME?) is responsible to populate the ICH registers
+ * with the information from the descriptor on startup depending on the actual
+ * chip(s) detected. */
 
 #define ICH9_REG_FPB		0xD0	/* 32 Bits Flash Partition Boundary */
 #define FPB_FPBA_OFF		0	/* 0-12: Block/Sector Erase Size */
@@ -185,6 +172,7 @@
 /* ICH SPI configuration lock-down. May be set during chipset enabling. */
 static int ichspi_lock = 0;
 
+static enum ich_chipset ich_generation = CHIPSET_ICH_UNKNOWN;
 uint32_t ichspi_bbar = 0;
 
 static void *ich_spibar = NULL;
@@ -306,24 +294,38 @@
 /* pretty printing functions */
 static void prettyprint_opcodes(OPCODES *ops)
 {
-	if(ops == NULL)
+	OPCODE oc;
+	const char *t;
+	const char *a;
+	uint8_t i;
+	static const char *const spi_type[4] = {
+		"read  w/o addr",
+		"write w/o addr",
+		"read  w/  addr",
+		"write w/  addr"
+	};
+	static const char *const atomic_type[3] = {
+		"none",
+		" 0  ",
+		" 1  "
+	};
+
+	if (ops == NULL)
 		return;
 
-	msg_pdbg("preop0=0x%02x, preop1=0x%02x\n", ops->preop[0],
-		 ops->preop[1]);
-
-	OPCODE oc;
-	uint8_t i;
+	msg_pdbg2("        OP        Type      Pre-OP\n");
 	for (i = 0; i < 8; i++) {
 		oc = ops->opcode[i];
-		msg_pdbg("op[%d]=0x%02x, %d, %d\n",
-			 i,
-			 oc.opcode,
-			 oc.spi_type,
-			 oc.atomic);
+		t = (oc.spi_type > 3) ? "invalid" : spi_type[oc.spi_type];
+		a = (oc.atomic > 2) ? "invalid" : atomic_type[oc.atomic];
+		msg_pdbg2("op[%d]: 0x%02x, %s, %s\n", i, oc.opcode, t, a);
 	}
+	msg_pdbg2("Pre-OP 0: 0x%02x, Pre-OP 1: 0x%02x\n", ops->preop[0],
+		 ops->preop[1]);
 }
 
+#define pprint_reg(reg, bit, val, sep) msg_pdbg("%s=%d" sep, #bit, (val & reg##_##bit)>>reg##_##bit##_OFF)
+
 static void prettyprint_ich9_reg_hsfs(uint16_t reg_val)
 {
 	msg_pdbg("HSFS: ");
@@ -371,7 +373,7 @@
 {
 	int a;
 
-	for (a = 0; a < sizeof(POSSIBLE_OPCODES)/sizeof(POSSIBLE_OPCODES[0]); a++) {
+	for (a = 0; a < ARRAY_SIZE(POSSIBLE_OPCODES); a++) {
 		if (POSSIBLE_OPCODES[a].opcode == opcode)
 			return POSSIBLE_OPCODES[a].spi_type;
 	}
@@ -421,6 +423,11 @@
 {
 	int a;
 
+	if (op == NULL) {
+		msg_perr("\n%s: null OPCODES pointer!\n", __func__);
+		return -1;
+	}
+
 	for (a = 0; a < 8; a++) {
 		if (op->opcode[a].opcode == opcode)
 			return a;
@@ -433,6 +440,11 @@
 {
 	int a;
 
+	if (op == NULL) {
+		msg_perr("\n%s: null OPCODES pointer!\n", __func__);
+		return -1;
+	}
+
 	for (a = 0; a < 2; a++) {
 		if (op->preop[a] == preop)
 			return a;
@@ -453,23 +465,20 @@
 		return -1;
 	}
 
-	switch (spi_programmer->type) {
-	case SPI_CONTROLLER_ICH7:
-	case SPI_CONTROLLER_VIA:
+	switch (ich_generation) {
+	case CHIPSET_ICH7:
 		preop = REGREAD16(ICH7_REG_PREOP);
 		optype = REGREAD16(ICH7_REG_OPTYPE);
 		opmenu[0] = REGREAD32(ICH7_REG_OPMENU);
 		opmenu[1] = REGREAD32(ICH7_REG_OPMENU + 4);
 		break;
-	case SPI_CONTROLLER_ICH9:
+	case CHIPSET_ICH8:
+	default:		/* Future version might behave the same */
 		preop = REGREAD16(ICH9_REG_PREOP);
 		optype = REGREAD16(ICH9_REG_OPTYPE);
 		opmenu[0] = REGREAD32(ICH9_REG_OPMENU);
 		opmenu[1] = REGREAD32(ICH9_REG_OPMENU + 4);
 		break;
-	default:
-		msg_perr("%s: unsupported chipset\n", __func__);
-		return -1;
 	}
 
 	op->preop[0] = (uint8_t) preop;
@@ -528,9 +537,8 @@
 	}
 
 	msg_pdbg("\n%s: preop=%04x optype=%04x opmenu=%08x%08x\n", __func__, preop, optype, opmenu[0], opmenu[1]);
-	switch (spi_programmer->type) {
-	case SPI_CONTROLLER_ICH7:
-	case SPI_CONTROLLER_VIA:
+	switch (ich_generation) {
+	case CHIPSET_ICH7:
 		/* Register undo only for enable_undo=1, i.e. first call. */
 		if (enable_undo) {
 			rmmio_valw(ich_spibar + ICH7_REG_PREOP);
@@ -543,7 +551,8 @@
 		mmio_writel(opmenu[0], ich_spibar + ICH7_REG_OPMENU);
 		mmio_writel(opmenu[1], ich_spibar + ICH7_REG_OPMENU + 4);
 		break;
-	case SPI_CONTROLLER_ICH9:
+	case CHIPSET_ICH8:
+	default:		/* Future version might behave the same */
 		/* Register undo only for enable_undo=1, i.e. first call. */
 		if (enable_undo) {
 			rmmio_valw(ich_spibar + ICH9_REG_PREOP);
@@ -556,32 +565,54 @@
 		mmio_writel(opmenu[0], ich_spibar + ICH9_REG_OPMENU);
 		mmio_writel(opmenu[1], ich_spibar + ICH9_REG_OPMENU + 4);
 		break;
-	default:
-		msg_perr("%s: unsupported chipset\n", __func__);
-		return -1;
 	}
 
 	return 0;
 }
 
 /*
+ * Returns -1 if at least one mandatory opcode is inaccessible, 0 otherwise.
+ * FIXME: this should also check for
+ *   - at least one probing opcode (RDID (incl. AT25F variants?), REMS, RES?)
+ *   - at least one erasing opcode (lots.)
+ *   - at least one program opcode (BYTE_PROGRAM, AAI_WORD_PROGRAM, ...?)
+ *   - necessary preops? (EWSR, WREN, ...?)
+ */
+static int ich_missing_opcodes()
+{
+	uint8_t ops[] = {
+		JEDEC_READ,
+		JEDEC_RDSR,
+		0
+	};
+	int i = 0;
+	while (ops[i] != 0) {
+		msg_pspew("checking for opcode 0x%02x\n", ops[i]);
+		if (find_opcode(curopcodes, ops[i]) == -1)
+			return -1;
+		i++;
+	}
+	return 0;
+}
+
+/*
  * Try to set BBAR (BIOS Base Address Register), but read back the value in case
  * it didn't stick.
  */
 static void ich_set_bbar(uint32_t min_addr)
 {
 	int bbar_off;
-	switch (spi_programmer->type) {
-	case SPI_CONTROLLER_ICH7:
-	case SPI_CONTROLLER_VIA:
+	switch (ich_generation) {
+	case CHIPSET_ICH7:
 		bbar_off = 0x50;
 		break;
-	case SPI_CONTROLLER_ICH9:
+	case CHIPSET_ICH8:
+		msg_perr("BBAR offset is unknown on ICH8!\n");
+		return;
+	case CHIPSET_ICH9:
+	default:		/* Future version might behave the same */
 		bbar_off = ICH9_REG_BBAR;
 		break;
-	default:
-		msg_perr("Unknown chipset for BBAR setting!\n");
-		return;
 	}
 	
 	ichspi_bbar = mmio_readl(ich_spibar + bbar_off) & ~BBAR_MASK;
@@ -598,58 +629,53 @@
 	 * failed, the restore will fail as well, so no problem there.
 	 */
 	if (ichspi_bbar != min_addr)
-		msg_perr("Setting BBAR failed!\n");
+		msg_perr("Setting BBAR to 0x%08x failed! New value: 0x%08x.\n",
+			 min_addr, ichspi_bbar);
 }
 
-/* Reads up to len byte from the fdata/spid register into the data array.
- * The amount actually read is limited by the maximum read size of the
- * chipset. */
+/* Read len bytes from the fdata/spid register into the data array.
+ *
+ * Note that using len > spi_programmer->max_data_read will return garbage or
+ * may even crash.
+ */
  static void ich_read_data(uint8_t *data, int len, int reg0_off)
  {
-	int a;
+	int i;
 	uint32_t temp32 = 0;
 
-	if (len > spi_programmer->max_data_read)
-		len = spi_programmer->max_data_read;
+	for (i = 0; i < len; i++) {
+		if ((i % 4) == 0)
+			temp32 = REGREAD32(reg0_off + i);
 
-	for (a = 0; a < len; a++) {
-		if ((a % 4) == 0)
-			temp32 = REGREAD32(reg0_off + (a));
-
-		data[a] = (temp32 & (((uint32_t) 0xff) << ((a % 4) * 8)))
-			  >> ((a % 4) * 8);
+		data[i] = (temp32 >> ((i % 4) * 8)) & 0xff;
 	}
 }
 
-/* Fills up to len bytes from the data array into the fdata/spid registers.
- * The amount actually written is limited by the maximum write size of the
- * chipset and is returned by the function. */
-static uint8_t ich_fill_data(const uint8_t *data, int len, int reg0_off)
+/* Fill len bytes from the data array into the fdata/spid registers.
+ *
+ * Note that using len > spi_programmer->max_data_write will trash the registers
+ * following the data registers.
+ */
+static void ich_fill_data(const uint8_t *data, int len, int reg0_off)
 {
 	uint32_t temp32 = 0;
-	int a;
-
-	if (len > spi_programmer->max_data_write)
-		len = spi_programmer->max_data_write;
+	int i;
 
 	if (len <= 0)
-		return 0;
+		return;
 
-	for (a = 0; a < len; a++) {
-		if ((a % 4) == 0) {
+	for (i = 0; i < len; i++) {
+		if ((i % 4) == 0)
 			temp32 = 0;
-		}
 
-		temp32 |= ((uint32_t) data[a]) << ((a % 4) * 8);
+		temp32 |= ((uint32_t) data[i]) << ((i % 4) * 8);
 
-		if ((a % 4) == 3) {
-			REGWRITE32(reg0_off + (a - (a % 4)), temp32);
-		}
+		if ((i % 4) == 3) /* 32 bits are full, write them to regs. */
+			REGWRITE32(reg0_off + (i - (i % 4)), temp32);
 	}
-	if (((a - 1) % 4) != 3) {
-		REGWRITE32(reg0_off + ((a - 1) - ((a - 1) % 4)), temp32);
-	}
-	return len;
+	i--;
+	if ((i % 4) != 3) /* Write remaining data to regs. */
+		REGWRITE32(reg0_off + (i - (i % 4)), temp32);
 }
 
 /* This function generates OPCODES from or programs OPCODES to ICH according to
@@ -673,11 +699,6 @@
 		msg_pdbg("Programming OPCODES... ");
 		curopcodes_done = &O_ST_M25P;
 		rc = program_opcodes(curopcodes_done, 1);
-		/* 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) {
@@ -688,7 +709,6 @@
 		curopcodes = curopcodes_done;
 		msg_pdbg("done\n");
 		prettyprint_opcodes(curopcodes);
-		msg_pdbg("\n");
 		return 0;
 	}
 }
@@ -811,9 +831,8 @@
 		return 1;
 	}
 
-	if ((!write_cmd) && (datalength != 0)) {
+	if ((!write_cmd) && (datalength != 0))
 		ich_read_data(data, datalength, ICH7_REG_SPID0);
-	}
 
 	return 0;
 }
@@ -937,9 +956,8 @@
 		return 1;
 	}
 
-	if ((!write_cmd) && (datalength != 0)) {
+	if ((!write_cmd) && (datalength != 0))
 		ich_read_data(data, datalength, ICH9_REG_FDATA0);
-	}
 
 	return 0;
 }
@@ -962,18 +980,104 @@
 		return SPI_INVALID_LENGTH;
 	}
 
-	switch (spi_programmer->type) {
-	case SPI_CONTROLLER_VIA:
-	case SPI_CONTROLLER_ICH7:
+	switch (ich_generation) {
+	case CHIPSET_ICH7:
 		return ich7_run_opcode(op, offset, datalength, data, maxlength);
-	case SPI_CONTROLLER_ICH9:
+	case CHIPSET_ICH8:
+	default:		/* Future version might behave the same */
 		return ich9_run_opcode(op, offset, datalength, data);
-	default:
-		/* If we ever get here, something really weird happened */
-		return -1;
 	}
 }
 
+#define DEFAULT_NUM_FD_REGIONS	5
+static int num_fd_regions;
+
+const char *const region_names[] = {
+	"Flash Descriptor", "BIOS", "Management Engine",
+	"Gigabit Ethernet", "Platform Data"
+};
+
+enum fd_access_level {
+	FD_REGION_LOCKED,
+	FD_REGION_READ_ONLY,
+	FD_REGION_WRITE_ONLY,
+	FD_REGION_READ_WRITE,
+};
+
+struct fd_region_permission {
+	enum fd_access_level level;
+	const char *name;
+} fd_region_permissions[] = {
+	/* order corresponds to FRAP bitfield */
+	{ FD_REGION_LOCKED, "locked" },
+	{ FD_REGION_READ_ONLY, "read-only" },
+	{ FD_REGION_WRITE_ONLY, "write-only" },
+	{ FD_REGION_READ_WRITE, "read-write" },
+};
+
+/* FIXME: Replace usage of access_names with the region_access struct */
+const char *const access_names[4] = {
+	"locked", "read-only", "write-only", "read-write"
+};
+
+struct fd_region {
+	const char *name;
+	struct fd_region_permission *permission;
+	uint32_t base;
+	uint32_t limit;
+} fd_regions[] = {
+	/* order corresponds to flash descriptor */
+	{ .name = "Flash Descriptor" },
+	{ .name = "BIOS" },
+	{ .name = "Management Engine" },
+	{ .name = "Gigabit Ethernet" },
+	{ .name = "Platform Data" },
+};
+
+static int check_fd_permissions(OPCODE *opcode, uint32_t addr, int count)
+{
+	int i;
+	uint8_t type = opcode->spi_type;
+	int ret = 0;
+
+	/* check flash descriptor permissions (if present) */
+	for (i = 0; i < num_fd_regions; i++) {
+		const char *name = fd_regions[i].name;
+		enum fd_access_level level;
+
+		if ((addr + count - 1 < fd_regions[i].base) ||
+		    (addr > fd_regions[i].limit))
+			continue;
+
+		if (!fd_regions[i].permission) {
+			msg_perr("No permissions set for flash region %s\n",
+			          fd_regions[i].name);
+			break;
+		}
+
+		level = fd_regions[i].permission->level;
+
+		if (type == SPI_OPCODE_TYPE_READ_WITH_ADDRESS) {
+			if (level != FD_REGION_READ_ONLY &&
+			    level != FD_REGION_READ_WRITE) {
+				msg_pspew("%s: Cannot read address 0x%08x in "
+				          "region %s\n", __func__,addr,name);
+				ret = SPI_ACCESS_DENIED;
+			}
+		} else if (type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) {
+			if (level != FD_REGION_WRITE_ONLY &&
+			    level != FD_REGION_READ_WRITE) {
+				msg_pspew("%s: Cannot write to address 0x%08x in"
+				          "region %s\n", __func__,addr,name);
+				ret = SPI_ACCESS_DENIED;
+			}
+		}
+		break;
+	}
+
+	return ret;
+}
+
 static int ich_spi_send_command(unsigned int writecnt, unsigned int readcnt,
 		    const unsigned char *writearr, unsigned char *readarr)
 {
@@ -1036,27 +1140,6 @@
 		return SPI_INVALID_LENGTH;
 	}
 
-	/* if opcode-type requires an address */
-	if (opcode->spi_type == SPI_OPCODE_TYPE_READ_WITH_ADDRESS ||
-	    opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) {
-		addr = (writearr[1] << 16) |
-		    (writearr[2] << 8) | (writearr[3] << 0);
-		switch (spi_programmer->type) {
-		case SPI_CONTROLLER_ICH7:
-		case SPI_CONTROLLER_VIA:
-		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.
 	 * The maximum data length is identical for the maximum read length and
 	 * for the maximum write length excluding opcode and address. Opcode and
@@ -1075,6 +1158,24 @@
 		count = readcnt;
 	}
 
+	/* if opcode-type requires an address */
+	if (opcode->spi_type == SPI_OPCODE_TYPE_READ_WITH_ADDRESS ||
+	    opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) {
+		addr = (writearr[1] << 16) |
+		    (writearr[2] << 8) | (writearr[3] << 0);
+		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;
+		}
+		if (num_fd_regions > 0) {
+			result = check_fd_permissions(opcode, addr, count);
+			if (result)
+				return result;
+		}
+	}
+
 	result = run_opcode(*opcode, addr, count, data);
 	if (result) {
 		msg_pdbg("Running OPCODE 0x%02x failed ", opcode->opcode);
@@ -1091,7 +1192,7 @@
 		    (opcode->spi_type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS)) {
 			int i;
 			msg_pspew("The data was:\n");
-			for(i=0; i<count; i++){
+			for (i = 0; i < count; i++){
 				msg_pspew("%3d: 0x%02x\n", i, data[i]);
 			}
 		}
@@ -1100,6 +1201,12 @@
 	return result;
 }
 
+static struct hwseq_data {
+	uint32_t size_comp0;
+	uint32_t size_comp1;
+} hwseq_data;
+
+/* Sets FLA in FADDR to (addr & 0x01FFFFFF) without touching other bits. */
 static void ich_hwseq_set_addr(uint32_t addr)
 {
 	uint32_t addr_old = REGREAD32(ICH9_REG_FADDR) & ~0x01FFFFFF;
@@ -1107,11 +1214,16 @@
 }
 
 /* Sets FADDR.FLA to 'addr' and returns the erase block size in bytes
- * of the block containing this address. */
+ * of the block containing this address. May return nonsense if the address is
+ * not valid. The erase block size for a specific address depends on the flash
+ * partition layout as specified by FPB and the partition properties as defined
+ * by UVSCC and LVSCC respectively. An alternative to implement this method
+ * would be by querying FPB and the respective VSCC register directly.
+ */
 static uint32_t ich_hwseq_get_erase_block_size(unsigned int addr)
 {
 	uint8_t enc_berase;
-	const uint32_t dec_berase[4] = {
+	static const uint32_t const dec_berase[4] = {
 		256,
 		4 * 1024,
 		8 * 1024,
@@ -1124,7 +1236,7 @@
 	return dec_berase[enc_berase];
 }
 
-/* Polls for Cycle Done Status, Flash Cycle Error or timeout in 10 us intervals.
+/* Polls for Cycle Done Status, Flash Cycle Error or timeout in 8 us intervals.
    Resets all error flags in HSFS.
    Returns 0 if the cycle completes successfully without errors within
    timeout us, 1 on errors. */
@@ -1134,17 +1246,18 @@
 	uint16_t hsfs;
 	uint32_t addr;
 
+	timeout /= 8; /* scale timeout duration to counter */
 	while ((((hsfs = REGREAD16(ICH9_REG_HSFS)) &
 		 (HSFS_FDONE | HSFS_FCERR)) == 0) &&
 	       --timeout) {
-		programmer_delay(10);
+		programmer_delay(8);
 	}
 	REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS));
 	if (!timeout) {
 		addr = REGREAD32(ICH9_REG_FADDR) & 0x01FFFFFF;
 		msg_perr("Timeout error between offset 0x%08x and "
-			 "0x%08x + %d (=0x%08x)!\n",
-			 addr, addr, len - 1, addr + len - 1);
+			 "0x%08x (= 0x%08x + %d)!\n",
+			 addr, addr + len - 1, addr, len - 1);
 		prettyprint_ich9_reg_hsfs(hsfs);
 		prettyprint_ich9_reg_hsfc(REGREAD16(ICH9_REG_HSFC));
 		return 1;
@@ -1153,8 +1266,8 @@
 	if (hsfs & HSFS_FCERR) {
 		addr = REGREAD32(ICH9_REG_FADDR) & 0x01FFFFFF;
 		msg_perr("Transaction error between offset 0x%08x and "
-			 "0x%08x + %d (=0x%08x)!\n",
-			 addr, addr, len - 1, addr + len - 1);
+			 "0x%08x (= 0x%08x + %d)!\n",
+			 addr, addr + len - 1, addr, len - 1);
 		prettyprint_ich9_reg_hsfs(hsfs);
 		prettyprint_ich9_reg_hsfc(REGREAD16(ICH9_REG_HSFC));
 		return 1;
@@ -1167,29 +1280,11 @@
 	uint32_t total_size, boundary;
 	uint32_t erase_size_low, size_low, erase_size_high, size_high;
 	struct block_eraser *eraser;
-	extern struct flash_descriptor fdbar;
 
-	if (flash->manufacture_id != INTEL_ID ||
-	    flash->model_id != INTEL_HWSEQ) {
-		msg_cerr("This chip (%s) is not supported in hardware"
-			 "sequencing mode and should never have been probed.\n",
-			 flash->name);
-		msg_cerr("%s: Please report a bug at flashrom@flashrom.org\n",
-			 __func__);
-		return 0;
-	}
-
-	msg_cdbg("Prerequisites for Intel Hardware Sequencing are ");
-	if (spi_programmer->type != SPI_CONTROLLER_ICH_HWSEQ) {
-		msg_cdbg("not met.\n");
-		return 0;
-	}
-	msg_cdbg("met.\n");
-
-	total_size = (getFCBA_component_density(0) +
-		      getFCBA_component_density(1));
-	msg_cdbg("Found %d attached SPI flash chip", fdbar.NC + 1);
-	if (fdbar.NC)
+	total_size = hwseq_data.size_comp0 + hwseq_data.size_comp1;
+	msg_cdbg("Found %d attached SPI flash chip",
+		 (hwseq_data.size_comp1 != 0) ? 2 : 1);
+	if (hwseq_data.size_comp1 != 0)
 		msg_cdbg("s with a combined");
 	else
 		msg_cdbg(" with a");
@@ -1229,19 +1324,10 @@
 		msg_cdbg("In that range are %d erase blocks with %d B each.\n",
 			 size_high / erase_size_high, erase_size_high);
 	}
+	flash->tested = TEST_OK_PREW;
 	return 1;
 }
 
-static int ich_hwseq_send_command(unsigned int writecnt,
-				      unsigned int readcnt,
-				      const unsigned char *writearr,
-				      unsigned char *readarr)
-{
-	msg_pdbg("skipped. Intel Hardware Sequencing does not support sending "
-		 "arbitrary commands.\n");
-	return -1;
-}
-
 int ich_hwseq_block_erase(struct flashchip *flash,
 			  unsigned int addr,
 			  unsigned int len)
@@ -1250,13 +1336,6 @@
 	uint16_t hsfc;
 	uint32_t timeout = 5000 * 1000; /* 5 s for max 64 kB */
 
-	if (flash->manufacture_id != INTEL_ID ||
-	    flash->model_id != INTEL_HWSEQ) {
-		msg_perr("This chip (%s) is not supported in hardware"
-			 "sequencing mode\n", flash->name);
-		return -1;
-	}
-
 	erase_block = ich_hwseq_get_erase_block_size(addr);
 	if (len != erase_block) {
 		msg_cerr("Erase block size for address 0x%06x is %d B, "
@@ -1299,19 +1378,13 @@
 	return 0;
 }
 
-int ich_hwseq_read(struct flashchip *flash, uint8_t *buf, int addr, int len)
+int ich_hwseq_read(struct flashchip *flash, uint8_t *buf, unsigned int addr,
+		   unsigned int len)
 {
 	uint16_t hsfc;
 	uint16_t timeout = 100 * 60;
 	uint8_t block_len;
 
-	if (flash->manufacture_id != INTEL_ID ||
-	    flash->model_id != INTEL_HWSEQ) {
-		msg_perr("This chip (%s) is not supported in hardware"
-			 "sequencing mode.\n", flash->name);
-		return -1;
-	}
-
 	if (addr < 0 || addr + len > flash->total_size * 1024) {
 		msg_perr("Request to read from an inaccessible memory address "
 			 "(addr=0x%x, len=%d).\n", addr, len);
@@ -1323,7 +1396,7 @@
 	REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS));
 
 	while (len > 0) {
-		block_len = min(len, spi_programmer->max_data_read);
+		block_len = min(len, opaque_programmer->max_data_read);
 		ich_hwseq_set_addr(addr);
 		hsfc = REGREAD16(ICH9_REG_HSFC);
 		hsfc &= ~HSFC_FCYCLE; /* set read operation */
@@ -1343,19 +1416,13 @@
 	return 0;
 }
 
-int ich_hwseq_write_256(struct flashchip *flash, uint8_t *buf, int addr, int len)
+int ich_hwseq_write(struct flashchip *flash, uint8_t *buf, unsigned int addr,
+		    unsigned int len)
 {
 	uint16_t hsfc;
 	uint16_t timeout = 100 * 60;
 	uint8_t block_len;
 
-	if (flash->manufacture_id != INTEL_ID ||
-	    flash->model_id != INTEL_HWSEQ) {
-		msg_perr("This chip (%s) is not supported in hardware"
-			 "sequencing mode\n", flash->name);
-		return -1;
-	}
-
 	if (addr < 0 || addr + len > flash->total_size * 1024) {
 		msg_perr("Request to write to an inaccessible memory address "
 			 "(addr=0x%x, len=%d).\n", addr, len);
@@ -1368,7 +1435,8 @@
 
 	while (len > 0) {
 		ich_hwseq_set_addr(addr);
-		block_len = ich_fill_data(buf, len, ICH9_REG_FDATA0);
+		block_len = min(len, opaque_programmer->max_data_write);
+		ich_fill_data(buf, block_len, ICH9_REG_FDATA0);
 		hsfc = REGREAD16(ICH9_REG_HSFC);
 		hsfc &= ~HSFC_FCYCLE; /* clear operation */
 		hsfc |= (0x2 << HSFC_FCYCLE_OFF); /* set write operation */
@@ -1451,38 +1519,73 @@
 #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)
 {
-	static const char *const access_names[4] = {
-		"locked", "read-only", "write-only", "read-write"
-	};
-	static const char *const 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 = ICH9_REG_FREG0 + 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]);
+		     offset, freg, i, fd_regions[i].name);
 
-	base  = ICH_FREG_BASE(freg);
-	limit = ICH_FREG_LIMIT(freg);
-	if (base > limit) {
+	fd_regions[i].base  = ICH_FREG_BASE(freg);
+	fd_regions[i].limit = ICH_FREG_LIMIT(freg) | 0x0fff;
+	fd_regions[i].permission = &fd_region_permissions[rwperms];
+	if (fd_regions[i].base > fd_regions[i].limit) {
 		/* 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]);
+	msg_pdbg("0x%08x-0x%08x is %s\n", fd_regions[i].base,
+	         fd_regions[i].limit, fd_regions[i].permission->name);
+}
+
+	/* In contrast to FRAP and the master section of the descriptor the bits
+	 * in the PR registers have an inverted meaning. The bits in FRAP
+	 * indicate read and write access _grant_. Here they indicate read
+	 * and write _protection_ respectively. If both bits are 0 the address
+	 * bits are ignored.
+	 */
+#define ICH_PR_PERMS(pr)	(((~((pr) >> PR_RP_OFF) & 1) << 0) | \
+				 ((~((pr) >> PR_WP_OFF) & 1) << 1))
+
+static void prettyprint_ich9_reg_pr(int i)
+{
+	uint8_t off = ICH9_REG_PR0 + (i * 4);
+	uint32_t pr = mmio_readl(ich_spibar + off);
+	int rwperms = ICH_PR_PERMS(pr);
+
+	msg_pdbg2("0x%02X: 0x%08x (PR%u", off, pr, i);
+	if (rwperms != 0x3)
+		msg_pdbg2(")\n0x%08x-0x%08x is %s\n", ICH_FREG_BASE(pr),
+			 ICH_FREG_LIMIT(pr) | 0x0fff, access_names[rwperms]);
+	else
+		msg_pdbg2(", unused)\n");
+}
+
+/* Set/Clear the read and write protection enable bits of PR register @i
+ * according to @read_prot and @write_prot. */
+static void ich9_set_pr(int i, int read_prot, int write_prot)
+{
+	void *addr = ich_spibar + ICH9_REG_PR0 + (i * 4);
+	uint32_t old = mmio_readl(addr);
+	uint32_t new;
+
+	msg_gspew("PR%u is 0x%08x", i, old);
+	new = old & ~((1 << PR_RP_OFF) | (1 << PR_WP_OFF));
+	if (read_prot)
+		new |= (1 << PR_RP_OFF);
+	if (write_prot)
+		new |= (1 << PR_WP_OFF);
+	if (old == new) {
+		msg_gspew(" already.\n");
+		return;
+	}
+	msg_gspew(", trying to set it to 0x%08x ", new);
+	rmmio_writel(new, addr);
+	msg_gspew("resulted in 0x%08x.\n", mmio_readl(addr));
 }
 
 static const struct spi_programmer spi_programmer_ich7 = {
@@ -1505,36 +1608,42 @@
 	.write_256 = default_spi_write_256,
 };
 
-static const struct spi_programmer spi_programmer_ich_hwseq = {
-	.type = SPI_CONTROLLER_ICH_HWSEQ,
+
+static const struct opaque_programmer opaque_programmer_ich_hwseq = {
 	.max_data_read = 64,
 	.max_data_write = 64,
-	.command = ich_hwseq_send_command,
-	.multicommand = default_spi_send_multicommand,
+	.probe = ich_hwseq_probe,
 	.read = ich_hwseq_read,
-	.write_256 = ich_hwseq_write_256,
+	.write = ich_hwseq_write,
+	.erase = ich_hwseq_block_erase,
 };
 
 int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
-			int ich_generation)
+		 enum ich_chipset ich_gen)
 {
 	int i;
 	uint8_t old, new;
 	uint16_t spibar_offset, tmp2;
 	uint32_t tmp;
-	int ichspi_desc = 0;
-	/* used for hw sequencing detection */
-	extern struct flash_descriptor fdbar;
+	char *arg;
+	int desc_valid = 0;
+	struct ich_descriptors desc = {{ 0 }};
+	enum ich_spi_mode {
+		ich_auto,
+		ich_hwseq,
+		ich_swseq
+	} ich_spi_mode = ich_auto;
+
+	ich_generation = ich_gen;
 
 	switch (ich_generation) {
-	case 7:
+	case CHIPSET_ICH_UNKNOWN:
+		return ERROR_FATAL;
+	case CHIPSET_ICH7:
+	case CHIPSET_ICH8:
 		spibar_offset = 0x3020;
 		break;
-	case 8:
-		spibar_offset = 0x3020;
-		break;
-	case 9:
-	case 10:
+	case CHIPSET_ICH9:
 	default:		/* Future version might behave the same */
 		spibar_offset = 0x3800;
 		break;
@@ -1546,13 +1655,8 @@
 	/* Assign Virtual Address */
 	ich_spibar = rcrb + spibar_offset;
 
-	if (target_bus != CHIP_BUSTYPE_SPI) {
-		msg_pdbg("Not targeting SPI, ignore the SPI init.\n");
-		return 0;
-	}
-
 	switch (ich_generation) {
-	case 7:
+	case CHIPSET_ICH7:
 		msg_pdbg("0x00: 0x%04x     (SPIS)\n",
 			     mmio_readw(ich_spibar + 0));
 		msg_pdbg("0x02: 0x%04x     (SPIC)\n",
@@ -1578,60 +1682,90 @@
 			     mmio_readl(ich_spibar + 0x58));
 		msg_pdbg("0x5c: 0x%08x (OPMENU+4)\n",
 			     mmio_readl(ich_spibar + 0x5c));
-		for (i = 0; i < 4; i++) {
+		for (i = 0; i < 3; i++) {
 			int offs;
 			offs = 0x60 + (i * 4);
 			msg_pdbg("0x%02x: 0x%08x (PBR%d)\n", offs,
 				     mmio_readl(ich_spibar + offs), i);
 		}
 		if (mmio_readw(ich_spibar) & (1 << 15)) {
-			msg_perr("WARNING: SPI Configuration Lockdown activated.\n");
+			msg_pdbg("WARNING: SPI Configuration Lockdown activated.\n");
 			ichspi_lock = 1;
 		}
-		register_spi_programmer(&spi_programmer_ich7);
 		ich_init_opcodes();
+		ich_set_bbar(0);
+		register_spi_programmer(&spi_programmer_ich7);
 		break;
-	case 8:
-	case 9:
-	case 10:
+	case CHIPSET_ICH8:
 	default:		/* Future version might behave the same */
+		arg = extract_programmer_param("ich_spi_mode");
+		if (arg && !strcmp(arg, "hwseq")) {
+			ich_spi_mode = ich_hwseq;
+			msg_pspew("user selected hwseq\n");
+		} else if (arg && !strcmp(arg, "swseq")) {
+			ich_spi_mode = ich_swseq;
+			msg_pspew("user selected swseq\n");
+		} else if (arg && !strcmp(arg, "auto")) {
+			msg_pspew("user selected auto\n");
+			ich_spi_mode = ich_auto;
+		} else if (arg && !strlen(arg)) {
+			msg_perr("Missing argument for ich_spi_mode.\n");
+			free(arg);
+			return ERROR_FATAL;
+		} else if (arg) {
+			msg_perr("Unknown argument for ich_spi_mode: %s\n",
+				 arg);
+			free(arg);
+			return ERROR_FATAL;
+		}
+		free(arg);
+
 		tmp2 = mmio_readw(ich_spibar + ICH9_REG_HSFS);
 		msg_pdbg("0x04: 0x%04x (HSFS)\n", tmp2);
 		prettyprint_ich9_reg_hsfs(tmp2);
 		if (tmp2 & HSFS_FLOCKDN) {
-			msg_pinfo("WARNING: SPI Configuration Lockdown activated.\n");
+			msg_pdbg("WARNING: SPI Configuration Lockdown activated.\n");
 			ichspi_lock = 1;
 		}
 		if (tmp2 & HSFS_FDV)
-			ichspi_desc = 1;
+			desc_valid = 1;
+		if (!(tmp2 & HSFS_FDOPSS) && desc_valid)
+			msg_pinfo("The Flash Descriptor Security Override "
+				  "Strap-Pin is set. Restrictions implied\n"
+				  "by the FRAP and FREG registers are NOT in "
+				  "effect. Please note that Protected\n"
+				  "Range (PR) restrictions still apply.\n");
+		ich_init_opcodes();
 
-		tmp2 = mmio_readw(ich_spibar + ICH9_REG_HSFC);
-		msg_pdbg("0x06: 0x%04x (HSFC)\n", tmp2);
-		prettyprint_ich9_reg_hsfc(tmp2);
+		if (desc_valid) {
+			num_fd_regions = DEFAULT_NUM_FD_REGIONS;
+			tmp2 = mmio_readw(ich_spibar + ICH9_REG_HSFC);
+			msg_pdbg("0x06: 0x%04x (HSFC)\n", tmp2);
+			prettyprint_ich9_reg_hsfc(tmp2);
+		}
 
 		tmp = mmio_readl(ich_spibar + ICH9_REG_FADDR);
 		msg_pdbg("0x08: 0x%08x (FADDR)\n", tmp);
-		tmp = mmio_readl(ich_spibar + ICH9_REG_FRAP);
-		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);
+		if (desc_valid) {
+			tmp = mmio_readl(ich_spibar + ICH9_REG_FRAP);
+			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));
 
-		msg_pdbg("0x74: 0x%08x (PR0)\n",
-			     mmio_readl(ich_spibar + ICH9_REG_PR0));
-		msg_pdbg("0x78: 0x%08x (PR1)\n",
-			     mmio_readl(ich_spibar + ICH9_REG_PR1));
-		msg_pdbg("0x7C: 0x%08x (PR2)\n",
-			     mmio_readl(ich_spibar + ICH9_REG_PR2));
-		msg_pdbg("0x80: 0x%08x (PR3)\n",
-			     mmio_readl(ich_spibar + ICH9_REG_PR3));
-		msg_pdbg("0x84: 0x%08x (PR4)\n",
-			     mmio_readl(ich_spibar + ICH9_REG_PR4));
+			/* Decode and print FREGx and FRAP registers */
+			for (i = 0; i < num_fd_regions; i++)
+				do_ich9_spi_frap(tmp, i);
+		}
+
+		/* try to disable PR locks before printing them */
+		if (!ichspi_lock)
+			for (i = 0; i < num_fd_regions; i++)
+				ich9_set_pr(i, 0, 0);
+		for (i = 0; i < num_fd_regions; i++)
+			prettyprint_ich9_reg_pr(i);
 
 		tmp = mmio_readl(ich_spibar + ICH9_REG_SSFS);
 		msg_pdbg("0x90: 0x%02x (SSFS)\n", tmp & 0xff);
@@ -1651,49 +1785,69 @@
 			     mmio_readl(ich_spibar + ICH9_REG_OPMENU));
 		msg_pdbg("0x9C: 0x%08x (OPMENU+4)\n",
 			     mmio_readl(ich_spibar + ICH9_REG_OPMENU + 4));
-		ichspi_bbar = mmio_readl(ich_spibar + ICH9_REG_BBAR);
-		msg_pdbg("0xA0: 0x%08x (BBAR)\n",
-			     ichspi_bbar);
+		if (ich_generation == CHIPSET_ICH8 && desc_valid) {
+			tmp = mmio_readl(ich_spibar + ICH8_REG_VSCC);
+			msg_pdbg("0xC1: 0x%08x (VSCC)\n", tmp);
+			msg_pdbg("VSCC: ");
+			prettyprint_ich_reg_vscc(tmp, MSG_DEBUG);
+		} else {
+			ichspi_bbar = mmio_readl(ich_spibar + ICH9_REG_BBAR);
+			msg_pdbg("0xA0: 0x%08x (BBAR)\n",
+				     ichspi_bbar);
 
-		tmp = mmio_readl(ich_spibar + ICH9_REG_LVSCC);
-		msg_pdbg("0xC4: 0x%08x (LVSCC)\n", tmp);
-		msg_pdbg("LVSCC: ");
-		prettyprint_ich9_reg_vscc(tmp);
+			if (desc_valid) {
+				tmp = mmio_readl(ich_spibar + ICH9_REG_LVSCC);
+				msg_pdbg("0xC4: 0x%08x (LVSCC)\n", tmp);
+				msg_pdbg("LVSCC: ");
+				prettyprint_ich_reg_vscc(tmp, MSG_DEBUG);
 
-		tmp = mmio_readl(ich_spibar + ICH9_REG_UVSCC);
-		msg_pdbg("0xC8: 0x%08x (UVSCC)\n", tmp);
-		msg_pdbg("UVSCC: ");
-		prettyprint_ich9_reg_vscc(tmp);
+				tmp = mmio_readl(ich_spibar + ICH9_REG_UVSCC);
+				msg_pdbg("0xC8: 0x%08x (UVSCC)\n", tmp);
+				msg_pdbg("UVSCC: ");
+				prettyprint_ich_reg_vscc(tmp, MSG_DEBUG);
 
-		tmp = mmio_readl(ich_spibar + ICH9_REG_FPB);
-		msg_pdbg("0xD0: 0x%08x (FPB)\n", tmp);
-
-		msg_pdbg("\n");
-		if (ichspi_desc) {
-			read_ich_descriptors_from_fdo(ich_spibar);
-			prettyprint_ich_descriptors(CHIPSET_UNKNOWN);
+				tmp = mmio_readl(ich_spibar + ICH9_REG_FPB);
+				msg_pdbg("0xD0: 0x%08x (FPB)\n", tmp);
+			}
+			ich_set_bbar(0);
 		}
 
-		/* FIXME: original code uses ichspi_lock to decide whether
-		 *        using ich_hwseq or not (if locked, go hwseq).
-		 *        But this is not exactly true. The lock-down doesn't
-		 *        mean we HAVE to use hwseq. So, removed the ichspi_lock
-		 *        from the if expression.
-		 *
-		 *        Note that the issue of hardware sequencing is the
-		 *        lack of support of flash status read so that breaks
-		 *        the --wp-status command.
-		 *        See chrome-os-partner:6594 for more details.
-		 *
-		 * TODO: implement an argopt to choose between software and
-		 *       hardware sequencing. By default, go software.
-		 *
-		 */
-		if (fdbar.NC != 0)
-			register_spi_programmer(&spi_programmer_ich_hwseq);
-		else {
+		msg_pdbg("\n");
+		if (desc_valid) {
+			if (read_ich_descriptors_via_fdo(ich_spibar, &desc) ==
+			    ICH_RET_OK)
+				prettyprint_ich_descriptors(CHIPSET_ICH_UNKNOWN,
+							    &desc);
+			/* If the descriptor is valid and indicates multiple
+			 * flash devices we need to use hwseq to be able to
+			 * access the second flash device.
+			 */
+			if (ich_spi_mode == ich_auto && desc.content.NC != 0) {
+				msg_pinfo("Enabling hardware sequencing due to "
+					  "multiple flash chips detected.\n");
+				ich_spi_mode = ich_hwseq;
+			}
+		}
+
+		if (ich_spi_mode == ich_auto && ichspi_lock &&
+		    ich_missing_opcodes()) {
+			msg_pinfo("Enabling hardware sequencing because "
+				  "some important opcode is locked.\n");
+			ich_spi_mode = ich_hwseq;
+		}
+
+		if (ich_spi_mode == ich_hwseq) {
+			if (!desc_valid) {
+				msg_perr("Hardware sequencing was requested "
+					 "but the flash descriptor is not "
+					 "valid. Aborting.\n");
+				return ERROR_FATAL;
+			}
+			hwseq_data.size_comp0 = getFCBA_component_density(&desc, 0);
+			hwseq_data.size_comp1 = getFCBA_component_density(&desc, 1);
+			register_opaque_programmer(&opaque_programmer_ich_hwseq);
+		} else {
 			register_spi_programmer(&spi_programmer_ich9);
-			ich_init_opcodes();
 		}
 		break;
 	}
@@ -1736,7 +1890,8 @@
 	ich_spibar = physmap("VT8237S MMIO registers", mmio_base, 0x70);
 
 	/* Not sure if it speaks all these bus protocols. */
-	buses_supported = CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH;
+	internal_buses_supported = BUS_LPC | BUS_FWH;
+	ich_generation = CHIPSET_ICH7;
 	register_spi_programmer(&spi_programmer_via);
 
 	msg_pdbg("0x00: 0x%04x     (SPIS)\n", mmio_readw(ich_spibar + 0));
@@ -1769,6 +1924,7 @@
 		ichspi_lock = 1;
 	}
 
+	ich_set_bbar(0);
 	ich_init_opcodes();
 
 	return 0;
diff --git a/internal.c b/internal.c
index 8ba9926..ac2fad6 100644
--- a/internal.c
+++ b/internal.c
@@ -37,7 +37,7 @@
 	return NULL;
 }
 
-struct pci_dev *pci_dev_find_vendorclass(uint16_t vendor, uint16_t class)
+struct pci_dev *pci_dev_find_vendorclass(uint16_t vendor, uint16_t devclass)
 {
 	struct pci_dev *temp;
 	struct pci_filter filter;
@@ -50,7 +50,7 @@
 		if (pci_filter_match(&filter, temp)) {
 			/* Read PCI class */
 			tmp2 = pci_read_word(temp, 0x0a);
-			if (tmp2 == class)
+			if (tmp2 == devclass)
 				return temp;
 		}
 
@@ -129,6 +129,19 @@
 int is_laptop = 0;
 int laptop_ok = 0;
 
+static const struct par_programmer par_programmer_internal = {
+		.chip_readb		= internal_chip_readb,
+		.chip_readw		= internal_chip_readw,
+		.chip_readl		= internal_chip_readl,
+		.chip_readn		= internal_chip_readn,
+		.chip_writeb		= internal_chip_writeb,
+		.chip_writew		= internal_chip_writew,
+		.chip_writel		= internal_chip_writel,
+		.chip_writen		= fallback_chip_writen,
+};
+
+enum chipbustype internal_buses_supported = BUS_NONE;
+
 static int internal_shutdown(void *data)
 {
 	release_io_perms();
@@ -186,6 +199,7 @@
 	int ret = 0;
 #endif
 	int force_laptop = 0;
+	int not_a_laptop = 0;
 	char *arg;
 	int probe_target_bus_later = 0;
 
@@ -218,9 +232,11 @@
 	free(arg);
 
 	arg = extract_programmer_param("laptop");
-	if (arg && !strcmp(arg,"force_I_want_a_brick")) {
+	if (arg && !strcmp(arg, "force_I_want_a_brick"))
 		force_laptop = 1;
-	} else if (arg && !strlen(arg)) {
+	else if (arg && !strcmp(arg, "this_is_not_a_laptop"))
+		not_a_laptop = 1;
+	else if (arg && !strlen(arg)) {
 		msg_perr("Missing argument for laptop.\n");
 		free(arg);
 		return 1;
@@ -234,17 +250,15 @@
 	arg = extract_programmer_param("bus");
 	if (arg) {
 		if (!strcasecmp(arg,"parallel")) {
-			target_bus = CHIP_BUSTYPE_PARALLEL;
+			target_bus = BUS_PARALLEL;
 		} else if (!strcasecmp(arg,"lpc")) {
-			target_bus = CHIP_BUSTYPE_LPC;
+			target_bus = BUS_LPC;
 		} else if (!strcasecmp(arg,"fwh")) {
-			target_bus = CHIP_BUSTYPE_FWH;
+			target_bus = BUS_FWH;
 		} else if (!strcasecmp(arg,"spi")) {
-			target_bus = CHIP_BUSTYPE_SPI;
+			target_bus = BUS_SPI;
 		} else {
-			msg_perr("Supported busses for %s programmer: parallel,"
-			         " lpc, fwh, spi\n",
-				 programmer_table[programmer].name);
+			msg_perr("Unsupported bus: %s\n", arg);
 			free(arg);
 			return 1;
 		}
@@ -260,9 +274,10 @@
 		return 1;
 
 	/* Default to Parallel/LPC/FWH flash devices. If a known host controller
-	 * is found, the init routine sets the buses_supported bitfield.
+	 * is found, the host controller init routine sets the
+	 * internal_buses_supported bitfield.
 	 */
-	buses_supported = CHIP_BUSTYPE_NONSPI;
+	internal_buses_supported = BUS_NONSPI;
 
 #if defined(__i386__) || defined(__x86_64__)
 	/* Initialize PCI access for flash enables */
@@ -294,8 +309,7 @@
 	if (probe_target_bus_later) {
 		/* read the target bus value from register. */
 		if (get_target_bus_from_chipset(&target_bus)) {
-			msg_perr("Cannot get target bus from %s programmer.\n",
-			         programmer_table[programmer].name);
+			msg_perr("Cannot get target bus from programmer.\n");
 			return 1;
 		}
 		msg_pdbg("get_target_bus_from_chipset() returns 0x%x.\n",
@@ -327,11 +341,19 @@
 
 	/* Warn if a non-whitelisted laptop is detected. */
 	if (is_laptop && !laptop_ok) {
-		msg_perr("========================================================================\n"
-			 "WARNING! You seem to be running flashrom on an unsupported laptop.\n"
-			 "Laptops, notebooks and netbooks are difficult to support and we recommend\n"
-			 "to use the vendor flashing utility. The embedded controller (EC) in these\n"
-			 "machines often interacts badly with flashing.\n"
+		msg_perr("========================================================================\n");
+		if (is_laptop == 1) {
+			msg_perr("WARNING! You seem to be running flashrom on an unsupported laptop.\n");
+		} else {
+			msg_perr("WARNING! You may be running flashrom on an unsupported laptop. We could\n"
+				 "not detect this for sure because your vendor has not setup the SMBIOS\n"
+				 "tables correctly. You can enforce execution by adding\n"
+				 "'-p internal:laptop=this_is_not_a_laptop' to the command line, but\n"
+				 "please read the following warning if you are not sure.\n\n");
+		}
+		msg_perr("Laptops, notebooks and netbooks are difficult to support and we\n"
+			 "recommend to use the vendor flashing utility. The embedded controller\n"
+			 "(EC) in these machines often interacts badly with flashing.\n"
 			 "See http://www.flashrom.org/Laptops for details.\n\n"
 			 "If flash is shared with the EC, erase is guaranteed to brick your laptop\n"
 			 "and write may brick your laptop.\n"
@@ -339,9 +361,9 @@
 			 "failure and sudden poweroff.\n"
 			 "You have been warned.\n"
 			 "========================================================================\n");
-		if (force_laptop) {
-			msg_perr("Proceeding anyway because user specified "
-				 "laptop=force_I_want_a_brick\n");
+
+		if (force_laptop || (not_a_laptop && (is_laptop == 2))) {
+			msg_perr("Proceeding anyway because user forced us to.\n");
 		} else {
 			msg_perr("Aborting.\n");
 			exit(1);
@@ -349,17 +371,18 @@
 	}
 
 #if __FLASHROM_LITTLE_ENDIAN__
-#if defined(__i386__) || defined(__x86_64__) || defined (__mips__)
+#if defined(__i386__) || defined(__x86_64__) || defined (__mips) || defined (__arm__)
 	/* try to enable it. Failure IS an option, since not all motherboards
 	 * really need this to be done, etc., etc.
 	 */
 	ret = chipset_flash_enable();
 	if (ret == -2) {
-		msg_perr("WARNING: No chipset found. Flash detection "
+		msg_pdbg("WARNING: No chipset found. Flash detection "
 			 "will most likely fail.\n");
-	}
-#endif
+	} else if (ret == ERROR_FATAL)
+		return ret;
 
+	register_par_programmer(&par_programmer_internal, internal_buses_supported);
 #if defined(__i386__) || defined(__x86_64__)
 	/* Probe unconditionally for IT87* LPC->SPI translation and for
 	 * IT87* Parallel write enable.
@@ -367,8 +390,8 @@
 	init_superio_ite();
 
 	/* probe for programmers that bridge LPC <--> SPI */
-	if (target_bus == CHIP_BUSTYPE_LPC ||
-	    target_bus == CHIP_BUSTYPE_FWH) {
+	if (target_bus == BUS_LPC ||
+	    target_bus == BUS_FWH) {
 		/* note: it85xx init done along with it87* init */
 		wpce775x_probe_spi_flash(NULL);
 		mec1308_probe_spi_flash(NULL);
@@ -382,7 +405,6 @@
 	 * 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__) || defined (__mips) || defined (__arm__)
 	return 0;
 #else
 	msg_perr("Your platform is not supported yet for the internal "
diff --git a/it85spi.c b/it85spi.c
index 31f3ffa..80a3d75 100644
--- a/it85spi.c
+++ b/it85spi.c
@@ -34,7 +34,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include "flash.h"
-#include "chipdrivers.h"
 #include "spi.h"
 #include "programmer.h"
 
@@ -88,20 +87,16 @@
 static int it85xx_scratch_rom_reenter = 0;
 
 /* This function will poll the keyboard status register until either
- *   an expected value shows up, or
- *   timeout reaches.
+ * an expected value shows up, or the timeout is reached.
+ * timeout is in usec.
  *
- * Returns: 0 -- the expected value has shown.
- *          1 -- timeout reached.
+ * Returns: 0 -- the expected value showed up.
+ *          1 -- timeout.
  */
-static int wait_for(
-		const unsigned int mask,
-		const unsigned int expected_value,
-		const int timeout,  /* in usec */
-		const char* error_message,
-		const char* function_name,
-		const int lineno
-) {
+static int wait_for(const unsigned int mask, const unsigned int expected_value,
+		    const int timeout, const char * error_message,
+		    const char * function_name, const int lineno)
+{
 	int time_passed;
 
 	for (time_passed = 0;; ++time_passed) {
@@ -116,21 +111,20 @@
 	return 1;
 }
 
-/* IT8502 employs a scratch ram when flash is being updated. Call the following
+/* IT8502 employs a scratch RAM when flash is being updated. Call the following
  * two functions before/after flash erase/program. */
-void it85xx_enter_scratch_rom()
+void it85xx_enter_scratch_rom(void)
 {
-	int ret;
-	int tries;
+	int ret, tries;
 
-	msg_pspew("%s():%d was called ...\n", __FUNCTION__, __LINE__);
-	if (it85xx_scratch_rom_reenter > 0) return;
-
+	msg_pdbg("%s():%d was called ...\n", __func__, __LINE__);
+	if (it85xx_scratch_rom_reenter > 0)
+		return;
 	for (tries = 0; tries < MAX_TRY; ++tries) {
 		/* Wait until IBF (input buffer) is not full. */
 		if (wait_for(KB_IBF, 0, MAX_TIMEOUT,
 		             "* timeout at waiting for IBF==0.\n",
-		             __FUNCTION__, __LINE__))
+		             __func__, __LINE__))
 			continue;
 
 		/* Copy EC firmware to SRAM. */
@@ -139,7 +133,7 @@
 		/* Confirm EC has taken away the command. */
 		if (wait_for(KB_IBF, 0, MAX_TIMEOUT,
 		             "* timeout at taking command.\n",
-		             __FUNCTION__, __LINE__))
+		             __func__, __LINE__))
 			continue;
 
 		/* Waiting for OBF (output buffer) has data.
@@ -147,12 +141,12 @@
 		 * ISR so that it is okay as long as the command is 0xFA. */
 		if (wait_for(KB_OBF, KB_OBF, MAX_TIMEOUT, NULL, NULL, 0))
 			msg_pdbg("%s():%d * timeout at waiting for OBF.\n",
-			         __FUNCTION__, __LINE__);
+			         __func__, __LINE__);
 		if ((ret = INB(LEGACY_KBC_PORT_DATA)) == 0xFA) {
 			break;
 		} else {
 			msg_perr("%s():%d * not run on SRAM ret=%d\n",
-			         __FUNCTION__, __LINE__, ret);
+			         __func__, __LINE__, ret);
 			continue;
 		}
 	}
@@ -160,25 +154,25 @@
 	if (tries < MAX_TRY) {
 		/* EC already runs on SRAM */
 		it85xx_scratch_rom_reenter++;
-		msg_pdbg("%s():%d * SUCCESS.\n", __FUNCTION__, __LINE__);
+		msg_pdbg("%s():%d * SUCCESS.\n", __func__, __LINE__);
 	} else {
-		msg_perr("%s():%d * Max try reached.\n",
-		         __FUNCTION__, __LINE__);
+		msg_perr("%s():%d * Max try reached.\n", __func__, __LINE__);
 	}
 }
 
-void it85xx_exit_scratch_rom()
+void it85xx_exit_scratch_rom(void)
 {
 	int tries;
 
-	msg_pdbg("%s():%d was called ...\n", __FUNCTION__, __LINE__);
-	if (it85xx_scratch_rom_reenter <= 0) return;
+	msg_pdbg("%s():%d was called ...\n", __func__, __LINE__);
+	if (it85xx_scratch_rom_reenter <= 0)
+		return;
 
 	for (tries = 0; tries < MAX_TRY; ++tries) {
 		/* Wait until IBF (input buffer) is not full. */
 		if (wait_for(KB_IBF, 0, MAX_TIMEOUT,
 		             "* timeout at waiting for IBF==0.\n",
-		             __FUNCTION__, __LINE__))
+		             __func__, __LINE__))
 			continue;
 
 		/* Exit SRAM. Run on flash. */
@@ -187,7 +181,7 @@
 		/* Confirm EC has taken away the command. */
 		if (wait_for(KB_IBF, 0, MAX_TIMEOUT,
 		             "* timeout at taking command.\n",
-		             __FUNCTION__, __LINE__)) {
+		             __func__, __LINE__)) {
 			/* We cannot ensure if EC has exited update mode.
 			 * If EC is in normal mode already, a further 0xFE
 			 * command will reboot system. So, exit loop here. */
@@ -200,10 +194,9 @@
 
 	if (tries < MAX_TRY) {
 		it85xx_scratch_rom_reenter = 0;
-		msg_pdbg("%s():%d * SUCCESS.\n", __FUNCTION__, __LINE__);
+		msg_pdbg("%s():%d * SUCCESS.\n", __func__, __LINE__);
 	} else {
-		msg_perr("%s():%d * Max try reached.\n",
-		         __FUNCTION__, __LINE__);
+		msg_perr("%s():%d * Max try reached.\n", __func__, __LINE__);
 	}
 }
 
@@ -226,7 +219,7 @@
 		return 1;
 
 #ifdef LPC_IO
-	/* Get LPCPNP of SHM. That's big-endian */
+	/* Get LPCPNP of SHM. That's big-endian. */
 	sio_write(s.port, LDNSEL, 0x0F); /* Set LDN to SHM (0x0F) */
 	shm_io_base = (sio_read(s.port, SHM_IO_BAR0) << 8) +
 	              sio_read(s.port, SHM_IO_BAR1);
@@ -236,8 +229,8 @@
 	/* These pointers are not used directly. They will be send to EC's
 	 * register for indirect access. */
 	base = 0xFFFFF000;
-	ce_high = ((unsigned char*)base) + 0xE00;  /* 0xFFFFFE00 */
-	ce_low = ((unsigned char*)base) + 0xD00;  /* 0xFFFFFD00 */
+	ce_high = ((unsigned char *)base) + 0xE00;  /* 0xFFFFFE00 */
+	ce_low = ((unsigned char *)base) + 0xD00;  /* 0xFFFFFD00 */
 
 	/* pre-set indirect-access registers since in most of cases they are
 	 * 0xFFFFxx00. */
@@ -246,12 +239,14 @@
 	INDIRECT_A3(shm_io_base, (base >> 24));
 #endif
 #ifdef LPC_MEMORY
-	base = (chipaddr)programmer_map_flash_region("it85 communication",
-						     0xFFFFF000, 0x1000);
+	/* FIXME: We should block accessing that region for anything else.
+	 * Major TODO here, and it will be a lot of work.
+	 */
+	base = (chipaddr)physmap("it85 communication", 0xFFFFF000, 0x1000);
 	msg_pdbg("%s():%d base=0x%08x\n", __func__, __LINE__,
 	         (unsigned int)base);
-	ce_high = (unsigned char*)(base + 0xE00);  /* 0xFFFFFE00 */
-	ce_low = (unsigned char*)(base + 0xD00);  /* 0xFFFFFD00 */
+	ce_high = (unsigned char *)(base + 0xE00);  /* 0xFFFFFE00 */
+	ce_low = (unsigned char *)(base + 0xD00);  /* 0xFFFFFD00 */
 #endif
 
 	return 0;
@@ -270,8 +265,9 @@
 	int i;
 
 	it85xx_enter_scratch_rom();
-	/* exit scratch rom ONLY when programmer shuts down. Otherwise, the
-	 * temporary flash state may halt EC. */
+	/* Exit scratch ROM ONLY when programmer shuts down. Otherwise, the
+	 * temporary flash state may halt the EC.
+	 */
 
 #ifdef LPC_IO
 	INDIRECT_A1(shm_io_base, (((unsigned long int)ce_high) >> 8) & 0xff);
@@ -322,19 +318,27 @@
 {
 	int ret;
 
-	if (!(buses_supported & CHIP_BUSTYPE_FWH)) {
+	if (!(internal_buses_supported & BUS_FWH)) {
 		msg_pdbg("%s():%d buses not support FWH\n", __func__, __LINE__);
 		return 1;
 	}
 	ret = it85xx_spi_common_init(s);
 	msg_pdbg("FWH: %s():%d ret=%d\n", __func__, __LINE__, ret);
 	if (!ret) {
-		msg_pdbg("%s():%d buses_supported=0x%x\n", __func__, __LINE__,
-		          buses_supported);
-		if (buses_supported & CHIP_BUSTYPE_FWH)
-			msg_pdbg("Overriding chipset SPI with IT85 FWH|SPI.\n");
-		/* Really leave FWH enabled? */
-		/* Set this as spi controller. */
+		msg_pdbg("%s: internal_buses_supported=0x%x\n", __func__,
+		          internal_buses_supported);
+		/* Check for FWH because IT85 listens to FWH cycles.
+		 * FIXME: The big question is whether FWH cycles are necessary
+		 * for communication even if LPC_IO is defined.
+		 */
+		if (internal_buses_supported & BUS_FWH)
+			msg_pdbg("Registering IT85 SPI.\n");
+		/* FIXME: Really leave FWH enabled? We can't use this region
+		 * anymore since accessing it would mess up IT85 communication.
+		 * If we decide to disable FWH for this region, we should print
+		 * a debug message about it.
+		 */
+		/* Set this as SPI controller. */
 		register_spi_programmer(&spi_programmer_it85xx);
 	}
 	return ret;
diff --git a/it87spi.c b/it87spi.c
index bb763af..edfa5b9 100644
--- a/it87spi.c
+++ b/it87spi.c
@@ -93,8 +93,7 @@
 		case 0x85:
 			msg_pdbg("Found ITE EC, ID 0x%04hx,"
 			         "Rev 0x%02x on port 0x%x.\n",
-			         s.model,
-			         sio_read(s.port, CHIP_VER_REG),
+			         s.model, sio_read(s.port, CHIP_VER_REG),
 			         s.port);
 			register_superio(s);
 			break;
@@ -106,17 +105,19 @@
 
 static int it8716f_spi_send_command(unsigned int writecnt, unsigned int readcnt,
 			const unsigned char *writearr, unsigned char *readarr);
-static int it8716f_spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len);
-static int it8716f_spi_chip_write_256(struct flashchip *flash, uint8_t *buf, int start, int len);
+static int it8716f_spi_chip_read(struct flashchip *flash, uint8_t *buf,
+				 unsigned int start, unsigned int len);
+static int it8716f_spi_chip_write_256(struct flashchip *flash, uint8_t *buf,
+				      unsigned int start, unsigned int len);
 
 static const struct spi_programmer spi_programmer_it87xx = {
-	.type = SPI_CONTROLLER_IT87XX,
-	.max_data_read = MAX_DATA_UNSPECIFIED,
-	.max_data_write = MAX_DATA_UNSPECIFIED,
-	.command = it8716f_spi_send_command,
-	.multicommand = default_spi_send_multicommand,
-	.read = it8716f_spi_chip_read,
-	.write_256 = it8716f_spi_chip_write_256,
+	.type		= SPI_CONTROLLER_IT87XX,
+	.max_data_read	= MAX_DATA_UNSPECIFIED,
+	.max_data_write	= MAX_DATA_UNSPECIFIED,
+	.command	= it8716f_spi_send_command,
+	.multicommand	= default_spi_send_multicommand,
+	.read		= it8716f_spi_chip_read,
+	.write_256	= it8716f_spi_chip_write_256,
 };
 
 static uint16_t it87spi_probe(uint16_t port)
@@ -128,10 +129,8 @@
 	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))) {
+	/* Check if LPC->SPI translation is active. */
+	if (!(tmp & 0x0e)) {
 		msg_pdbg("No IT87* serial flash segment enabled.\n");
 		exit_conf_mode_ite(port);
 		/* Nothing to do. */
@@ -193,7 +192,7 @@
 	free(portpos);
 	exit_conf_mode_ite(port);
 	it8716f_flashport = flashport;
-	if (buses_supported & CHIP_BUSTYPE_SPI)
+	if (internal_buses_supported & BUS_SPI)
 		msg_pdbg("Overriding chipset SPI with IT87 SPI.\n");
 	/* FIXME: Add the SPI bus or replace the other buses with it? */
 	register_spi_programmer(&spi_programmer_it87xx);
@@ -259,7 +258,7 @@
 	} while (busy);
 	if (readcnt > 3) {
 		msg_pinfo("%s called with unsupported readcnt %i.\n",
-		       __func__, readcnt);
+			  __func__, readcnt);
 		return SPI_INVALID_LENGTH;
 	}
 	switch (writecnt) {
@@ -289,7 +288,7 @@
 		break;
 	default:
 		msg_pinfo("%s called with unsupported writecnt %i.\n",
-		       __func__, writecnt);
+			  __func__, writecnt);
 		return SPI_INVALID_LENGTH;
 	}
 	/*
@@ -313,9 +312,10 @@
 }
 
 /* Page size is usually 256 bytes */
-static int it8716f_spi_page_program(struct flashchip *flash, uint8_t *buf, int start)
+static int it8716f_spi_page_program(struct flashchip *flash, uint8_t *buf,
+				    unsigned int start)
 {
-	int i;
+	unsigned int i;
 	int result;
 	chipaddr bios = flash->virtual_memory;
 
@@ -325,9 +325,8 @@
 	/* FIXME: The command below seems to be redundant or wrong. */
 	OUTB(0x06, it8716f_flashport + 1);
 	OUTB(((2 + (fast_spi ? 1 : 0)) << 4), it8716f_flashport);
-	for (i = 0; i < flash->page_size; i++) {
+	for (i = 0; i < flash->page_size; i++)
 		chip_writeb(buf[i], bios + start + i);
-	}
 	OUTB(0, it8716f_flashport);
 	/* Wait until the Write-In-Progress bit is cleared.
 	 * This usually takes 1-10 ms, so wait in 1 ms steps.
@@ -341,7 +340,8 @@
  * IT8716F only allows maximum of 512 kb SPI mapped to LPC memory cycles
  * Need to read this big flash using firmware cycles 3 byte at a time.
  */
-static int it8716f_spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+static int it8716f_spi_chip_read(struct flashchip *flash, uint8_t *buf,
+				 unsigned int start, unsigned int len)
 {
 	fast_spi = 0;
 
@@ -358,7 +358,8 @@
 	return 0;
 }
 
-static int it8716f_spi_chip_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
+static int it8716f_spi_chip_write_256(struct flashchip *flash, uint8_t *buf,
+				      unsigned int start, unsigned int len)
 {
 	/*
 	 * IT8716F only allows maximum of 512 kb SPI chip size for memory
@@ -374,7 +375,7 @@
 	    (flash->page_size > 256)) {
 		spi_chip_write_1(flash, buf, start, len);
 	} else {
-		int lenhere;
+		unsigned int lenhere;
 
 		if (start % flash->page_size) {
 			/* start to the end of the page or to start + len,
diff --git a/jedec.c b/jedec.c
index b96f5fd..8004863 100644
--- a/jedec.c
+++ b/jedec.c
@@ -23,7 +23,6 @@
  */
 
 #include "flash.h"
-#include "chipdrivers.h"
 
 #define MAX_REFLASH_TRIES 0x10
 #define MASK_FULL 0xffff
@@ -92,7 +91,7 @@
 		msg_cdbg("%s: excessive loops, i=0x%x\n", __func__, i);
 }
 
-static int getaddrmask(struct flashchip *flash)
+static unsigned int getaddrmask(struct flashchip *flash)
 {
 	switch (flash->feature_bits & FEATURE_ADDR_MASK) {
 	case FEATURE_ADDR_FULL:
@@ -356,12 +355,12 @@
 }
 
 /* chunksize is 1 */
-int write_jedec_1(struct flashchip *flash, uint8_t *src, int start, int len)
+int write_jedec_1(struct flashchip *flash, uint8_t *src, unsigned int start, unsigned int len)
 {
 	int i, failed = 0;
 	chipaddr dst = flash->virtual_memory + start;
 	chipaddr olddst;
-	int mask;
+	unsigned int mask;
 
 	mask = getaddrmask(flash);
 
@@ -377,14 +376,14 @@
 	return failed;
 }
 
-int write_page_write_jedec_common(struct flashchip *flash, uint8_t *src, int start, int page_size)
+int write_page_write_jedec_common(struct flashchip *flash, uint8_t *src, unsigned int start, unsigned int page_size)
 {
 	int i, tried = 0, failed;
 	uint8_t *s = src;
 	chipaddr bios = flash->virtual_memory;
 	chipaddr dst = bios + start;
 	chipaddr d = dst;
-	int mask;
+	unsigned int mask;
 
 	mask = getaddrmask(flash);
 
@@ -425,15 +424,15 @@
  * This function is a slightly modified copy of spi_write_chunked.
  * Each page is written separately in chunks with a maximum size of chunksize.
  */
-int write_jedec(struct flashchip *flash, uint8_t *buf, int start, int len)
+int write_jedec(struct flashchip *flash, uint8_t *buf, unsigned int start, int unsigned len)
 {
-	int i, starthere, lenhere;
+	unsigned int i, starthere, lenhere;
 	/* FIXME: page_size is the wrong variable. We need max_writechunk_size
 	 * in struct flashchip to do this properly. All chips using
 	 * write_jedec have page_size set to max_writechunk_size, so
 	 * we're OK for now.
 	 */
-	int page_size = flash->page_size;
+	unsigned int page_size = flash->page_size;
 
 	/* Warning: This loop has a very unusual condition and body.
 	 * The loop needs to go through each page with at least one affected
@@ -462,7 +461,7 @@
 int erase_chip_block_jedec(struct flashchip *flash, unsigned int addr,
 			   unsigned int blocksize)
 {
-	int mask;
+	unsigned int mask;
 
 	mask = getaddrmask(flash);
 	if ((addr != 0) || (blocksize != flash->total_size * 1024)) {
@@ -475,7 +474,7 @@
 
 int probe_jedec(struct flashchip *flash)
 {
-	int mask;
+	unsigned int mask;
 
 	mask = getaddrmask(flash);
 	return probe_jedec_common(flash, mask);
@@ -483,7 +482,7 @@
 
 int erase_sector_jedec(struct flashchip *flash, unsigned int page, unsigned int size)
 {
-	int mask;
+	unsigned int mask;
 
 	mask = getaddrmask(flash);
 	return erase_sector_jedec_common(flash, page, size, mask);
@@ -491,7 +490,7 @@
 
 int erase_block_jedec(struct flashchip *flash, unsigned int page, unsigned int size)
 {
-	int mask;
+	unsigned int mask;
 
 	mask = getaddrmask(flash);
 	return erase_block_jedec_common(flash, page, size, mask);
@@ -499,7 +498,7 @@
 
 int erase_chip_jedec(struct flashchip *flash)
 {
-	int mask;
+	unsigned int mask;
 
 	mask = getaddrmask(flash);
 	return erase_chip_jedec_common(flash, mask);
diff --git a/layout.c b/layout.c
index 093f080..c26ca70 100644
--- a/layout.c
+++ b/layout.c
@@ -471,8 +471,9 @@
 int handle_partial_read(
     struct flashchip *flash,
     uint8_t *buf,
-    int (*read) (struct flashchip *flash, uint8_t *buf, int start, int len),
-                 int write_to_file) {
+    int (*read) (struct flashchip *flash, uint8_t *buf,
+                 unsigned int start, unsigned int len),
+    int write_to_file) {
 
 	unsigned int start = 0;
 	int entry;
@@ -525,8 +526,8 @@
 int handle_partial_verify(
     struct flashchip *flash,
     uint8_t *buf,
-    int (*verify) (struct flashchip *flash, uint8_t *buf, int start, int len,
-                   const char* message)) {
+    int (*verify) (struct flashchip *flash, uint8_t *buf,
+                   unsigned int start, unsigned int len, const char *message)) {
 	unsigned int start = 0;
 	int entry;
 	unsigned int size = flash->total_size * 1024;
diff --git a/linux_spi.c b/linux_spi.c
new file mode 100644
index 0000000..44a2fcd
--- /dev/null
+++ b/linux_spi.c
@@ -0,0 +1,144 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2011 Sven Schnelle <svens@stackframe.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <linux/spi/spidev.h>
+#include <sys/ioctl.h>
+#include "flash.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+#include "spi.h"
+
+static int fd = -1;
+
+static int linux_spi_shutdown(void *data);
+static int linux_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+			const unsigned char *txbuf, unsigned char *rxbuf);
+static int linux_spi_read(struct flashchip *flash, uint8_t *buf,
+			  unsigned int start, unsigned int len);
+static int linux_spi_write_256(struct flashchip *flash, uint8_t *buf,
+			       unsigned int start, unsigned int len);
+
+static const struct spi_programmer spi_programmer_linux = {
+	.type		= SPI_CONTROLLER_LINUX,
+	.max_data_read	= MAX_DATA_UNSPECIFIED, /* TODO? */
+	.max_data_write	= MAX_DATA_UNSPECIFIED, /* TODO? */
+	.command	= linux_spi_send_command,
+	.multicommand	= default_spi_send_multicommand,
+	.read		= linux_spi_read,
+	.write_256	= linux_spi_write_256,
+};
+
+int linux_spi_init(void)
+{
+	char *p, *endp, *dev;
+	uint32_t speed = 0;
+
+	dev = extract_programmer_param("dev");
+	if (!dev || !strlen(dev)) {
+		msg_perr("No SPI device given. Use flashrom -p "
+			 "linux_spi:dev=/dev/spidevX.Y\n");
+		return 1;
+	}
+
+	p = extract_programmer_param("speed");
+	if (p && strlen(p)) {
+		speed = (uint32_t)strtoul(p, &endp, 10) * 1024;
+		if (p == endp) {
+			msg_perr("%s: invalid clock: %s kHz\n", __func__, p);
+			return 1;
+		}
+	}
+
+	msg_pdbg("Using device %s\n", dev);
+	if ((fd = open(dev, O_RDWR)) == -1) {
+		msg_perr("%s: failed to open %s: %s\n", __func__,
+			 dev, strerror(errno));
+		return 1;
+	}
+
+	if (speed > 0) {
+		if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) == -1) {
+			msg_perr("%s: failed to set speed %dHz: %s\n",
+				 __func__, speed, strerror(errno));
+			close(fd);
+			return 1;
+		}
+
+		msg_pdbg("Using %d kHz clock\n", speed);
+	}
+
+	if (register_shutdown(linux_spi_shutdown, NULL))
+		return 1;
+
+	register_spi_programmer(&spi_programmer_linux);
+
+	return 0;
+}
+
+static int linux_spi_shutdown(void *data)
+{
+	if (fd != -1) {
+		close(fd);
+		fd = -1;
+	}
+	return 0;
+}
+
+static int linux_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+			const unsigned char *txbuf, unsigned char *rxbuf)
+{
+	struct spi_ioc_transfer msg[2] = {
+		{
+			.tx_buf = (uint64_t)(ptrdiff_t)txbuf,
+			.len = writecnt,
+		},
+		{
+			.rx_buf = (uint64_t)(ptrdiff_t)rxbuf,
+			.len = readcnt,
+		},
+	};
+
+	if (fd == -1)
+		return -1;
+
+	if (ioctl(fd, SPI_IOC_MESSAGE(2), msg) == -1) {
+		msg_cerr("%s: ioctl: %s\n", __func__, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+static int linux_spi_read(struct flashchip *flash, uint8_t *buf,
+			  unsigned int start, unsigned int len)
+{
+	return spi_read_chunked(flash, buf, start, len, (unsigned)getpagesize());
+}
+
+static int linux_spi_write_256(struct flashchip *flash, uint8_t *buf,
+			       unsigned int start, unsigned int len)
+{
+	return spi_write_chunked(flash, buf, start, len, ((unsigned)getpagesize()) - 4);
+}
diff --git a/m29f400bt.c b/m29f400bt.c
index e0d41d4..61bfa2e 100644
--- a/m29f400bt.c
+++ b/m29f400bt.c
@@ -28,7 +28,7 @@
    functions. */
 
 /* chunksize is 1 */
-int write_m29f400bt(struct flashchip *flash, uint8_t *src, int start, int len)
+int write_m29f400bt(struct flashchip *flash, uint8_t *src, unsigned int start, unsigned int len)
 {
 	int i;
 	chipaddr bios = flash->virtual_memory;
diff --git a/mec1308.c b/mec1308.c
index 1848a62..32fec8d 100644
--- a/mec1308.c
+++ b/mec1308.c
@@ -329,6 +329,65 @@
 	return 0;
 }
 
+int mec1308_spi_read(struct flashchip *flash, uint8_t * buf, int start, int len)
+{
+	return spi_read_chunked(flash, buf, start, len, flash->page_size);
+}
+
+int mec1308_spi_write_256(struct flashchip *flash,
+                          uint8_t *buf, int start, int len)
+{
+	return spi_write_chunked(flash, buf, start, len, flash->page_size);
+}
+
+static int mec1308_chip_select(void)
+{
+	return mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_CS_EN);
+}
+
+static int mec1308_chip_deselect(void)
+{
+	return mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_CS_DIS);
+}
+
+/*
+ * MEC1308 will not allow direct access to SPI chip from host if EC is
+ * connected to LPC bus. This function will forward commands issued thru
+ * mailbox interface to the SPI flash chip.
+ */
+int mec1308_spi_send_command(unsigned int writecnt,
+                             unsigned int readcnt,
+                             const unsigned char *writearr,
+                             unsigned char *readarr)
+{
+	int i, rc = 0;
+
+	if (mec1308_chip_select())
+		return 1;
+
+	for (i = 0; i < writecnt; i++) {
+		if (mbx_write(MEC1308_MBX_DATA_START, writearr[i]) ||
+		    mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_SEND)) {
+			msg_pdbg("%s: failed to issue send command\n",__func__);
+			rc = 1;
+			goto mec1308_spi_send_command_exit;
+		}
+	}
+
+	for (i = 0; i < readcnt; i++) {
+		if (mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_READ)) {
+			msg_pdbg("%s: failed to issue read command\n",__func__);
+			rc = 1;
+			goto mec1308_spi_send_command_exit;
+		}
+		readarr[i] = mbx_read(MEC1308_MBX_DATA_START);
+	}
+
+mec1308_spi_send_command_exit:
+	rc |= mec1308_chip_deselect();
+	return rc;
+}
+
 static const struct spi_programmer spi_programmer_mec1308 = {
 	.type = SPI_CONTROLLER_MEC1308,
 	.max_data_read = 256,	/* FIXME: should be MAX_DATA_READ_UNLIMITED? */
@@ -421,63 +480,4 @@
 	msg_pdbg("%s(): successfully initialized mec1308\n", __func__);
 	return 0;
 }
-
-int mec1308_spi_read(struct flashchip *flash, uint8_t * buf, int start, int len)
-{
-	return spi_read_chunked(flash, buf, start, len, flash->page_size);
-}
-
-int mec1308_spi_write_256(struct flashchip *flash,
-                          uint8_t *buf, int start, int len)
-{
-	return spi_write_chunked(flash, buf, start, len, flash->page_size);
-}
-
-static int mec1308_chip_select(void)
-{
-	return mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_CS_EN);
-}
-
-static int mec1308_chip_deselect(void)
-{
-	return mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_CS_DIS);
-}
-
-/*
- * MEC1308 will not allow direct access to SPI chip from host if EC is
- * connected to LPC bus. This function will forward commands issued thru
- * mailbox interface to the SPI flash chip.
- */
-int mec1308_spi_send_command(unsigned int writecnt,
-                             unsigned int readcnt,
-                             const unsigned char *writearr,
-                             unsigned char *readarr)
-{
-	int i, rc = 0;
-
-	if (mec1308_chip_select())
-		return 1;
-
-	for (i = 0; i < writecnt; i++) {
-		if (mbx_write(MEC1308_MBX_DATA_START, writearr[i]) ||
-		    mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_SEND)) {
-			msg_pdbg("%s: failed to issue send command\n",__func__);
-			rc = 1;
-			goto mec1308_spi_send_command_exit;
-		}
-	}
-
-	for (i = 0; i < readcnt; i++) {
-		if (mbx_write(MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_READ)) {
-			msg_pdbg("%s: failed to issue read command\n",__func__);
-			rc = 1;
-			goto mec1308_spi_send_command_exit;
-		}
-		readarr[i] = mbx_read(MEC1308_MBX_DATA_START);
-	}
-
-mec1308_spi_send_command_exit:
-	rc |= mec1308_chip_deselect();
-	return rc;
-}
 #endif
diff --git a/nic3com.c b/nic3com.c
index bcc63e0..0c27957 100644
--- a/nic3com.c
+++ b/nic3com.c
@@ -55,6 +55,17 @@
 	{},
 };
 
+static const struct par_programmer par_programmer_nic3com = {
+		.chip_readb		= nic3com_chip_readb,
+		.chip_readw		= fallback_chip_readw,
+		.chip_readl		= fallback_chip_readl,
+		.chip_readn		= fallback_chip_readn,
+		.chip_writeb		= nic3com_chip_writeb,
+		.chip_writew		= fallback_chip_writew,
+		.chip_writel		= fallback_chip_writel,
+		.chip_writen		= fallback_chip_writen,
+};
+
 static int nic3com_shutdown(void *data)
 {
 	/* 3COM 3C90xB cards need a special fixup. */
@@ -96,11 +107,12 @@
 	 */
 	OUTW(SELECT_REG_WINDOW + 0, io_base_addr + INT_STATUS);
 
-	buses_supported = CHIP_BUSTYPE_PARALLEL;
-	max_rom_decode.parallel = 128 * 1024;
-
 	if (register_shutdown(nic3com_shutdown, NULL))
 		return 1;
+
+	max_rom_decode.parallel = 128 * 1024;
+	register_par_programmer(&par_programmer_nic3com, BUS_PARALLEL);
+
 	return 0;
 }
 
diff --git a/nicintel.c b/nicintel.c
index 2e6e46a..b20f856 100644
--- a/nicintel.c
+++ b/nicintel.c
@@ -28,7 +28,7 @@
 
 const struct pcidev_status nics_intel[] = {
 	{PCI_VENDOR_ID_INTEL, 0x1209, NT, "Intel", "8255xER/82551IT Fast Ethernet Controller"},
-	{PCI_VENDOR_ID_INTEL, 0x1229, NT, "Intel", "82557/8/9/0/1 Ethernet Pro 100"},
+	{PCI_VENDOR_ID_INTEL, 0x1229, OK, "Intel", "82557/8/9/0/1 Ethernet Pro 100"},
 
 	{},
 };
@@ -43,6 +43,17 @@
 
 #define CSR_FCR 0x0c
 
+static const struct par_programmer par_programmer_nicintel = {
+		.chip_readb		= nicintel_chip_readb,
+		.chip_readw		= fallback_chip_readw,
+		.chip_readl		= fallback_chip_readl,
+		.chip_readn		= fallback_chip_readn,
+		.chip_writeb		= nicintel_chip_writeb,
+		.chip_writew		= fallback_chip_writew,
+		.chip_writel		= fallback_chip_writel,
+		.chip_writen		= fallback_chip_writen,
+};
+
 static int nicintel_shutdown(void *data)
 {
 	physunmap(nicintel_control_bar, NICINTEL_CONTROL_MEMMAP_SIZE);
@@ -93,9 +104,8 @@
 	 */
 	pci_rmmio_writew(0x0001, nicintel_control_bar + CSR_FCR);
 
-	buses_supported = CHIP_BUSTYPE_PARALLEL;
-
 	max_rom_decode.parallel = NICINTEL_MEMMAP_SIZE;
+	register_par_programmer(&par_programmer_nicintel, BUS_PARALLEL);
 
 	return 0;
 
diff --git a/nicnatsemi.c b/nicnatsemi.c
index ac37cf0..f9825fd 100644
--- a/nicnatsemi.c
+++ b/nicnatsemi.c
@@ -35,6 +35,17 @@
 	{},
 };
 
+static const struct par_programmer par_programmer_nicnatsemi = {
+		.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,
+};
+
 static int nicnatsemi_shutdown(void *data)
 {
 	pci_cleanup(pacc);
@@ -48,7 +59,8 @@
 
 	io_base_addr = pcidev_init(PCI_BASE_ADDRESS_0, nics_natsemi);
 
-	buses_supported = CHIP_BUSTYPE_PARALLEL;
+	if (register_shutdown(nicnatsemi_shutdown, NULL))
+		return 1;
 
 	/* 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
@@ -57,9 +69,8 @@
 	 * functions below wants to be 0x0000FFFF.
 	 */
 	max_rom_decode.parallel = 131072;
+	register_par_programmer(&par_programmer_nicnatsemi, BUS_PARALLEL);
 
-	if (register_shutdown(nicnatsemi_shutdown, NULL))
-		return 1;
 	return 0;
 }
 
diff --git a/nicrealtek.c b/nicrealtek.c
index 4566e50..9f9265f 100644
--- a/nicrealtek.c
+++ b/nicrealtek.c
@@ -36,6 +36,17 @@
 	{},
 };
 
+static const struct par_programmer par_programmer_nicrealtek = {
+		.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,
+};
+
 static int nicrealtek_shutdown(void *data)
 {
 	/* FIXME: We forgot to disable software access again. */
@@ -50,10 +61,11 @@
 
 	io_base_addr = pcidev_init(PCI_BASE_ADDRESS_0, nics_realtek);
 
-	buses_supported = CHIP_BUSTYPE_PARALLEL;
-
 	if (register_shutdown(nicrealtek_shutdown, NULL))
 		return 1;
+
+	register_par_programmer(&par_programmer_nicrealtek, BUS_PARALLEL);
+
 	return 0;
 }
 
diff --git a/opaque.c b/opaque.c
new file mode 100644
index 0000000..1c31612
--- /dev/null
+++ b/opaque.c
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2011 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 opaque programmer framework.
+ * An opaque programmer is a programmer which does not provide direct access
+ * to the flash chip and which abstracts all flash chip properties into a
+ * programmer specific interface.
+ */
+
+#include <stdint.h>
+#include "flash.h"
+#include "flashchips.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+
+const struct opaque_programmer opaque_programmer_none = {
+	.max_data_read = MAX_DATA_UNSPECIFIED,
+	.max_data_write = MAX_DATA_UNSPECIFIED,
+	.probe = NULL,
+	.read = NULL,
+	.write = NULL,
+	.erase = NULL,
+};
+
+const struct opaque_programmer *opaque_programmer = &opaque_programmer_none;
+
+int probe_opaque(struct flashchip *flash)
+{
+	if (!opaque_programmer->probe) {
+		msg_perr("%s called before register_opaque_programmer. "
+			 "Please report a bug at flashrom@flashrom.org\n",
+			 __func__);
+		return 0;
+	}
+
+	return opaque_programmer->probe(flash);
+}
+
+int read_opaque(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len)
+{
+	if (!opaque_programmer->read) {
+		msg_perr("%s called before register_opaque_programmer. "
+			 "Please report a bug at flashrom@flashrom.org\n",
+			 __func__);
+		return 1;
+	}
+	return opaque_programmer->read(flash, buf, start, len);
+}
+
+int write_opaque(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len)
+{
+	if (!opaque_programmer->write) {
+		msg_perr("%s called before register_opaque_programmer. "
+			 "Please report a bug at flashrom@flashrom.org\n",
+			 __func__);
+		return 1;
+	}
+	return opaque_programmer->write(flash, buf, start, len);
+}
+
+int erase_opaque(struct flashchip *flash, unsigned int blockaddr, unsigned int blocklen)
+{
+	if (!opaque_programmer->erase) {
+		msg_perr("%s called before register_opaque_programmer. "
+			 "Please report a bug at flashrom@flashrom.org\n",
+			 __func__);
+		return 1;
+	}
+	return opaque_programmer->erase(flash, blockaddr, blocklen);
+}
+
+void register_opaque_programmer(const struct opaque_programmer *pgm)
+{
+	if (!pgm->probe || !pgm->read || !pgm->write || !pgm->erase) {
+		msg_perr("%s called with one of probe/read/write/erase being "
+			 "NULL. Please report a bug at flashrom@flashrom.org\n",
+			 __func__);
+		return;
+	}
+	opaque_programmer = pgm;
+	buses_supported |= BUS_PROG;
+}
diff --git a/pcidev.c b/pcidev.c
index 1f9a5cc..e8b4dc1 100644
--- a/pcidev.c
+++ b/pcidev.c
@@ -247,9 +247,9 @@
 	msg_pinfo("PCI devices:\n");
 	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)" : "");
+		          devs[i].device_name, devs[i].vendor_id,
+		          devs[i].device_id,
+		          (devs[i].status == NT) ? " (untested)" : "");
 	}
 }
 
@@ -295,6 +295,10 @@
 {									\
 	struct undo_pci_write_data *undo_pci_write_data;		\
 	undo_pci_write_data = malloc(sizeof(struct undo_pci_write_data)); \
+	if (!undo_pci_write_data) {					\
+		msg_gerr("Out of memory!\n");				\
+		exit(1);						\
+	}								\
 	undo_pci_write_data->dev = *a;					\
 	undo_pci_write_data->reg = b;					\
 	undo_pci_write_data->type = pci_write_type_##c;			\
diff --git a/physmap.c b/physmap.c
index cb035a5..d92015d 100644
--- a/physmap.c
+++ b/physmap.c
@@ -43,16 +43,13 @@
 
 static void *map_first_meg(unsigned long phys_addr, size_t len)
 {
-
-	if (realmem_map) {
+	if (realmem_map)
 		return realmem_map + phys_addr;
-	}
 
 	realmem_map = valloc(1024 * 1024);
 
-	if (!realmem_map) {
+	if (!realmem_map)
 		return ERROR_PTR;
-	}
 
 	if (__djgpp_map_physical_memory(realmem_map, (1024 * 1024), 0)) {
 		free(realmem_map);
@@ -68,23 +65,21 @@
 	int ret;
 	__dpmi_meminfo mi;
 
-	/* enable 4GB limit on DS descriptor */
-	if (!__djgpp_nearptr_enable()) {
+	/* Enable 4GB limit on DS descriptor. */
+	if (!__djgpp_nearptr_enable())
 		return ERROR_PTR;
-	}
 
 	if ((phys_addr + len - 1) < (1024 * 1024)) {
-	/* we need to use another method to map first 1MB */
+		/* We need to use another method to map first 1MB. */
 		return map_first_meg(phys_addr, len);
 	}
 
 	mi.address = phys_addr;
 	mi.size = len;
-	ret =  __dpmi_physical_address_mapping (&mi);
+	ret = __dpmi_physical_address_mapping(&mi);
 
-	if (ret != 0) {
+	if (ret != 0)
 		return ERROR_PTR;
-	}
 
 	return (void *) mi.address + __djgpp_conventional_base;
 }
@@ -99,7 +94,8 @@
 	/* There is no known way to unmap the first 1 MB. The DPMI server will
 	 * do this for us on exit.
 	 */
-	if ((virt_addr >= realmem_map) && ((virt_addr + len) <= (realmem_map + (1024 * 1024)))) {
+	if ((virt_addr >= realmem_map) &&
+	    ((virt_addr + len) <= (realmem_map + (1024 * 1024)))) {
 		return;
 	}
 
@@ -114,7 +110,7 @@
 
 void *sys_physmap(unsigned long phys_addr, size_t len)
 {
-	return (void*)phys_to_virt(phys_addr);
+	return (void *)phys_to_virt(phys_addr);
 }
 
 #define sys_physmap_rw_uncached	sys_physmap
@@ -195,7 +191,8 @@
 	if (-1 == fd_mem_cached) {
 		/* Open the memory device CACHED. */
 		if (-1 == (fd_mem_cached = open(MEM_DEV, O_RDWR))) {
-			msg_perr("Critical error: open(" MEM_DEV "): %s", strerror(errno));
+			msg_perr("Critical error: open(" MEM_DEV "): %s",
+				 strerror(errno));
 			exit(2);
 		}
 	}
@@ -221,36 +218,37 @@
 #define PHYSMAP_RW	0
 #define PHYSMAP_RO	1
 
-static 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;
 
 	if (len == 0) {
 		msg_pspew("Not mapping %s, zero size at 0x%08lx.\n",
-			     descr, phys_addr);
+			  descr, phys_addr);
 		return ERROR_PTR;
 	}
-		
+
 	if ((getpagesize() - 1) & len) {
 		msg_perr("Mapping %s at 0x%08lx, unaligned size 0x%lx.\n",
-			descr, phys_addr, (unsigned long)len);
+			 descr, phys_addr, (unsigned long)len);
 	}
 
 	if ((getpagesize() - 1) & phys_addr) {
 		msg_perr("Mapping %s, 0x%lx bytes at unaligned 0x%08lx.\n",
-			descr, (unsigned long)len, phys_addr);
+			 descr, (unsigned long)len, phys_addr);
 	}
 
-	if (readonly) {
+	if (readonly)
 		virt_addr = sys_physmap_ro_cached(phys_addr, len);
-	} else {
+	else
 		virt_addr = sys_physmap_rw_uncached(phys_addr, len);
-	}
 
 	if (ERROR_PTR == virt_addr) {
 		if (NULL == descr)
 			descr = "memory";
-		msg_perr("Error accessing %s, 0x%lx bytes at 0x%08lx\n", descr, (unsigned long)len, phys_addr);
+		msg_perr("Error accessing %s, 0x%lx bytes at 0x%08lx\n", descr,
+			 (unsigned long)len, phys_addr);
 		perror(MEM_DEV " mmap failed");
 #ifdef __linux__
 		if (EINVAL == errno) {
@@ -262,8 +260,8 @@
 		}
 #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");
+			 "and reboot, or reboot into\n"
+			 "single user mode.\n");
 #endif
 		if (!mayfail)
 			exit(3);
@@ -274,12 +272,14 @@
 
 void *physmap(const char *descr, unsigned long phys_addr, size_t len)
 {
-	return physmap_common(descr, phys_addr, len, PHYSMAP_NOFAIL, PHYSMAP_RW);
+	return physmap_common(descr, phys_addr, len, PHYSMAP_NOFAIL,
+			      PHYSMAP_RW);
 }
 
 void *physmap_try_ro(const char *descr, unsigned long phys_addr, size_t len)
 {
-	return physmap_common(descr, phys_addr, len, PHYSMAP_MAYFAIL, PHYSMAP_RO);
+	return physmap_common(descr, phys_addr, len, PHYSMAP_MAYFAIL,
+			      PHYSMAP_RO);
 }
 
 #if defined(__i386__) || defined(__x86_64__)
@@ -288,7 +288,7 @@
 /*
  * Reading and writing to MSRs, however requires instructions rdmsr/wrmsr,
  * which are ring0 privileged instructions so only the kernel can do the
- * read/write.  This function, therefore, requires that the msr kernel module
+ * read/write. This function, therefore, requires that the msr kernel module
  * be loaded to access these instructions from user space using device
  * /dev/cpu/0/msr.
  */
@@ -309,7 +309,6 @@
 	if (read(fd_msr, buf, 8) == 8) {
 		msr.lo = buf[0];
 		msr.hi = buf[1];
-
 		return msr;
 	}
 
@@ -341,7 +340,7 @@
 		exit(1);
 	}
 
-	/* some MSRs must not be written */
+	/* Some MSRs must not be written. */
 	if (errno == EIO)
 		return -1;
 
@@ -379,7 +378,7 @@
 
 	close(fd_msr);
 
-	/* Clear MSR file descriptor */
+	/* Clear MSR file descriptor. */
 	fd_msr = -1;
 }
 #else
@@ -462,7 +461,7 @@
 
 	close(fd_msr);
 
-	/* Clear MSR file descriptor */
+	/* Clear MSR file descriptor. */
 	fd_msr = -1;
 }
 
diff --git a/pm49fl00x.c b/pm49fl00x.c
index 4136b17..13992a3 100644
--- a/pm49fl00x.c
+++ b/pm49fl00x.c
@@ -21,12 +21,11 @@
  */
 
 #include "flash.h"
-#include "chipdrivers.h"
 
-static void write_lockbits_49fl00x(chipaddr bios, int size,
-			    unsigned char bits, int block_size)
+static void write_lockbits_49fl00x(chipaddr bios, unsigned int size,
+			    unsigned char bits, unsigned int block_size)
 {
-	int i, left = size;
+	unsigned int i, left = size;
 
 	for (i = 0; left >= block_size; i++, left -= block_size) {
 		/* pm49fl002 */
diff --git a/print.c b/print.c
index 9957ae9..9a93948 100644
--- a/print.c
+++ b/print.c
@@ -23,7 +23,6 @@
 #include <string.h>
 #include <stdlib.h>
 #include "flash.h"
-#include "flashchips.h"
 #include "programmer.h"
 
 /*
@@ -33,24 +32,24 @@
 char *flashbuses_to_text(enum chipbustype bustype)
 {
 	char *ret = calloc(1, 1);
-	if (bustype == CHIP_BUSTYPE_UNKNOWN) {
-		ret = strcat_realloc(ret, "Unknown, ");
 	/*
 	 * FIXME: Once all chipsets and flash chips have been updated, NONSPI
 	 * will cease to exist and should be eliminated here as well.
 	 */
-	} else if (bustype == CHIP_BUSTYPE_NONSPI) {
+	if (bustype == BUS_NONSPI) {
 		ret = strcat_realloc(ret, "Non-SPI, ");
 	} else {
-		if (bustype & CHIP_BUSTYPE_PARALLEL)
+		if (bustype & BUS_PARALLEL)
 			ret = strcat_realloc(ret, "Parallel, ");
-		if (bustype & CHIP_BUSTYPE_LPC)
+		if (bustype & BUS_LPC)
 			ret = strcat_realloc(ret, "LPC, ");
-		if (bustype & CHIP_BUSTYPE_FWH)
+		if (bustype & BUS_FWH)
 			ret = strcat_realloc(ret, "FWH, ");
-		if (bustype & CHIP_BUSTYPE_SPI)
+		if (bustype & BUS_SPI)
 			ret = strcat_realloc(ret, "SPI, ");
-		if (bustype == CHIP_BUSTYPE_NONE)
+		if (bustype & BUS_PROG)
+			ret = strcat_realloc(ret, "Programmer-specific, ");
+		if (bustype == BUS_NONE)
 			ret = strcat_realloc(ret, "None, ");
 	}
 	/* Kill last comma. */
@@ -59,40 +58,68 @@
 	return ret;
 }
 
-#define POS_PRINT(x) do { pos += strlen(x); msg_ginfo(x); } while (0)
-
-static int digits(int n)
-{
-	int i;
-
-	if (!n)
-		return 1;
-
-	for (i = 0; n; ++i)
-		n /= 10;
-
-	return i;
-}
-
 static void print_supported_chips(void)
 {
-	int okcol = 0, pos = 0, i, chipcount = 0;
+	const char *delim = "/";
+	const int mintoklen = 5;
+	const int border = 2;
+	int i, chipcount = 0;
 	int maxvendorlen = strlen("Vendor") + 1;
 	int maxchiplen = strlen("Device") + 1;
+	int maxtypelen = strlen("Type") + 1;
 	const struct flashchip *f;
 	char *s;
+	char *tmpven, *tmpdev;
+	int tmpvenlen, tmpdevlen, curvenlen, curdevlen;
 
+	/* calculate maximum column widths and by iterating over all chips */
 	for (f = flashchips; f->name != NULL; f++) {
 		/* Ignore "unknown XXXX SPI chip" entries. */
 		if (!strncmp(f->name, "unknown", 7))
 			continue;
 		chipcount++;
-		maxvendorlen = max(maxvendorlen, strlen(f->vendor));
-		maxchiplen = max(maxchiplen, strlen(f->name));
+
+		/* Find maximum vendor length (respecting line splitting). */
+		tmpven = (char *)f->vendor;
+		do {
+			/* and take minimum token lengths into account */
+			tmpvenlen = 0;
+			do {
+				tmpvenlen += strcspn(tmpven, delim);
+				/* skip to the address after the first token */
+				tmpven += tmpvenlen;
+				if (tmpven[0] == '\0')
+					break;
+				tmpven++;
+			} while (tmpvenlen < mintoklen);
+			maxvendorlen = max(maxvendorlen, tmpvenlen);
+			if (tmpven[0] == '\0')
+				break;
+		} while (1);
+
+		/* same for device name */
+		tmpdev = (char *)f->name;
+		do {
+			tmpdevlen = 0;
+			do {
+				tmpdevlen += strcspn(tmpdev, delim);
+				tmpdev += tmpdevlen;
+				if (tmpdev[0] == '\0')
+					break;
+				tmpdev++;
+			} while (tmpdevlen < mintoklen);
+			maxchiplen = max(maxchiplen, tmpdevlen);
+			if (tmpdev[0] == '\0')
+				break;
+		} while (1);
+
+		s = flashbuses_to_text(f->bustype);
+		maxtypelen = max(maxtypelen, strlen(s));
+		free(s);
 	}
-	maxvendorlen++;
-	maxchiplen++;
-	okcol = maxvendorlen + maxchiplen;
+	maxvendorlen += border;
+	maxchiplen += border;
+	maxtypelen += border;
 
 	msg_ginfo("Supported flash chips (total: %d):\n\n", chipcount);
 	msg_ginfo("Vendor");
@@ -102,10 +129,35 @@
 	for (i = strlen("Device"); i < maxchiplen; i++)
 		msg_ginfo(" ");
 
-	msg_ginfo("Tested   Known    Size/kB:  Type:\n");
-	for (i = 0; i < okcol; i++)
+	msg_ginfo("Test");
+	for (i = 0; i < border; i++)
 		msg_ginfo(" ");
-	msg_ginfo("OK       Broken\n\n");
+	msg_ginfo("Known");
+	for (i = 0; i < border; i++)
+		msg_ginfo(" ");
+	msg_ginfo(" Size");
+	for (i = 0; i < border; i++)
+		msg_ginfo(" ");
+
+	msg_ginfo("Type");
+	for (i = strlen("Type"); i < maxtypelen; i++)
+		msg_ginfo(" ");
+	msg_gdbg("Voltage");
+	msg_ginfo("\n");
+
+	for (i = 0; i < maxvendorlen + maxchiplen; i++)
+		msg_ginfo(" ");
+	msg_ginfo("OK  ");
+	for (i = 0; i < border; i++)
+		msg_ginfo(" ");
+	msg_ginfo("Broken");
+	for (i = 0; i < border; i++)
+		msg_ginfo(" ");
+	msg_ginfo("[kB]");
+	for (i = 0; i < border + maxtypelen; i++)
+		msg_ginfo(" ");
+	msg_gdbg("range [V]");
+	msg_ginfo("\n\n");
 	msg_ginfo("(P = PROBE, R = READ, E = ERASE, W = WRITE)\n\n");
 
 	for (f = flashchips; f->name != NULL; f++) {
@@ -113,50 +165,156 @@
 		if (!strncmp(f->name, "unknown", 7))
 			continue;
 
-		msg_ginfo("%s", f->vendor);
-		for (i = strlen(f->vendor); i < maxvendorlen; i++)
-			msg_ginfo(" ");
-		msg_ginfo("%s", f->name);
-		for (i = strlen(f->name); i < maxchiplen; i++)
-			msg_ginfo(" ");
+		/* support for multiline vendor names:
+		 * - make a copy of the original vendor name
+		 * - use strok to put the first token in tmpven
+		 * - keep track of the length of all tokens on the current line
+		 *   for ' '-padding in curvenlen
+		 * - check if additional tokens should be printed on the current
+		 *   line
+		 * - after all other values are printed print the surplus tokens
+		 *   on fresh lines
+		 */
+		tmpven = malloc(strlen(f->vendor) + 1);
+		if (tmpven == NULL) {
+			msg_gerr("Out of memory!\n");
+			exit(1);
+		}
+		strcpy(tmpven, f->vendor);
 
-		pos = maxvendorlen + maxchiplen;
-		if ((f->tested & TEST_OK_MASK)) {
-			if ((f->tested & TEST_OK_PROBE))
-				POS_PRINT("P ");
-			if ((f->tested & TEST_OK_READ))
-				POS_PRINT("R ");
-			if ((f->tested & TEST_OK_ERASE))
-				POS_PRINT("E ");
-			if ((f->tested & TEST_OK_WRITE))
-				POS_PRINT("W ");
-		}
-		while (pos < okcol + 9) {
-			msg_ginfo(" ");
-			pos++;
-		}
-		if ((f->tested & TEST_BAD_MASK)) {
-			if ((f->tested & TEST_BAD_PROBE))
-				POS_PRINT("P ");
-			if ((f->tested & TEST_BAD_READ))
-				POS_PRINT("R ");
-			if ((f->tested & TEST_BAD_ERASE))
-				POS_PRINT("E ");
-			if ((f->tested & TEST_BAD_WRITE))
-				POS_PRINT("W ");
+		tmpven = strtok(tmpven, delim);
+		msg_ginfo("%s", tmpven);
+		curvenlen = strlen(tmpven);
+		while ((tmpven = strtok(NULL, delim)) != NULL) {
+			msg_ginfo("%s", delim);
+			curvenlen++;
+			tmpvenlen = strlen(tmpven);
+			if (tmpvenlen >= mintoklen)
+				break; /* big enough to be on its own line */
+			msg_ginfo("%s", tmpven);
+			curvenlen += tmpvenlen;
 		}
 
-		while (pos < okcol + 18) {
+		for (i = curvenlen; i < maxvendorlen; i++)
 			msg_ginfo(" ");
-			pos++;
+
+		/* support for multiline device names as above */
+		tmpdev = malloc(strlen(f->name) + 1);
+		if (tmpdev == NULL) {
+			msg_gerr("Out of memory!\n");
+			exit(1);
 		}
-		msg_ginfo("%d", f->total_size);
-		for (i = 0; i < 10 - digits(f->total_size); i++)
+		strcpy(tmpdev, f->name);
+
+		tmpdev = strtok(tmpdev, delim);
+		msg_ginfo("%s", tmpdev);
+		curdevlen = strlen(tmpdev);
+		while ((tmpdev = strtok(NULL, delim)) != NULL) {
+			msg_ginfo("%s", delim);
+			curdevlen++;
+			tmpdevlen = strlen(tmpdev);
+			if (tmpdevlen >= mintoklen)
+				break; /* big enough to be on its own line */
+			msg_ginfo("%s", tmpdev);
+			curdevlen += tmpdevlen;
+		}
+
+		for (i = curdevlen; i < maxchiplen; i++)
+			msg_ginfo(" ");
+
+		if ((f->tested & TEST_OK_PROBE))
+			msg_ginfo("P");
+		else
+			msg_ginfo(" ");
+		if ((f->tested & TEST_OK_READ))
+			msg_ginfo("R");
+		else
+			msg_ginfo(" ");
+		if ((f->tested & TEST_OK_ERASE))
+			msg_ginfo("E");
+		else
+			msg_ginfo(" ");
+		if ((f->tested & TEST_OK_WRITE))
+			msg_ginfo("W");
+		else
+			msg_ginfo(" ");
+		for (i = 0; i < border; i++)
+			msg_ginfo(" ");
+
+		if ((f->tested & TEST_BAD_PROBE))
+			msg_ginfo("P");
+		else
+			msg_ginfo(" ");
+		if ((f->tested & TEST_BAD_READ))
+			msg_ginfo("R");
+		else
+			msg_ginfo(" ");
+		if ((f->tested & TEST_BAD_ERASE))
+			msg_ginfo("E");
+		else
+			msg_ginfo(" ");
+		if ((f->tested & TEST_BAD_WRITE))
+			msg_ginfo("W");
+		else
+			msg_ginfo(" ");
+		for (i = 0; i < border + 1; i++)
+			msg_ginfo(" ");
+
+		msg_ginfo("%5d", f->total_size);
+		for (i = 0; i < border; i++)
 			msg_ginfo(" ");
 
 		s = flashbuses_to_text(f->bustype);
-		msg_ginfo("%s\n", s);
+		msg_ginfo("%s", s);
+		for (i = strlen(s); i < maxtypelen; i++)
+			msg_ginfo(" ");
 		free(s);
+
+		if (f->voltage.min == 0 && f->voltage.max == 0)
+			msg_gdbg("no info");
+		else
+			msg_gdbg("%0.02f;%0.02f",
+				 f->voltage.min/(double)1000,
+				 f->voltage.max/(double)1000);
+
+		/* print surplus vendor and device name tokens */
+		while (tmpven != NULL || tmpdev != NULL) {
+			msg_ginfo("\n");
+			if (tmpven != NULL){
+				msg_ginfo("%s", tmpven);
+				curvenlen = strlen(tmpven);
+				while ((tmpven = strtok(NULL, delim)) != NULL) {
+					msg_ginfo("%s", delim);
+					curvenlen++;
+					tmpvenlen = strlen(tmpven);
+					/* big enough to be on its own line */
+					if (tmpvenlen >= mintoklen)
+						break;
+					msg_ginfo("%s", tmpven);
+					curvenlen += tmpvenlen;
+				}
+			} else
+				curvenlen = 0;
+
+			for (i = curvenlen; i < maxvendorlen; i++)
+				msg_ginfo(" ");
+
+			if (tmpdev != NULL){
+				msg_ginfo("%s", tmpdev);
+				curdevlen = strlen(tmpdev);
+				while ((tmpdev = strtok(NULL, delim)) != NULL) {
+					msg_ginfo("%s", delim);
+					curdevlen++;
+					tmpdevlen = strlen(tmpdev);
+					/* big enough to be on its own line */
+					if (tmpdevlen >= mintoklen)
+						break;
+					msg_ginfo("%s", tmpdev);
+					curdevlen += tmpdevlen;
+				}
+			}
+		}
+		msg_ginfo("\n");
 	}
 }
 
@@ -204,7 +362,7 @@
 				   const char *devicetype)
 {
 	int i, boardcount_good = 0, boardcount_bad = 0;
-	const struct board_pciid_enable *e = board_pciid_enables;
+	const struct board_match *e = board_matches;
 	const struct board_info *b = boards;
 	int maxvendorlen = strlen("Vendor") + 1;
 	int maxboardlen = strlen("Board") + 1;
@@ -242,7 +400,7 @@
 			msg_ginfo(" ");
 		msg_ginfo((b->working) ? "OK      " : "BAD     ");
 
-		for (e = board_pciid_enables; e->vendor_name != NULL; e++) {
+		for (e = board_matches; e->vendor_name != NULL; e++) {
 			if (strcmp(e->vendor_name, b->vendor)
 			    || strcmp(e->board_name, b->name))
 				continue;
@@ -376,15 +534,16 @@
 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",	"AN-M2",		1, "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?DEFTITLE=Y&fMTYPE=Socket%20AM2&pMODEL_NAME=AN-M2", NULL),
+	B("abit",	"AV8",			1, "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?DEFTITLE=Y&fMTYPE=Socket%20939&pMODEL_NAME=AV8", 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",	"BM6",			1, "http://www.abit.com.tw/page/en/motherboard/motherboard_detail.php?pMODEL_NAME=BM6&fMTYPE=Socket%20370", 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",	"Fatal1ty F-I90HD",	1, "http://www.abit.com.tw/page/en/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",	"IP35 Pro",		1, "http://www.abit.com.tw/page/en/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@aol.com to flashrom@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",	"KN8 Ultra",		1, "http://www.abit.com.tw/page/en/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),
@@ -399,18 +558,20 @@
 	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",	"890GX Extreme3",	1, "http://www.asrock.com/mb/overview.asp?Model=890GX%20Extreme3", NULL),
-	B("ASRock",	"939A785GMH/128M",	1, "http://www.asrock.com/mb/overview.asp?Model=939A785GMH/128M&s=939", NULL),
+	B("ASRock",	"939A785GMH/128M",	1, "http://www.asrock.com/mb/overview.asp?Model=939A785GMH/128M", 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",	"A770CrossFire",	1, "http://www.asrock.com/mb/overview.asp?Model=A770CrossFire", NULL),
 	B("ASRock",	"ALiveNF6G-DVI",	1, "http://www.asrock.com/mb/overview.asp?Model=ALiveNF6G-DVI", NULL),
+	B("ASRock",	"ConRoeXFire-eSATA2",	1, "http://www.asrock.com/mb/overview.asp?model=conroexfire-esata2", NULL),
 	B("ASRock",	"K7S41",		1, "http://www.asrock.com/mb/overview.asp?Model=K7S41", NULL),
 	B("ASRock",	"K7S41GX",		1, "http://www.asrock.com/mb/overview.asp?Model=K7S41GX", 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",	"K7VT4A+",		0, "http://www.asrock.com/mb/overview.asp?Model=K7VT4A%2b", "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("ASRock",	"M3A790GXH/128M",	1, "http://www.asrock.com/mb/overview.asp?Model=M3A790GXH/128M", NULL),
+	B("ASRock",	"P4i65GV",		1, "http://www.asrock.com/mb/overview.asp?Model=P4i65GV", 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",	"A7N8X-VM/400",		1, "http://www.asus.com/Motherboards/AMD_Socket_A/A7N8XVM400/", NULL),
 	B("ASUS",	"A7V133",		1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/socka/kt133a/a7v133/", NULL),
 	B("ASUS",	"A7V333",		1, "ftp://ftp.asus.com.tw/pub/asus/mb/socka/kt333/a7v333/", NULL),
 	B("ASUS",	"A7V400-MX",		1, "http://www.asus.com/product.aspx?P_ID=hORgEHRBDLMfwAwx", NULL),
@@ -420,10 +581,11 @@
 	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",	"A8M2N-LA (NodusM3-GL8E)",	1, "http://h10010.www1.hp.com/ewfrf/wc/document?docname=c00757531&cc=us&dlc=en&lc=en", "This is an OEM board from HP, the HP name is NodusM3-GL8E."),
 	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 Deluxe",	0, NULL, "Untested board enable."),
 	B("ASUS",	"A8N-SLI Premium",	1, "http://www.asus.com/product.aspx?P_ID=nbulqxniNmzf0mH1", NULL),
 	B("ASUS",	"A8N-VM",		1, "http://www.asus.com/Motherboards/AMD_Socket_939/A8NVM/", NULL),
 	B("ASUS",	"A8N-VM CSM",		1, "http://www.asus.com/product.aspx?P_ID=JBqqlpj4cspbSa3s", NULL),
@@ -431,14 +593,19 @@
 	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",	"Crosshair II Formula",	1, "http://www.asus.com/product.aspx?P_ID=EIDxaW1Ln3YR9RA2", NULL),
+	B("ASUS",	"Crosshair IV Extreme",	1, "http://www.asus.com/product.aspx?P_ID=lt1ShF6xEn3rlLe7", NULL),
 	B("ASUS",	"E35M1-I DELUXE",	1, "http://www.asus.com/product.aspx?P_ID=9BmKhMwWCwqyl1lz", NULL),
+	B("ASUS",	"K8N",			1, "http://www.asus.com/product.aspx?P_ID=zigzffr75jWJ7j2y", NULL),
 	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",		1, NULL, 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-E SLI",		1, "http://www.asus.com/product.aspx?P_ID=NJ8fkR6ufRM9XvFC", NULL),
 	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),
@@ -447,10 +614,15 @@
 	B("ASUS",	"M3A",			1, "http://www.asus.com/product.aspx?P_ID=P48rppKk4jrc9pNd", NULL),
 	B("ASUS",	"M3A76-CM",		1, "http://www.asus.com/product.aspx?P_ID=aU8effdifLvraVze", NULL),
 	B("ASUS",	"M3A78-EM",		1, "http://www.asus.com/product.aspx?P_ID=KjpYqzmAd9vsTM2D", NULL),
+	B("ASUS",	"M3N78-VM",		1, "http://www.asus.com/product.aspx?P_ID=ovqEgLFRjnSClhSV", NULL),
 	B("ASUS",	"M4A78-EM",		1, "http://www.asus.com/product.aspx?P_ID=0KyowHKUFAQqH2DO", NULL),
+	B("ASUS",	"M4A785TD-V EVO",	1, "http://www.asus.com/product.aspx?P_ID=fcsXWSxnhzZE9rnR", NULL),
 	B("ASUS",	"M4A785TD-M EVO",	1, "http://www.asus.com/product.aspx?P_ID=QHbvGVB1mXmmD8qQ", NULL),
+	B("ASUS",	"M4A78LT-M LE",		1, "http://www.asus.com/product.aspx?P_ID=exJL00uovTJaDqxR", 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",	"M4A89GTD PRO",		1, "http://www.asus.com/product.aspx?P_ID=Gdf0vtpVf72LTYgs", NULL),
+	B("ASUS",	"M6Ne",			0, "http://www.asus.com/Product.aspx?P_ID=IbqN4JCxeRiep4WN", "Untested board enable."),
 	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@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@hermann-uwe.de>. May work now."),
 	B("ASUS",	"P2B",			1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/slot1/440bx/p2b/", NULL),
@@ -465,20 +637,35 @@
 	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",	"P4GV-LA (Guppy)",	1, NULL, 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",	"P4P800-VM",		1, NULL, NULL),
 	B("ASUS",	"P4SC-E",		1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/sock478/p4sc-e/", "Part of ASUS Terminator P4 533 barebone system"),
 	B("ASUS",	"P4SD-LA",		1, "http://h10025.www1.hp.com/ewfrf/wc/document?docname=c00022505", NULL),
+	B("ASUS",	"P4S533-X",		1, "ftp://ftp.asus.com.tw/pub/ASUS/mb/sock478/p4s533-x/", 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@uml12d.zq1.de> to flashrom@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",	"P5GDC Deluxe",         1, "http://www.asus.com/product.aspx?P_ID=AbeoopyNpI2TZixg", NULL),
+	B("ASUS",	"P5GD1 Pro",		1, "http://www.asus.com/product.aspx?P_ID=50M49xQh71EZOeM1", NULL),
+	B("ASUS",	"P5GD1-VM/S",		1, NULL, "This is an OEM board from FSC. Although flashrom supports it and can probably not distinguish it from the P5GD1-VM, please note that the P5GD1-VM BIOS does not support the FSC variants completely."),
+	B("ASUS",	"P5GD1(-VM)",		0, NULL, "Untested board enable."),
+	B("ASUS",	"P5GD2 Premium",	1, "http://www.asus.it/product.aspx?P_ID=lRKaz1Rb6Xb0OFM7", NULL),
+	B("ASUS",	"P5GDC Deluxe",		1, "http://www.asus.com/product.aspx?P_ID=AbeoopyNpI2TZixg", NULL),
+	B("ASUS",	"P5GDC-V Deluxe",	1, NULL, NULL),
+	B("ASUS",	"P5GD2/C variants",	0, NULL, "Untested board enable."),
 	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",	"P5LD2",		0, NULL, "Untested board enable."),
+	B("ASUS",	"P5LP-LE (Lithium-UL8E)", 1, "http://h10025.www1.hp.com/ewfrf/wc/document?docname=c00379616&tmp_task=prodinfoCategory&cc=us&dlc=en&lc=en&product=1159887", "This is an OEM board from HP."),
+	B("ASUS",	"P5LP-LE (Epson OEM)",	1, NULL, "This is an OEM board from Epson (e.g. Endeavor MT7700)."),
+	B("ASUS",	"P5LP-LE",		0, NULL, "This designation is used for OEM boards from HP, Epson and maybe others. The HP names vary and not all of them have been tested yet. Please report any success or failure, thanks."),
+	B("ASUS",	"P5N-E SLI",		0, "http://www.asus.com/product.aspx?P_ID=KyHOsOKWujC2QguJ", "Needs a board enable (http://patchwork.coreboot.org/patch/3298/)."),
+	B("ASUS",	"P5N-D",		1, "http://www.asus.com/Motherboards/Intel_Socket_775/P5ND/", NULL),
+	B("ASUS",	"P5N-E SLI",		0, "http://www.asus.com/Motherboards/Intel_Socket_775/P5NE_SLI/", "Untested board enable."),
 	B("ASUS",	"P5N32-E SLI",		1, "http://www.asus.com/product.aspx?P_ID=vBZLIBtPzYB2bLcb", 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),
@@ -487,8 +674,12 @@
 	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("ASUS",	"P7H57D-V EVO",		1, "http://www.asus.com/Motherboards/Intel_Socket_1156/P7H57DV_EVO/", NULL),
+	B("ASUS",	"P7H55-M LX",		0, NULL, "flashrom works correctly, but GbE LAN is nonworking (probably due to a missing/bogus MAC address; see http://www.flashrom.org/pipermail/flashrom/2011-July/007432.html and http://ubuntuforums.org/showthread.php?t=1534389 for a possible workaround)"),
+	B("ASUS",	"P8B-E/4L",		0, NULL, "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+	B("ASUS",	"P8B WS",		0, NULL, "Probing works (Winbond W25Q32, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
 	B("ASUS",	"Z8NA-D6C",		1, "http://www.asus.com/product.aspx?P_ID=k81cpN8uEB01BpQ6", NULL),
 	B("BCOM",	"WinNET100",		1, "http://www.coreboot.org/BCOM_WINNET100", "Used in the IGEL-316 thin client."),
+	B("Biostar",	"N68S3+",		1, NULL, NULL),
 	B("Biostar",	"M6TBA",		0, "ftp://ftp.biostar-usa.com/manuals/M6TBA/", "No public report found. Owned by Uwe Hermann <uwe@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),
@@ -503,19 +694,23 @@
 	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",	"P4M800PRO-M (V1.0A)",	1, "http://www.ecs.com.tw/ECSWebSite_2007/Products/ProductsDetail.aspx?CategoryID=1&DetailID=574&DetailName=Feature&MenuID=52&LanID=0", NULL),
+	B("Elitegroup", "P4VXMS (V1.0A)",	1, NULL, 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-8NPA7I",		1, "http://epox.com/product.asp?ID=EP-8NPA7I", NULL),
+	B("EPoX",	"EP-8NPA7I",		1, "http://www.epox.com/product.asp?ID=EP-8NPA7I", NULL),
+	B("EPoX",	"EP-9NPA7I",		1, "http://www.epox.com/product.asp?ID=EP-9NPA7I", 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("EVGA",	"132-CK-NF78",		1, "http://http://www.evga.com/articles/385.asp", NULL),
+	B("EVGA",	"132-CK-NF78",		1, "http://www.evga.com/articles/385.asp", NULL),
 	B("EVGA",	"270-WS-W555-A2 (Classified SR-2)", 1, "http://www.evga.com/products/moreInfo.asp?pn=270-WS-W555-A2", 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@hermann-uwe.de>. Seems the PCI subsystem IDs are identical with the Tekram P6Pro-A5. May work now."),
 	B("Foxconn",	"6150K8MD-8EKRSH",	1, "http://www.foxconnchannel.com/product/motherboards/detail_overview.aspx?id=en-us0000157", NULL),
 	B("Foxconn",	"A6VMX",		1, "http://www.foxconnchannel.com/product/motherboards/detail_overview.aspx?id=en-us0000346", NULL),
+	B("Foxconn",	"P4M800P7MA-RS2",	1, "http://www.foxconnchannel.com/Product/Motherboards/detail_overview.aspx?id=en-us0000138", NULL),
 	B("Freetech",	"P6F91i",		1, "http://web.archive.org/web/20010417035034/http://www.freetech.com/prod/P6F91i.html", 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),
@@ -524,14 +719,17 @@
 	B("GIGABYTE",	"GA-6IEM",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=1379", NULL),
 	B("GIGABYTE",	"GA-6VXE7+",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=2410", 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-7DXR",		1, "http://www.gigabyte.de/Products/Motherboard/Products_Spec.aspx?ProductID=1302", NULL),
+	B("GIGABYTE",	"GA-MA785GMT-UD2H (rev. 1.0)", 1, "http://www.gigabyte.com/products/product-page.aspx?pid=3156", NULL),
+	B("GIGABYTE",	"GA-770TA-UD3",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=3272", NULL),
+	B("GIGABYTE",	"GA-7DXR",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=1302", 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-8I945GZME-RH",	1, "http://www.gigabyte.com/products/product-page.aspx?pid=2304", NULL),
 	B("GIGABYTE",	"GA-8IP775",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=1830", NULL),
 	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-8SIMLH",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=1399", NULL),
+	B("GIGABYTE",	"GA-945PL-S3P (rev. 6.6)", 1, "http://www.gigabyte.com/products/product-page.aspx?pid=2541", NULL),
 	B("GIGABYTE",	"GA-965GM-S2 (rev. 2.0)", 1, "http://www.gigabyte.com/products/product-page.aspx?pid=2617", 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),
@@ -555,6 +753,9 @@
 	B("GIGABYTE",	"GA-MA790GP-DS4H",	1, "http://www.gigabyte.com/products/product-page.aspx?pid=2887", NULL),
 	B("GIGABYTE",	"GA-MA790XT-UD4P (rev. 1.0)", 1, "http://www.gigabyte.com/products/product-page.aspx?pid=3010", NULL),
 	B("GIGABYTE",	"GA-P55A-UD4 (rev. 1.0)", 1, "http://www.gigabyte.com/products/product-page.aspx?pid=3436", NULL),
+	B("GIGABYTE",	"GA-P67A-UD3P",		1, "http://www.gigabyte.com/products/product-page.aspx?pid=3649", NULL),
+	B("GIGABYTE",	"GA-X58A-UD7 (rev. 2.0)", 1, NULL, NULL),
+	B("GIGABYTE",	"GA-Z68MX-UD2H-B (rev. 1.3)", 1, "http://www.gigabyte.com/products/product-page.aspx?pid=3854", NULL),
 	B("HP",		"e-Vectra P2706T",	1, "http://h20000.www2.hp.com/bizsupport/TechSupport/Home.jsp?lang=en&cc=us&prodSeriesId=77515&prodTypeId=12454", 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),
@@ -567,57 +768,64 @@
 	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",	"D865GLC",		0, NULL, "ICH5 with BIOS lock enable, see http://paste.flashrom.org/view.php?id=775"),
 	B("Intel",	"DG45ID",		0, "http://www.intel.com/products/desktop/motherboards/dg45id/dg45id-overview.htm", "Probing works (Winbond W25x32, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME is locked."),
+	B("Intel",	"DH67CF",		0, NULL, "H67 with BIOS lock enable and locked ME region, see http://www.flashrom.org/pipermail/flashrom/2011-September/007789.html"),
 	B("Intel",	"EP80759",		1, NULL, NULL),
 	B("Intel",	"Foxhollow",		1, NULL, "Intel reference board."),
 	B("Intel",	"Greencity",		1, NULL, "Intel reference board."),
 	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("Jetway",	"J-7BXAN",		1, "http://www.jetway.com.tw/evisn/download/d7BXAS.htm", NULL),
+	B("Jetway",	"J7F4K1G5D-PB",		1, "http://www.jetway.com.tw/jw/ipcboard_view.asp?productid=282&proname=J7F4K1G5D", NULL),
 	B("Kontron",	"986LCD-M",		1, "http://de.kontron.com/products/boards+and+mezzanines/embedded+motherboards/miniitx+motherboards/986lcdmmitx.html", NULL),
 	B("Lanner",	"EM-8510C",		1, NULL, 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-6153",		1, "http://www.msi.com/product/mb/MS-6153.html", NULL),
 	B("MSI",	"MS-6156",		1, "http://uk.ts.fujitsu.com/rl/servicesupport/techsupport/boards/Motherboards/MicroStar/Ms6156/MS6156.htm", NULL),
-	B("MSI",	"MS-6163 (MS-6163 Pro)",1, "http://www.msi.com/index.php?func=proddesc&prod_no=340", 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@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-6391 (845 Pro4)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=293", 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-6163 (MS-6163 Pro)",1, "http://www.msi.com/product/mb/MS-6163-Pro.html", NULL),
+	B("MSI",	"MS-6178",		0, "http://www.msi.com/product/mb/MS-6178.html", "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@hermann-uwe.de>."),
+	B("MSI",	"MS-6330 (K7T Turbo)",	1, "http://www.msi.com/product/mb/K7T-Turbo.html", NULL),
+	B("MSI",	"MS-6391 (845 Pro4)",	1, "http://www.msi.com/product/mb/845-Pro4.html", NULL),
+	B("MSI",	"MS-6561 (745 Ultra)",	1, "http://www.msi.com/product/mb/745-Ultra.html", NULL),
 	B("MSI",	"MS-6566 (845 Ultra-C)",1, "http://www.msi.com/product/mb/845-Ultra-C.html", 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-6570 (K7N2)",	1, "http://www.msi.com/product/mb/K7N2.html", 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-6590 (KT4 Ultra)",	1, "http://www.msi.com/product/mb/KT4-Ultra.html", NULL),
+	B("MSI",	"MS-6702E (K8T Neo2-F)",1, "http://www.msi.com/product/mb/K8T-Neo2-F--FIR.html", NULL),
+	B("MSI",	"MS-6712 (KT4V)",	1, "http://www.msi.com/product/mb/KT4V---KT4V-L--v1-0-.html", NULL),
+	B("MSI",	"MS-6787 (P4MAM-V/P4MAM-L)", 1, "http://www.msi.com/service/search/?kw=6787&type=product", NULL),
+	B("MSI",	"MS-7005 (651M-L)",	1, "http://www.msi.com/product/mb/651M-L.html", NULL),
+	B("MSI",	"MS-7025 (K8N Neo2 Platinum)", 1, "http://www.msi.com/product/mb/K8N-Neo2-Platinum.html", 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-7061 (KM4M-V/KM4AM-V)", 1, "http://www.msi.com/service/search/?kw=7061&type=product", 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-7135 (K8N Neo3)",	1, "http://www.msi.com/product/mb/K8N-Neo3.html", NULL),
+	B("MSI",	"MS-7142 (K8MM-V)",	1, "http://www.msi.com/product/mb/K8MM-V.html", 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-7211 (PM8M3-V)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=234", 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@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-7207 (K8NGM2-L)",	1, "http://www.msi.com/product/mb/K8NGM2-FID--IL--L.html", NULL),
+	B("MSI",	"MS-7211 (PM8M3-V)",	1, "http://www.msi.com/product/mb/PM8M3-V.html", NULL),
+	B("MSI",	"MS-7236 (945PL Neo3)",	1, "http://www.msi.com/product/mb/945PL-Neo3.html", NULL),
+	B("MSI",	"MS-7253 (K9VGM-V)",	1, "http://www.msi.com/product/mb/K9VGM-V.html", NULL),
+	B("MSI",	"MS-7255 (P4M890M)",	1, "http://www.msi.com/product/mb/P4M890M-L-IL.html", NULL),
+	B("MSI",	"MS-7260 (K9N Neo PCB 1.0)", 0, "http://www.msi.com/product/mb/K9N-Neo--PCB-1-0-.html", "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@hermann-uwe.de>."),
+	B("MSI",	"MS-7312 (K9MM-V)",	1, "http://www.msi.com/product/mb/K9MM-V.html", NULL),
+	B("MSI",	"MS-7345 (P35 Neo2-FIR)", 1, "http://www.msi.com/product/mb/P35-Neo2-FR---FIR.html", NULL),
+	B("MSI",	"MS-7368 (K9AG Neo2-Digital)", 1, "http://www.msi.com/product/mb/K9AG-Neo2-Digital.html", NULL),
+	B("MSI",	"MS-7369 (K9N Neo V2)", 1, "http://www.msi.com/product/mb/K9N-Neo-V2.html", NULL),
+	B("MSI",	"MS-7376 (K9A2 Platinum V1)", 1, "http://www.msi.com/product/mb/K9A2-Platinum.html", NULL),
 	B("MSI",	"MS-7529 (G31M3-L(S) V2)", 1, "http://www.msi.com/product/mb/G31M3-L-V2---G31M3-LS-V2.html", NULL),
-	B("MSI",	"MS-7529 (G31TM-P21)",  1, "http://msi.com/product/mb/G31TM-P21.html", NULL),
-	B("MSI",	"MS-7596 (785GM-E51)",  1, "http://eu.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=1866", NULL),
+	B("MSI",	"MS-7529 (G31TM-P21)",  1, "http://www.msi.com/product/mb/G31TM-P21.html", NULL),
+	B("MSI",	"MS-7596 (785GM-E51)",  1, "http://www.msi.com/product/mb/785GM-E51.html", NULL),
 	B("MSI",	"MS-7599 (870-C45)",    1, "http://www.msi.com/product/mb/870-C45.html", NULL),
-	B("MSI",	"MS-7640 (890FXA-GD70)",1, "http://www.msi.com/product/mb/890FXA-GD70.html", "Reported by \"Linux User #330250\" http://flashrom.org/pipermail/flashrom/2011-March/006072.html"),
-	B("MSI",	"MS-7642 (890GXM-G65)",	1, "http://www.msi.com/index.php?func=proddesc&maincat_no=1&prod_no=2012", NULL),
+	B("MSI",	"MS-7640 (890FXA-GD70)",1, "http://www.msi.com/product/mb/890FXA-GD70.html", NULL),
+	B("MSI",	"MS-7642 (890GXM-G65)",	1, "http://www.msi.com/product/mb/890GXM-G65.html", NULL),
+	B("MSI",	"MS-7696 (A75MA-G55)",	1, "http://www.msi.com/product/mb/A75MA-G55.html", NULL),
 	B("MSI",	"MS-7698 (E350IA-E45)",	1, "http://www.msi.com/product/mb/E350IA-E45.html", NULL),
 	B("NEC",	"PowerMate 2000",	1, "http://support.necam.com/mobilesolutions/hardware/Desktops/pm2000/celeron/", NULL),
 	B("Nokia",	"IP530",		1, NULL, NULL),
+	B("PCCHIPS ",	"M598LMR (V9.0)",	1, NULL, NULL),
 	B("PCCHIPS ",	"M863G (V5.1A)",	1, "http://www.pcchips.com.tw/PCCWebSite/Products/ProductsDetail.aspx?CategoryID=1&DetailID=343&DetailName=Feature&MenuID=1&LanID=0", 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),
@@ -632,6 +840,7 @@
 	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",	"FH67",			1, "http://www.shuttle.eu/products/mini-pc/sh67h3/specification/", 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@hermann-uwe.de>. May work now."),
@@ -642,12 +851,15 @@
 	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("Sun",	"Ultra 40 M2",		1, "http://download.oracle.com/docs/cd/E19127-01/ultra40.ws/820-0123-13/intro.html", NULL),
 	B("Supermicro",	"H8QC8",		1, "http://www.supermicro.com/Aplus/motherboard/Opteron/nforce/H8QC8.cfm", NULL),
 	B("Supermicro", "X5DP8-G2",		1, "http://www.supermicro.com/products/motherboard/Xeon/E7501/X5DP8-G2.cfm", NULL),
+	B("Supermicro", "X7DBT-INF",		1, "http://www.supermicro.com/products/motherboard/Xeon1333/5000P/X7DBT-INF.cfm", NULL),
 	B("Supermicro", "X7SPA-HF",		1, "http://www.supermicro.com/products/motherboard/ATOM/ICH9/X7SPA.cfm?typ=H&IPMI=Y", NULL),
 	B("Supermicro", "X8DT3",		1, "http://www.supermicro.com/products/motherboard/QPI/5500/X8DT3.cfm", NULL),
 	B("Supermicro", "X8DTH-6F",		1, "http://www.supermicro.com/products/motherboard/QPI/5500/X8DTH-6F.cfm", NULL),
 	B("Supermicro",	"X8DTT-F",		1, "http://www.supermicro.com/products/motherboard/QPI/5500/X8DTT-F.cfm", NULL),
+	B("Supermicro",	"X8DTU-6TF+",		0, "http://www.supermicro.com/products/motherboard/QPI/5500/X8DTU_.cfm?TYP=SAS&LAN=10", "Probing works (Atmel AT25DF321A, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
 	B("Supermicro",	"X8DTU-F",		1, "http://www.supermicro.com/products/motherboard/QPI/5500/X8DTU-F.cfm", NULL),
 	B("Supermicro",	"X8SIE(-F)",		0, "http://www.supermicro.com/products/motherboard/Xeon3000/3400/X8SIE.cfm?IPMI=N&TYP=LN2", "Requires unlocking the ME although the registers are set up correctly by the descriptor/BIOS already (tested with swseq and hwseq)."),
 	B("Supermicro",	"X8STi",		1, "http://www.supermicro.com/products/motherboard/Xeon3000/X58/X8STi.cfm", NULL),
@@ -667,6 +879,7 @@
 	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",	"S2912 (Thunder n3600R)", 1, "http://www.tyan.com/product_board_detail.aspx?pid=157", NULL),
 	B("Tyan",	"S2915 (Thunder n6650W)", 1, "http://tyan.com/product_board_detail.aspx?pid=163", NULL),
 	B("Tyan",	"S2915-E (Thunder n6650W)", 1, "http://tyan.com/product_SKU_spec.aspx?ProductType=MB&pid=541&SKU=600000041", NULL),
 	B("Tyan",	"S2933 (Thunder n3600S)", 1, "http://tyan.com/product_SKU_spec.aspx?ProductType=MB&pid=478&SKU=600000063", NULL),
@@ -696,6 +909,8 @@
 	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",	"Fusion-ITX WiFi (FUSION350-A-E)", 1, NULL, NULL),
+	B("ZOTAC",	"GeForce 8200",		1, "http://pden.zotac.com/index.php?page=shop.product_details&product_id=129&category_id=92", 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
 
diff --git a/print_wiki.c b/print_wiki.c
index 6280aff..57a27d8 100644
--- a/print_wiki.c
+++ b/print_wiki.c
@@ -29,7 +29,8 @@
 
 static const char 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\
+background-color:#eeeeee; text-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";
@@ -37,12 +38,13 @@
 #if CONFIG_INTERNAL == 1
 static const char chipset_th[] = "{| border=\"0\" style=\"font-size: smaller\"\n\
 |- bgcolor=\"#6699dd\"\n! align=\"left\" | Vendor\n\
-! align=\"left\" | Southbridge\n! align=\"left\" | PCI IDs\n\
-! align=\"left\" | Status\n\n";
+! align=\"left\" | Southbridge\n! align=\"center\" | PCI IDs\n\
+! align=\"center\" | Status\n\n";
 
 static const char board_th[] = "{| border=\"0\" style=\"font-size: smaller\" \
 valign=\"top\"\n|- bgcolor=\"#6699dd\"\n! align=\"left\" | Vendor\n\
-! align=\"left\" | Mainboard\n! align=\"left\" | Required option\n! align=\"left\" | Status\n\n";
+! align=\"left\" | Mainboard\n! align=\"left\" | Required option\n\
+! align=\"center\" | Status\n\n";
 
 static const char board_intro[] = "\
 \n== Supported mainboards ==\n\n\
@@ -57,20 +59,26 @@
 further verified mainboards on the [[Mailinglist|mailing list]].\n";
 #endif
 
-static const char chip_th[] = "{| border=\"0\" style=\"font-size: smaller\" \
-valign=\"top\"\n|- bgcolor=\"#6699dd\"\n! align=\"left\" | Vendor\n\
-! align=\"left\" | Device\n! align=\"left\" | Size / kB\n\
-! align=\"left\" | Type\n! align=\"left\" colspan=\"4\" | Status\n\n\
+static const char chip_th[] = "{\
+| border=\"0\" style=\"font-size: smaller\" valign=\"top\"\n\
+|- bgcolor=\"#6699dd\"\n\
+! align=\"left\" | Vendor\n\
+! align=\"left\" | Device\n\
+! align=\"center\" | Size [kB]\n\
+! align=\"center\" | Type\n\
+! align=\"center\" colspan=\"4\" | Status\n\
+! align=\"center\" colspan=\"2\" | Voltage [V]\n\n\
 |- bgcolor=\"#6699ff\"\n| colspan=\"4\" | &nbsp;\n\
-| Probe\n| Read\n| Erase\n| Write\n\n";
+| Probe\n| Read\n| Erase\n| Write\n\
+| align=\"center\" | Min \n| align=\"center\" | Max\n\n";
 
 static const char programmer_section[] = "\
 \n== Supported programmers ==\n\nThis is a list \
 of supported PCI devices flashrom can use as programmer:\n\n{| border=\"0\" \
 valign=\"top\"\n| valign=\"top\"|\n\n{| border=\"0\" style=\"font-size: \
 smaller\" valign=\"top\"\n|- bgcolor=\"#6699dd\"\n! align=\"left\" | Vendor\n\
-! align=\"left\" | Device\n! align=\"left\" | PCI IDs\n\
-! align=\"left\" | Status\n\n";
+! align=\"left\" | Device\n! align=\"center\" | PCI IDs\n\
+! align=\"center\" | Status\n\n";
 
 #if CONFIG_INTERNAL == 1
 static const char laptop_intro[] = "\n== Supported laptops/notebooks ==\n\n\
@@ -125,7 +133,7 @@
 	int num_notes = 0;
 	char *notes = calloc(1, 1);
 	char tmp[900 + 1];
-	const struct board_pciid_enable *b = board_pciid_enables;
+	const struct board_match *b = board_matches;
 
 	for (i = 0; boards[i].vendor != NULL; i++) {
 		if (boards[i].working)
@@ -204,9 +212,15 @@
 	const struct flashchip *f, *old = NULL;
 	uint32_t t;
 	char *s;
+	char vmax[6];
+	char vmin[6];
 
-	for (f = flashchips; f->name != NULL; f++)
+	for (f = flashchips; f->name != NULL; f++) {
+		/* Don't count "unknown XXXX SPI chip" entries. */
+		if (!strncmp(f->name, "unknown", 7))
+			continue;
 		chipcount++;
+	}
 
 	printf("\n== Supported chips ==\n\nTotal amount of supported "
 	       "chips: '''%d'''\n\n{| border=\"0\" valign=\"top\"\n"
@@ -223,8 +237,13 @@
 
 		t = f->tested;
 		s = flashbuses_to_text(f->bustype);
-		printf("|- bgcolor=\"#%s\"\n| %s || %s || %d "
-		       "|| %s || {{%s}} || {{%s}} || {{%s}} || {{%s}}\n",
+		sprintf(vmin, "%0.03f", f->voltage.min / (double)1000);
+		sprintf(vmax, "%0.03f", f->voltage.max / (double)1000);
+		/* '{{%s}}' is used in combination with 'OK', 'No' and '?3' to
+		 * select special formatting templates for the bg color. */
+		printf("|- bgcolor=\"#%s\"\n| %s || %s || align=\"right\" | %d "
+		       "|| %s || {{%s}} || {{%s}} || {{%s}} || {{%s}}"
+		       "|| %s || %s \n",
 		       (c == 1) ? "eeeeee" : "dddddd", f->vendor, f->name,
 		       f->total_size, s,
 		       (t & TEST_OK_PROBE) ? "OK" :
@@ -234,7 +253,9 @@
 		       (t & TEST_OK_ERASE) ? "OK" :
 		       (t & TEST_BAD_ERASE) ? "No" : "?3",
 		       (t & TEST_OK_WRITE) ? "OK" :
-		       (t & TEST_BAD_WRITE) ? "No" : "?3");
+		       (t & TEST_BAD_WRITE) ? "No" : "?3",
+		       f->voltage.min ? vmin : "N/A",
+		       f->voltage.min ? vmax : "N/A");
 		free(s);
 
 		/* Split table into 'cols' columns. */
diff --git a/processor_enable.c b/processor_enable.c
index ddb094f..47d1a39 100644
--- a/processor_enable.c
+++ b/processor_enable.c
@@ -86,16 +86,29 @@
 #include <string.h>
 #include <ctype.h>
 
-/* Returns true if the /proc/cpuinfo contains a line: "CPU part *: *0xc09".
+/* Returns true if the /proc/cpuinfo indicates we're a tegra2.
+ *
+ * This means that it contains the two lines:
+ *   CPU part *: *0xc09
+ *   CPU variant *: *0x1
+ *
+ * The "variant" is important because we don't yet support Tegra3 and Tegra3
+ * identifies itself as variant 0x2.
+ *
  * TODO: need to extend in future for same SPI controller in chip family.
  */
 static int is_tegra2(void)
 {
 	FILE *cpuinfo;
 	uint32_t impl = 0, architecture = 0, variant = 0, part = 0;
-	const char *name = "CPU part";
-	const char *value = "0xc09";
-	int ret = 0;
+	const char *part_name = "CPU part";
+	const char *part_value = "0xc09";
+	const char *variant_name = "CPU variant";
+	const char *variant_value = "0x1";
+	int found_part = 0;
+	int found_variant = 0;
+	const char *cur_value = NULL;
+	int *cur_found = NULL;
 
 	cpuinfo = fopen("/proc/cpuinfo", "rb");
 	if (!cpuinfo)
@@ -107,8 +120,15 @@
 		ptr = line;
 		while (*ptr && isspace((unsigned char)*ptr))
 			ptr++;
-		if (strncmp(ptr, name, strlen(name)) == 0)
-			ptr += strlen(name);
+		if (strncmp(ptr, variant_name, strlen(variant_name)) == 0) {
+			ptr += strlen(variant_name);
+			cur_value = variant_value;
+			cur_found = &found_variant;
+		} else if (strncmp(ptr, part_name, strlen(part_name)) == 0) {
+			ptr += strlen(part_name);
+			cur_value = part_value;
+			cur_found = &found_part;
+		}
 		while (*ptr && isspace((unsigned char)*ptr))
 			ptr++;
 		if (*ptr != ':')
@@ -116,10 +136,13 @@
 		ptr++;
 		while (*ptr && isspace((unsigned char)*ptr))
 			ptr++;
-		ret = (strncmp(ptr, value, strlen(value)) == 0);
+		if (cur_found)
+			*cur_found = (strncmp(ptr, cur_value,
+					      strlen(cur_value)) == 0);
+		cur_found = NULL;
 	}
 	fclose(cpuinfo);
-	return ret;
+	return found_part && found_variant;
 }
 #endif
 
diff --git a/programmer.c b/programmer.c
index 5c1f963..5eabdaf 100644
--- a/programmer.c
+++ b/programmer.c
@@ -19,6 +19,20 @@
  */
 
 #include "flash.h"
+#include "programmer.h"
+
+static const struct par_programmer par_programmer_none = {
+		.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,
+};
+
+const struct par_programmer *par_programmer = &par_programmer_none;
 
 /* No-op shutdown() for programmers which don't need special handling */
 int noop_shutdown(void)
@@ -96,3 +110,9 @@
 		buf[i] = chip_readb(addr + i);
 	return;
 }
+
+void register_par_programmer(const struct par_programmer *pgm, const enum chipbustype buses)
+{
+	par_programmer = pgm;
+	buses_supported |= buses;
+}
diff --git a/programmer.h b/programmer.h
index aa4e2f4..6c9f795 100644
--- a/programmer.h
+++ b/programmer.h
@@ -24,6 +24,8 @@
 #ifndef __PROGRAMMER_H__
 #define __PROGRAMMER_H__ 1
 
+#include "flash.h"	/* for chipaddr and flashchip */
+
 enum programmer {
 #if CONFIG_INTERNAL == 1
 	PROGRAMMER_INTERNAL,
@@ -79,11 +81,12 @@
 #if CONFIG_SATAMV == 1
 	PROGRAMMER_SATAMV,
 #endif
+#if CONFIG_LINUX_SPI == 1
+	PROGRAMMER_LINUX_SPI,
+#endif
 	PROGRAMMER_INVALID /* This must always be the last entry. */
 };
 
-extern enum programmer programmer;
-
 struct programmer_entry {
 	const char *vendor;
 	const char *name;
@@ -94,20 +97,12 @@
 				    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_init(enum programmer prog, char *param);
 int programmer_shutdown(void);
 
 enum bitbang_spi_master_type {
@@ -158,7 +153,7 @@
 	P3
 };
 
-struct board_pciid_enable {
+struct board_match {
 	/* Any device, but make it sensible, like the ISA bridge. */
 	uint16_t first_vendor;
 	uint16_t first_device;
@@ -190,7 +185,7 @@
 	int (*enable) (void); /* May be NULL. */
 };
 
-extern const struct board_pciid_enable board_pciid_enables[];
+extern const struct board_match board_matches[];
 
 struct board_info {
 	const char *vendor;
@@ -290,7 +285,7 @@
 #endif
 #if NEED_PCI == 1
 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_vendorclass(uint16_t vendor, uint16_t devclass);
 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);
@@ -304,6 +299,7 @@
 extern int force_boardmismatch;
 void probe_superio(void);
 int register_superio(struct superio s);
+extern enum chipbustype internal_buses_supported;
 int internal_init(void);
 void internal_chip_writeb(uint8_t val, chipaddr addr);
 void internal_chip_writew(uint16_t val, chipaddr addr);
@@ -358,6 +354,18 @@
 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);
+struct par_programmer {
+	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);
+};
+extern const struct par_programmer *par_programmer;
+void register_par_programmer(const struct par_programmer *pgm, const enum chipbustype buses);
 
 /* dummyflasher.c */
 #if CONFIG_DUMMY == 1
@@ -425,9 +433,6 @@
 /* nicintel_spi.c */
 #if CONFIG_NICINTEL_SPI == 1
 int nicintel_spi_init(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
 
@@ -489,6 +494,11 @@
 int buspirate_spi_init(void);
 #endif
 
+/* linux_spi.c */
+#if CONFIG_LINUX_SPI == 1
+int linux_spi_init(void);
+#endif
+
 /* dediprog.c */
 #if CONFIG_DEDIPROG == 1
 int dediprog_init(void);
@@ -546,6 +556,12 @@
 #if CONFIG_OGP_SPI == 1 || CONFIG_NICINTEL_SPI == 1 || CONFIG_RAYER_SPI == 1 || (CONFIG_INTERNAL == 1 && (defined(__i386__) || defined(__x86_64__) || defined(__arm__)))
 	SPI_CONTROLLER_BITBANG,
 #endif
+#if CONFIG_LINUX_SPI == 1
+	SPI_CONTROLLER_LINUX,
+#endif
+#if CONFIG_SERPROG == 1
+	SPI_CONTROLLER_SERPROG,
+#endif
 };
 extern const int spi_programmer_count;
 
@@ -554,30 +570,41 @@
 #define MAX_DATA_WRITE_UNLIMITED 256
 struct spi_programmer {
 	enum spi_controller type;
-	int max_data_read;
-	int max_data_write;
+	unsigned int max_data_read;
+	unsigned int max_data_write;
 	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);
+	int (*read)(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
+	int (*write_256)(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
 };
 
 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);
-int default_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len);
-int default_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len);
+int default_spi_read(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
+int default_spi_write_256(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
 void register_spi_programmer(const struct spi_programmer *programmer);
 
 /* ichspi.c */
 #if CONFIG_INTERNAL == 1
+enum ich_chipset {
+	CHIPSET_ICH_UNKNOWN,
+	CHIPSET_ICH7 = 7,
+	CHIPSET_ICH8,
+	CHIPSET_ICH9,
+	CHIPSET_ICH10,
+	CHIPSET_5_SERIES_IBEX_PEAK,
+	CHIPSET_6_SERIES_COUGAR_POINT,
+	CHIPSET_7_SERIES_PANTHER_POINT
+};
+
 extern uint32_t ichspi_bbar;
 int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
-		    int ich_generation);
+		 enum ich_chipset ich_generation);
 int via_init_spi(struct pci_dev *dev);
 
 /* it85spi.c */
@@ -593,38 +620,38 @@
 int mcp6x_spi_init(int want_spi);
 
 /* mec1308.c */
-struct superio probe_superio_mec1308(void);
 int mec1308_probe_spi_flash(const char *name);
-int mec1308_spi_read(struct flashchip *flash,
-                     uint8_t * buf, int start, int len);
-int mec1308_spi_write_256(struct flashchip *flash,
-                          uint8_t *buf, int start, int len);
-int mec1308_spi_send_command(unsigned int writecnt, unsigned int readcnt,
-                             const unsigned char *writearr,
-                             unsigned char *readarr);
 
 /* sb600spi.c */
 int sb600_probe_spi(struct pci_dev *dev);
 
 /* tegra2_spi.c */
 int tegra2_spi_init(void);
-int tegra2_spi_shutdown(void *);
-int tegra2_spi_send_command(unsigned int writecnt, unsigned int readcnt,
-		      const unsigned char *writearr, unsigned char *readarr);
-int tegra2_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len);
-int tegra2_spi_write(struct flashchip *flash, uint8_t *buf, int start, int len);
 
 /* wbsio_spi.c */
 int wbsio_check_for_spi(void);
 #endif
 
+/* opaque.c */
+struct opaque_programmer {
+	int max_data_read;
+	int max_data_write;
+	/* Specific functions for this programmer */
+	int (*probe) (struct flashchip *flash);
+	int (*read) (struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
+	int (*write) (struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
+	int (*erase) (struct flashchip *flash, unsigned int blockaddr, unsigned int blocklen);
+};
+extern const struct opaque_programmer *opaque_programmer;
+void register_opaque_programmer(const struct opaque_programmer *pgm);
+
 /* serprog.c */
 #if CONFIG_SERPROG == 1
 int serprog_init(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);
+void serprog_delay(int usecs);
 #endif
 
 /* serial.c */
@@ -635,15 +662,7 @@
 #endif
 
 /* wpce775x.c */
-struct superio probe_superio_wpce775x(void);
 int wpce775x_probe_spi_flash(const char *name);
-int wpce775x_spi_read(struct flashchip *flash,
-                      uint8_t * buf, int start, int len);
-int wpce775x_spi_write_256(struct flashchip *flash,
-                           uint8_t *buf, int start, int len);
-int wpce775x_spi_send_command(unsigned int writecnt, unsigned int readcnt,
-			      const unsigned char *writearr,
-			      unsigned char *readarr);
 
 void sp_flush_incoming(void);
 fdtype sp_openserport(char *dev, unsigned int baud);
diff --git a/rayer_spi.c b/rayer_spi.c
index 0807487..417fde9 100644
--- a/rayer_spi.c
+++ b/rayer_spi.c
@@ -31,18 +31,25 @@
 #if defined(__i386__) || defined(__x86_64__)
 
 #include <stdlib.h>
+#include <string.h>
 #include "flash.h"
 #include "programmer.h"
 
+enum rayer_type {
+	TYPE_RAYER,
+	TYPE_XILINX_DLC5,
+};
+
 /* We have two sets of pins, out and in. The numbers for both sets are
  * independent and are bitshift values, not real pin numbers.
+ * Default settings are for the RayeR hardware.
  */
 /* Pins for master->slave direction */
-#define SPI_CS_PIN 5
-#define SPI_SCK_PIN 6
-#define SPI_MOSI_PIN 7
+static int rayer_cs_bit = 5;
+static int rayer_sck_bit = 6;
+static int rayer_mosi_bit = 7;
 /* Pins for slave->master direction */
-#define SPI_MISO_PIN 6
+static int rayer_miso_bit = 6;
 
 static uint16_t lpt_iobase;
 
@@ -51,22 +58,22 @@
 
 static void rayer_bitbang_set_cs(int val)
 {
-	lpt_outbyte &= ~(1 << SPI_CS_PIN);
-	lpt_outbyte |= (val << SPI_CS_PIN);
+	lpt_outbyte &= ~(1 << rayer_cs_bit);
+	lpt_outbyte |= (val << rayer_cs_bit);
 	OUTB(lpt_outbyte, lpt_iobase);
 }
 
 static void rayer_bitbang_set_sck(int val)
 {
-	lpt_outbyte &= ~(1 << SPI_SCK_PIN);
-	lpt_outbyte |= (val << SPI_SCK_PIN);
+	lpt_outbyte &= ~(1 << rayer_sck_bit);
+	lpt_outbyte |= (val << rayer_sck_bit);
 	OUTB(lpt_outbyte, lpt_iobase);
 }
 
 static void rayer_bitbang_set_mosi(int val)
 {
-	lpt_outbyte &= ~(1 << SPI_MOSI_PIN);
-	lpt_outbyte |= (val << SPI_MOSI_PIN);
+	lpt_outbyte &= ~(1 << rayer_mosi_bit);
+	lpt_outbyte |= (val << rayer_mosi_bit);
 	OUTB(lpt_outbyte, lpt_iobase);
 }
 
@@ -75,7 +82,7 @@
 	uint8_t tmp;
 
 	tmp = INB(lpt_iobase + 1);
-	tmp = (tmp >> SPI_MISO_PIN) & 0x1;
+	tmp = (tmp >> rayer_miso_bit) & 0x1;
 	return tmp;
 }
 
@@ -89,14 +96,15 @@
 
 int rayer_spi_init(void)
 {
-	char *portpos = NULL;
+	char *arg = NULL;
+	enum rayer_type rayer_type = TYPE_RAYER;
 
 	/* Non-default port requested? */
-	portpos = extract_programmer_param("iobase");
-	if (portpos) {
+	arg = extract_programmer_param("iobase");
+	if (arg) {
 		char *endptr = NULL;
 		unsigned long tmp;
-		tmp = strtoul(portpos, &endptr, 0);
+		tmp = strtoul(arg, &endptr, 0);
 		/* Port 0, port >0x10000, unaligned ports and garbage strings
 		 * are rejected.
 		 */
@@ -109,7 +117,7 @@
 			msg_perr("Error: iobase= specified, but the I/O base "
 				 "given was invalid.\nIt must be a multiple of "
 				 "0x4 and lie between 0x100 and 0xfffc.\n");
-			free(portpos);
+			free(arg);
 			return 1;
 		} else {
 			lpt_iobase = (uint16_t)tmp;
@@ -120,11 +128,44 @@
 		/* Pick a default value for the I/O base. */
 		lpt_iobase = 0x378;
 	}
-	free(portpos);
+	free(arg);
 	
 	msg_pdbg("Using address 0x%x as I/O base for parallel port access.\n",
 		 lpt_iobase);
 
+	arg = extract_programmer_param("type");
+	if (arg) {
+		if (!strcasecmp(arg, "rayer")) {
+			rayer_type = TYPE_RAYER;
+		} else if (!strcasecmp(arg, "xilinx")) {
+			rayer_type = TYPE_XILINX_DLC5;
+		} else {
+			msg_perr("Error: Invalid device type specified.\n");
+			free(arg);
+			return 1;
+		}
+	}
+	free(arg);
+	switch (rayer_type) {
+	case TYPE_RAYER:
+		msg_pdbg("Using RayeR SPIPGM pinout.\n");
+		/* Bits for master->slave direction */
+		rayer_cs_bit = 5;
+		rayer_sck_bit = 6;
+		rayer_mosi_bit = 7;
+		/* Bits for slave->master direction */
+		rayer_miso_bit = 6;
+		break;
+	case TYPE_XILINX_DLC5:
+		msg_pdbg("Using Xilinx Parallel Cable III (DLC 5) pinout.\n");
+		/* Bits for master->slave direction */
+		rayer_cs_bit = 2;
+		rayer_sck_bit = 1;
+		rayer_mosi_bit = 0;
+		/* Bits for slave->master direction */
+		rayer_miso_bit = 4;
+	}
+
 	get_io_perms();
 
 	/* Get the initial value before writing to any line. */
diff --git a/satamv.c b/satamv.c
index 01919d0..b5d964e 100644
--- a/satamv.c
+++ b/satamv.c
@@ -19,6 +19,7 @@
  */
 
 /* Datasheets are not public (yet?) */
+#if defined(__i386__) || defined(__x86_64__)
 
 #include <stdlib.h>
 #include "flash.h"
@@ -40,6 +41,17 @@
 #define PCI_BAR2_CONTROL		0x00c08
 #define GPIO_PORT_CONTROL		0x104f0
 
+static const struct par_programmer par_programmer_satamv = {
+		.chip_readb		= satamv_chip_readb,
+		.chip_readw		= fallback_chip_readw,
+		.chip_readl		= fallback_chip_readl,
+		.chip_readn		= fallback_chip_readn,
+		.chip_writeb		= satamv_chip_writeb,
+		.chip_writew		= fallback_chip_writew,
+		.chip_writel		= fallback_chip_writel,
+		.chip_writen		= fallback_chip_writen,
+};
+
 static int satamv_shutdown(void *data)
 {
 	physunmap(mv_bar, 0x20000);
@@ -136,11 +148,10 @@
 	mv_iobar = tmp & 0xffff;
 	msg_pspew("Activating I/O BAR at 0x%04x\n", mv_iobar);
 
-	buses_supported = CHIP_BUSTYPE_PARALLEL;
-
 	/* 512 kByte with two 8-bit latches, and
 	 * 4 MByte with additional 3-bit latch. */
 	max_rom_decode.parallel = 4 * 1024 * 1024;
+	register_par_programmer(&par_programmer_satamv, BUS_PARALLEL);
 
 	return 0;
 
@@ -182,3 +193,7 @@
 {
 	return satamv_indirect_chip_readb(addr);
 }
+
+#else
+#error PCI port I/O access is not supported on this architecture yet.
+#endif
diff --git a/satasii.c b/satasii.c
index dbc4f4f..e51e3ae 100644
--- a/satasii.c
+++ b/satasii.c
@@ -42,6 +42,17 @@
 	{},
 };
 
+static const struct par_programmer par_programmer_satasii = {
+		.chip_readb		= satasii_chip_readb,
+		.chip_readw		= fallback_chip_readw,
+		.chip_readl		= fallback_chip_readl,
+		.chip_readn		= fallback_chip_readn,
+		.chip_writeb		= satasii_chip_writeb,
+		.chip_writew		= fallback_chip_writew,
+		.chip_writel		= fallback_chip_writel,
+		.chip_writen		= fallback_chip_writen,
+};
+
 static int satasii_shutdown(void *data)
 {
 	physunmap(sii_bar, SATASII_MEMMAP_SIZE);
@@ -76,10 +87,11 @@
 	if ((id != 0x0680) && (!(pci_mmio_readl(sii_bar) & (1 << 26))))
 		msg_pinfo("Warning: Flash seems unconnected.\n");
 
-	buses_supported = CHIP_BUSTYPE_PARALLEL;
-
 	if (register_shutdown(satasii_shutdown, NULL))
 		return 1;
+
+	register_par_programmer(&par_programmer_satasii, BUS_PARALLEL);
+
 	return 0;
 }
 
diff --git a/sb600spi.c b/sb600spi.c
index 37aac3b..9d82b47 100644
--- a/sb600spi.c
+++ b/sb600spi.c
@@ -24,7 +24,6 @@
 #if defined(__i386__) || defined(__x86_64__)
 
 #include "flash.h"
-#include "chipdrivers.h"
 #include "programmer.h"
 #include "spi.h"
 
@@ -260,8 +259,11 @@
 	smbus_dev = pci_dev_find(0x1002, 0x4385);
 
 	if (!smbus_dev) {
-		msg_perr("ERROR: SMBus device not found. Not enabling SPI.\n");
-		return ERROR_NONFATAL;
+		smbus_dev = pci_dev_find(0x1022, 0x780b); /* AMD Hudson */
+		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. */
diff --git a/serial.c b/serial.c
index 5cc2fe0..b504974 100644
--- a/serial.c
+++ b/serial.c
@@ -110,6 +110,8 @@
 	    (tolower((unsigned char)dev[1]) == 'o') &&
 	    (tolower((unsigned char)dev[2]) == 'm')) {
 		dev2 = malloc(strlen(dev) + 5);
+		if (!dev2)
+			sp_die("Error: Out of memory");
 		strcpy(dev2, "\\\\.\\");
 		strcpy(dev2 + 4, dev);
 	}
diff --git a/serprog.c b/serprog.c
index e8a4402..7925cee 100644
--- a/serprog.c
+++ b/serprog.c
@@ -1,7 +1,7 @@
 /*
  * This file is part of the flashrom project.
  *
- * Copyright (C) 2009 Urja Rannikko <urjaman@gmail.com>
+ * Copyright (C) 2009, 2011 Urja Rannikko <urjaman@gmail.com>
  * Copyright (C) 2009 Carl-Daniel Hailfinger
  *
  * This program is free software; you can redistribute it and/or modify
@@ -36,8 +36,9 @@
 #include <termios.h>
 #include "flash.h"
 #include "programmer.h"
+#include "chipdrivers.h"
 
-#define MSGHEADER "serprog:"
+#define MSGHEADER "serprog: "
 
 /*
  * FIXME: This prototype was added to help reduce diffs for the shutdown
@@ -67,6 +68,7 @@
 #define S_CMD_SYNCNOP		0x10	/* Special no-operation that returns NAK+ACK    */
 #define S_CMD_Q_RDNMAXLEN	0x11	/* Query read-n maximum length			*/
 #define S_CMD_S_BUSTYPE		0x12	/* Set used bustype(s).				*/
+#define S_CMD_O_SPIOP		0x13	/* Perform SPI operation.			*/
 
 static uint16_t sp_device_serbuf_size = 16;
 static uint16_t sp_device_opbuf_size = 300;
@@ -202,7 +204,7 @@
 			return;
 		}
 	}
-	msg_perr("Error: cannot synchronize protocol\n"
+	msg_perr("Error: cannot synchronize protocol "
 		"- check communications and reset device?\n");
 	exit(1);
 }
@@ -218,32 +220,27 @@
 static int sp_automatic_cmdcheck(uint8_t cmd)
 {
 	if ((sp_check_avail_automatic) && (sp_check_commandavail(cmd) == 0)) {
-		msg_pdbg("Warning: Automatic command availability check"
-				" failed for cmd %d - wont execute cmd\n",cmd);
+		msg_pdbg("Warning: Automatic command availability check failed "
+			 "for cmd 0x%x - won't execute cmd\n", cmd);
 		return 1;
 		}
 	return 0;
 }
 
 static int sp_docommand(uint8_t command, uint32_t parmlen,
-			     uint8_t * params, uint32_t retlen, void *retparms)
+			uint8_t *params, uint32_t retlen, void *retparms)
 {
-	unsigned char *sendpacket;
 	unsigned char c;
 	if (sp_automatic_cmdcheck(command))
 		return 1;
-	sendpacket = malloc(1 + parmlen);
-	if (!sendpacket)
-		sp_die("Error: cannot malloc command buffer");
-	sendpacket[0] = command;
-	memcpy(&(sendpacket[1]), params, parmlen);
-	if (write(sp_fd, sendpacket, 1 + parmlen) != (1 + parmlen)) {
-		sp_die("Error: cannot write command");
-	}
-	free(sendpacket);
+	if (write(sp_fd, &command, 1) != 1)
+		sp_die("Error: cannot write op code");
+	if (write(sp_fd, params, parmlen) != (parmlen))
+		sp_die("Error: cannot write parameters");
 	if (read(sp_fd, &c, 1) != 1)
 		sp_die("Error: cannot read from device");
-	if (c == S_NAK) return 1;
+	if (c == S_NAK)
+		return 1;
 	if (c != S_ACK) {
 		msg_perr("Error: invalid response 0x%02X from device\n",c);
 		exit(1);
@@ -254,8 +251,8 @@
 			int r;
 			r = read(sp_fd, retparms + rd_bytes,
 				 retlen - rd_bytes);
-			if (r <= 0) sp_die
-				    ("Error: cannot read return parameters");
+			if (r <= 0)
+				sp_die("Error: cannot read return parameters");
 			rd_bytes += r;
 		} while (rd_bytes != retlen);
 	}
@@ -302,6 +299,34 @@
 	return 0;
 }
 
+static int serprog_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+				    const unsigned char *writearr,
+				    unsigned char *readarr);
+static int serprog_spi_read(struct flashchip *flash, uint8_t *buf,
+			    unsigned int start, unsigned int len);
+static struct spi_programmer spi_programmer_serprog = {
+	.type		= SPI_CONTROLLER_SERPROG,
+	.max_data_read	= MAX_DATA_READ_UNLIMITED,
+	.max_data_write	= MAX_DATA_WRITE_UNLIMITED,
+	.command	= serprog_spi_send_command,
+	.multicommand	= default_spi_send_multicommand,
+	.read		= serprog_spi_read,
+	.write_256	= default_spi_write_256,
+};
+
+static const struct par_programmer par_programmer_serprog = {
+		.chip_readb		= serprog_chip_readb,
+		.chip_readw		= fallback_chip_readw,
+		.chip_readl		= fallback_chip_readl,
+		.chip_readn		= serprog_chip_readn,
+		.chip_writeb		= serprog_chip_writeb,
+		.chip_writew		= fallback_chip_writew,
+		.chip_writel		= fallback_chip_writel,
+		.chip_writen		= fallback_chip_writen,
+};
+
+static enum chipbustype serprog_buses_supported = BUS_NONE;
+
 int serprog_init(void)
 {
 	uint16_t iface;
@@ -325,7 +350,7 @@
 			msg_perr("Error: No baudrate specified.\n"
 				 "Use flashrom -p serprog:dev=/dev/device:baud\n");
 			free(device);
-			return 1;		
+			return 1;
 		}
 		if (strlen(device)) {
 			sp_fd = sp_openserport(device, atoi(baudport));
@@ -358,7 +383,7 @@
 			msg_perr("Error: No port specified.\n"
 				 "Use flashrom -p serprog:ip=ipaddr:port\n");
 			free(device);
-			return 1;		
+			return 1;
 		}
 		if (strlen(device)) {
 			sp_fd = sp_opensocket(device, atoi(baudport));
@@ -392,53 +417,141 @@
 	msg_pdbg(MSGHEADER "Synchronized\n");
 
 	if (sp_docommand(S_CMD_Q_IFACE, 0, NULL, 2, &iface)) {
-		msg_perr("Error: NAK to Query Interface version\n");
-		exit(1);
+		msg_perr("Error: NAK to query interface version\n");
+		return 1;
 	}
 
 	if (iface != 1) {
-		msg_perr("Error: Unknown interface version %d\n", iface);
-		exit(1);
+		msg_perr("Error: Unknown interface version: %d\n", iface);
+		return 1;
 	}
 
 	msg_pdbg(MSGHEADER "Interface version ok.\n");
 
 	if (sp_docommand(S_CMD_Q_CMDMAP, 0, NULL, 32, sp_cmdmap)) {
 		msg_perr("Error: query command map not supported\n");
-		exit(1);
+		return 1;
 	}
 
 	sp_check_avail_automatic = 1;
 
-	/* Check for the minimum operational set of commands */
-	if (sp_check_commandavail(S_CMD_R_BYTE) == 0) {
-		msg_perr("Error: Single byte read not supported\n");
-		exit(1);
+	/* FIXME: This assumes that serprog device bustypes are always
+	 * identical with flashrom bustype enums and that they all fit
+	 * in a single byte.
+	 */
+	if (sp_docommand(S_CMD_Q_BUSTYPE, 0, NULL, 1, &c)) {
+		msg_perr("Warning: NAK to query supported buses\n");
+		c = BUS_NONSPI;	/* A reasonable default for now. */
 	}
-	/* This could be translated to single byte reads (if missing),	*
-	 * but now we dont support that.				*/
-	if (sp_check_commandavail(S_CMD_R_NBYTES) == 0) {
-		msg_perr("Error: Read n bytes not supported\n");
-		exit(1);
+	serprog_buses_supported = c;
+
+	msg_pdbg(MSGHEADER "Bus support: parallel=%s, LPC=%s, FWH=%s, SPI=%s\n",
+		 (c & BUS_PARALLEL) ? "on" : "off",
+		 (c & BUS_LPC) ? "on" : "off",
+		 (c & BUS_FWH) ? "on" : "off",
+		 (c & BUS_SPI) ? "on" : "off");
+	/* Check for the minimum operational set of commands. */
+	if (serprog_buses_supported & BUS_SPI) {
+		uint8_t bt = BUS_SPI;
+		if (sp_check_commandavail(S_CMD_O_SPIOP) == 0) {
+			msg_perr("Error: SPI operation not supported while the "
+				 "bustype is SPI\n");
+			return 1;
+		}
+		/* Success of any of these commands is optional. We don't need
+		   the programmer to tell us its limits, but if it doesn't, we
+		   will assume stuff, so it's in the programmers best interest
+		   to tell us. */
+		sp_docommand(S_CMD_S_BUSTYPE, 1, &bt, 0, NULL);
+		if (!sp_docommand(S_CMD_Q_WRNMAXLEN, 0, NULL, 3, rbuf)) {
+			uint32_t v;
+			v = ((unsigned int)(rbuf[0]) << 0);
+			v |= ((unsigned int)(rbuf[1]) << 8);
+			v |= ((unsigned int)(rbuf[2]) << 16);
+			if (v == 0)
+				v = (1 << 24) - 1; /* SPI-op maximum. */
+			spi_programmer_serprog.max_data_write = v;
+			msg_pdbg(MSGHEADER "Maximum write-n length is %d\n", v);
+		}
+		if (!sp_docommand(S_CMD_Q_RDNMAXLEN, 0, NULL, 3, rbuf)) {
+			uint32_t v;
+			v = ((unsigned int)(rbuf[0]) << 0);
+			v |= ((unsigned int)(rbuf[1]) << 8);
+			v |= ((unsigned int)(rbuf[2]) << 16);
+			if (v == 0)
+				v = (1 << 24) - 1; /* SPI-op maximum. */
+			spi_programmer_serprog.max_data_read = v;
+			msg_pdbg(MSGHEADER "Maximum read-n length is %d\n", v);
+		}
+		bt = serprog_buses_supported;
+		sp_docommand(S_CMD_S_BUSTYPE, 1, &bt, 0, NULL);
 	}
-	/* In the future one could switch to read-only mode if these	*
-	 * are not available.						*/
-	if (sp_check_commandavail(S_CMD_O_INIT) == 0) {
-		msg_perr("Error: Initialize operation buffer not supported\n");
-		exit(1);
-	}
-	if (sp_check_commandavail(S_CMD_O_WRITEB) == 0) {
-		msg_perr("Error: Write to opbuf: write byte not supported\n");
-		exit(1);
-	}
-	if (sp_check_commandavail(S_CMD_O_DELAY) == 0) {
-		msg_perr("Error: Write to opbuf: delay not supported\n");
-		exit(1);
-	}
-	if (sp_check_commandavail(S_CMD_O_EXEC) == 0) {
-		msg_perr(
-			"Error: Execute operation buffer not supported\n");
-		exit(1);
+
+	if (serprog_buses_supported & BUS_NONSPI) {
+		if (sp_check_commandavail(S_CMD_O_INIT) == 0) {
+			msg_perr("Error: Initialize operation buffer "
+				 "not supported\n");
+			return 1;
+		}
+
+		if (sp_check_commandavail(S_CMD_O_DELAY) == 0) {
+			msg_perr("Error: Write to opbuf: "
+				 "delay not supported\n");
+			return 1;
+		}
+
+		/* S_CMD_O_EXEC availability checked later. */
+
+		if (sp_check_commandavail(S_CMD_R_BYTE) == 0) {
+			msg_perr("Error: Single byte read not supported\n");
+			return 1;
+		}
+		/* This could be translated to single byte reads (if missing),
+		 * but now we don't support that. */
+		if (sp_check_commandavail(S_CMD_R_NBYTES) == 0) {
+			msg_perr("Error: Read n bytes not supported\n");
+			return 1;
+		}
+		if (sp_check_commandavail(S_CMD_O_WRITEB) == 0) {
+			msg_perr("Error: Write to opbuf: "
+				 "write byte not supported\n");
+			return 1;
+		}
+
+		if (sp_docommand(S_CMD_Q_WRNMAXLEN, 0, NULL, 3, rbuf)) {
+			msg_pdbg(MSGHEADER "Write-n not supported");
+			sp_max_write_n = 0;
+		} else {
+			sp_max_write_n = ((unsigned int)(rbuf[0]) << 0);
+			sp_max_write_n |= ((unsigned int)(rbuf[1]) << 8);
+			sp_max_write_n |= ((unsigned int)(rbuf[2]) << 16);
+			if (!sp_max_write_n) {
+				sp_max_write_n = (1 << 24);
+			}
+			msg_pdbg(MSGHEADER "Maximum write-n length is %d\n",
+				 sp_max_write_n);
+			sp_write_n_buf = malloc(sp_max_write_n);
+			if (!sp_write_n_buf) {
+				msg_perr("Error: cannot allocate memory for "
+					 "Write-n buffer\n");
+				return 1;
+			}
+			sp_write_n_bytes = 0;
+		}
+
+		if (sp_check_commandavail(S_CMD_Q_RDNMAXLEN) &&
+		    (sp_docommand(S_CMD_Q_RDNMAXLEN, 0, NULL, 3, rbuf) == 0)) {
+			sp_max_read_n = ((unsigned int)(rbuf[0]) << 0);
+			sp_max_read_n |= ((unsigned int)(rbuf[1]) << 8);
+			sp_max_read_n |= ((unsigned int)(rbuf[2]) << 16);
+			msg_pdbg(MSGHEADER "Maximum read-n length is %d\n",
+				 sp_max_read_n ? sp_max_read_n : (1 << 24));
+		} else {
+			msg_pdbg(MSGHEADER "Maximum read-n length "
+				 "not reported\n");
+			sp_max_read_n = 0;
+		}
+
 	}
 
 	if (sp_docommand(S_CMD_Q_PGMNAME, 0, NULL, 16, pgmname)) {
@@ -446,64 +559,45 @@
 		strcpy((char *)pgmname, "(unknown)");
 	}
 	pgmname[16] = 0;
-	msg_pinfo(MSGHEADER "Programmer name \"%s\"\n", pgmname);
+	msg_pinfo(MSGHEADER "Programmer name is \"%s\"\n", pgmname);
 
 	if (sp_docommand(S_CMD_Q_SERBUF, 0, NULL, 2, &sp_device_serbuf_size)) {
 		msg_perr("Warning: NAK to query serial buffer size\n");
 	}
-	msg_pdbg(MSGHEADER "serial buffer size %d\n",
+	msg_pdbg(MSGHEADER "Serial buffer size is %d\n",
 		     sp_device_serbuf_size);
 
-	if (sp_docommand(S_CMD_Q_OPBUF, 0, NULL, 2, &sp_device_opbuf_size)) {
-		msg_perr("Warning: NAK to query operation buffer size\n");
-	}
-	msg_pdbg(MSGHEADER "operation buffer size %d\n",
-		     sp_device_opbuf_size);
-
-	if (sp_docommand(S_CMD_Q_BUSTYPE, 0, NULL, 1, &c)) {
-		msg_perr("Warning: NAK to query supported buses\n");
-		c = CHIP_BUSTYPE_NONSPI;	/* A reasonable default for now. */
-	}
-	buses_supported = c;
-
-	if (sp_docommand(S_CMD_O_INIT, 0, NULL, 0, NULL)) {
-		msg_perr("Error: NAK to initialize operation buffer\n");
-		exit(1);
-	}
-
-	if (sp_docommand(S_CMD_Q_WRNMAXLEN, 0, NULL, 3, rbuf)) {
-		msg_pdbg(MSGHEADER "Write-n not supported");
-		sp_max_write_n = 0;
-	} else {
-		sp_max_write_n = ((unsigned int)(rbuf[0]) << 0);
-		sp_max_write_n |= ((unsigned int)(rbuf[1]) << 8);
-		sp_max_write_n |= ((unsigned int)(rbuf[2]) << 16);
-		msg_pdbg(MSGHEADER "Maximum write-n length %d\n",
-			     sp_max_write_n);
-		sp_write_n_buf = malloc(sp_max_write_n);
-		if (!sp_write_n_buf) {
-			msg_perr("Error: cannot allocate memory for Write-n buffer\n");
-			exit(1);
+	if (sp_check_commandavail(S_CMD_O_INIT)) {
+		/* This would be inconsistent. */
+		if (sp_check_commandavail(S_CMD_O_EXEC) == 0) {
+			msg_perr("Error: Execute operation buffer not "
+				 "supported\n");
+			return 1;
 		}
-		sp_write_n_bytes = 0;
-	}
-	
-	if ((sp_check_commandavail(S_CMD_Q_RDNMAXLEN))
-		&&((sp_docommand(S_CMD_Q_RDNMAXLEN,0,NULL, 3, rbuf) == 0))) {
-		sp_max_read_n = ((unsigned int)(rbuf[0]) << 0);
-		sp_max_read_n |= ((unsigned int)(rbuf[1]) << 8);
-		sp_max_read_n |= ((unsigned int)(rbuf[2]) << 16);
-		msg_pdbg(MSGHEADER "Maximum read-n length %d\n",
-			sp_max_read_n ? sp_max_read_n : (1<<24));
-	} else {
-		msg_pdbg(MSGHEADER "Maximum read-n length not reported\n");
-		sp_max_read_n = 0;
-	}
+
+		if (sp_docommand(S_CMD_O_INIT, 0, NULL, 0, NULL)) {
+			msg_perr("Error: NAK to initialize operation buffer\n");
+			return 1;
+		}
+
+		if (sp_docommand(S_CMD_Q_OPBUF, 0, NULL, 2,
+		    &sp_device_opbuf_size)) {
+			msg_perr("Warning: NAK to query operation buffer "
+				 "size\n");
+		}
+		msg_pdbg(MSGHEADER "operation buffer size is %d\n",
+			 sp_device_opbuf_size);
+  	}
 
 	sp_prev_was_write = 0;
 	sp_streamed_transmit_ops = 0;
 	sp_streamed_transmit_bytes = 0;
 	sp_opbuf_usage = 0;
+	if (serprog_buses_supported & BUS_SPI)
+		register_spi_programmer(&spi_programmer_serprog);
+	if (serprog_buses_supported & BUS_NONSPI)
+		register_par_programmer(&par_programmer_serprog,
+					serprog_buses_supported & BUS_NONSPI);
 	return 0;
 }
 
@@ -513,7 +607,7 @@
 {
 	unsigned char header[7];
 	msg_pspew(MSGHEADER "Passing write-n bytes=%d addr=0x%x\n",
-		     sp_write_n_bytes, sp_write_n_addr);
+		  sp_write_n_bytes, sp_write_n_addr);
 	if (sp_streamed_transmit_bytes >=
 	    (7 + sp_write_n_bytes + sp_device_serbuf_size))
 		sp_flush_stream();
@@ -667,26 +761,77 @@
 {
 	size_t lenm = len;
 	chipaddr addrm = addr;
-	while ((sp_max_read_n)&&(lenm > sp_max_read_n)) {
-		sp_do_read_n(&(buf[addrm-addr]),addrm,sp_max_read_n);
+	while ((sp_max_read_n != 0) && (lenm > sp_max_read_n)) {
+		sp_do_read_n(&(buf[addrm-addr]), addrm, sp_max_read_n);
 		addrm += sp_max_read_n;
 		lenm -= sp_max_read_n;
 	}
-	if (lenm) sp_do_read_n(&(buf[addrm-addr]),addrm,lenm);
+	if (lenm)
+		sp_do_read_n(&(buf[addrm-addr]), addrm, lenm);
 }
 
-void serprog_delay(int delay)
+void serprog_delay(int usecs)
 {
 	unsigned char buf[4];
-	msg_pspew("%s\n", __func__);
+	msg_pspew("%s usecs=%d\n", __func__, usecs);
+	if (!sp_check_commandavail(S_CMD_O_DELAY)) {
+		msg_pdbg("Note: serprog_delay used, but the programmer doesn't "
+			 "support delay\n");
+		internal_delay(usecs);
+		return;
+	}
 	if ((sp_max_write_n) && (sp_write_n_bytes))
 		sp_pass_writen();
 	sp_check_opbuf_usage(5);
-	buf[0] = ((delay >> 0) & 0xFF);
-	buf[1] = ((delay >> 8) & 0xFF);
-	buf[2] = ((delay >> 16) & 0xFF);
-	buf[3] = ((delay >> 24) & 0xFF);
+	buf[0] = ((usecs >> 0) & 0xFF);
+	buf[1] = ((usecs >> 8) & 0xFF);
+	buf[2] = ((usecs >> 16) & 0xFF);
+	buf[3] = ((usecs >> 24) & 0xFF);
 	sp_stream_buffer_op(S_CMD_O_DELAY, 4, buf);
 	sp_opbuf_usage += 5;
 	sp_prev_was_write = 0;
 }
+
+static int serprog_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+			     const unsigned char *writearr,
+			     unsigned char *readarr)
+{
+	unsigned char *parmbuf;
+	int ret;
+	msg_pspew("%s, writecnt=%i, readcnt=%i\n", __func__, writecnt, readcnt);
+	if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
+		sp_execute_opbuf();
+	parmbuf = malloc(writecnt + 6);
+	if (!parmbuf)
+		sp_die("Error: cannot malloc SPI send param buffer");
+	parmbuf[0] = (writecnt >> 0) & 0xFF;
+	parmbuf[1] = (writecnt >> 8) & 0xFF;
+	parmbuf[2] = (writecnt >> 16) & 0xFF;
+	parmbuf[3] = (readcnt >> 0) & 0xFF;
+	parmbuf[4] = (readcnt >> 8) & 0xFF;
+	parmbuf[5] = (readcnt >> 16) & 0xFF;
+	memcpy(parmbuf + 6, writearr, writecnt);
+	ret = sp_docommand(S_CMD_O_SPIOP, writecnt + 6, parmbuf, readcnt,
+			   readarr);
+	free(parmbuf);
+	return ret;
+}
+
+/* FIXME: This function is optimized so that it does not split each transaction
+ * into chip page_size long blocks unnecessarily like spi_read_chunked. This has
+ * the advantage that it is much faster for most chips, but breaks those with
+ * non-contiguous address space (like AT45DB161D). When spi_read_chunked is
+ * fixed this method can be removed. */
+static int serprog_spi_read(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len)
+{
+	unsigned int i, cur_len;
+	const unsigned int max_read = spi_programmer_serprog.max_data_read;
+	for (i = 0; i < len; i += cur_len) {
+		int ret;
+		cur_len = min(max_read, (len - i));
+		ret = spi_nbyte_read(start + i, buf + i, cur_len);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
diff --git a/spi.c b/spi.c
index 5d2e441..aa62936 100644
--- a/spi.c
+++ b/spi.c
@@ -97,33 +97,43 @@
 	return result;
 }
 
-int default_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+int default_spi_read(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len)
 {
-	int max_data = spi_programmer->max_data_read;
+	unsigned int max_data = spi_programmer->max_data_read;
+	int rc;
 	if (max_data == MAX_DATA_UNSPECIFIED) {
 		msg_perr("%s called, but SPI read chunk size not defined "
 			 "on this hardware. Please report a bug at "
 			 "flashrom@flashrom.org\n", __func__);
 		return 1;
 	}
-	return spi_read_chunked(flash, buf, start, len, max_data);
+	rc = spi_read_chunked(flash, buf, start, len, max_data);
+	/* translate SPI-specific access denied error to generic error */
+	if (rc == SPI_ACCESS_DENIED)
+		rc = ACCESS_DENIED;
+	return rc;
 }
 
-int default_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
+int default_spi_write_256(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len)
 {
-	int max_data = spi_programmer->max_data_write;
+	unsigned int max_data = spi_programmer->max_data_write;
+	int rc;
 	if (max_data == MAX_DATA_UNSPECIFIED) {
 		msg_perr("%s called, but SPI write chunk size not defined "
 			 "on this hardware. Please report a bug at "
 			 "flashrom@flashrom.org\n", __func__);
 		return 1;
 	}
-	return spi_write_chunked(flash, buf, start, len, max_data);
+	rc = spi_write_chunked(flash, buf, start, len, max_data);
+	/* translate SPI-specific access denied error to generic error */
+	if (rc == SPI_ACCESS_DENIED)
+		rc = ACCESS_DENIED;
+	return rc;
 }
 
-int spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+int spi_chip_read(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len)
 {
-	int addrbase = 0;
+	unsigned int addrbase = 0;
 	if (!spi_programmer->read) {
 		msg_perr("%s called, but SPI read is unsupported on this "
 			 "hardware. Please report a bug at "
@@ -160,7 +170,7 @@
  * .write_256 = spi_chip_write_1
  */
 /* real chunksize is up to 256, logical chunksize is 256 */
-int spi_chip_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
+int spi_chip_write_256(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len)
 {
 	if (!spi_programmer->write_256) {
 		msg_perr("%s called, but SPI page write is unsupported on this "
@@ -195,5 +205,5 @@
 void register_spi_programmer(const struct spi_programmer *pgm)
 {
 	spi_programmer = pgm;
-	buses_supported |= CHIP_BUSTYPE_SPI;
+	buses_supported |= BUS_SPI;
 }
diff --git a/spi.h b/spi.h
index b908603..3c386fc 100644
--- a/spi.h
+++ b/spi.h
@@ -125,5 +125,6 @@
 #define SPI_INVALID_LENGTH	-4
 #define SPI_FLASHROM_BUG	-5
 #define SPI_PROGRAMMER_ERROR	-6
+#define SPI_ACCESS_DENIED	-7
 
 #endif		/* !__SPI_H__ */
diff --git a/spi25.c b/spi25.c
index beb377b..cb4a441 100644
--- a/spi25.c
+++ b/spi25.c
@@ -120,7 +120,6 @@
 	uint32_t id2;
 
 	if (spi_rdid(readarr, bytes)) {
-		msg_cdbg("\n");
 		return 0;
 	}
 
@@ -201,7 +200,6 @@
 	uint32_t id1, id2;
 
 	if (spi_rems(readarr)) {
-		msg_cdbg("\n");
 		return 0;
 	}
 
@@ -259,7 +257,6 @@
 	}
 
 	if (spi_res(readarr, 1)) {
-		msg_cdbg("\n");
 		return 0;
 	}
 
@@ -283,7 +280,6 @@
 	uint32_t id1, id2;
 
 	if (spi_res(readarr, 2)) {
-		msg_cdbg("\n");
 		return 0;
 	}
 
@@ -845,7 +841,7 @@
 	return ret;
 }
 
-int spi_byte_program(int addr, uint8_t databyte)
+int spi_byte_program(unsigned int addr, uint8_t databyte)
 {
 	int result;
 	struct spi_command cmds[] = {
@@ -880,7 +876,7 @@
 	return result;
 }
 
-int spi_nbyte_program(int addr, uint8_t *bytes, int len)
+int spi_nbyte_program(unsigned int addr, uint8_t *bytes, unsigned int len)
 {
 	int result;
 	/* FIXME: Switch to malloc based on len unless that kills speed. */
@@ -921,8 +917,10 @@
 
 	result = spi_send_multicommand(cmds);
 	if (result) {
-		msg_cerr("%s failed during command execution at address 0x%x\n",
-			__func__, addr);
+		if (result != SPI_ACCESS_DENIED) {
+			msg_cerr("%s failed during command execution at address 0x%x\n",
+				__func__, addr);
+		}
 	}
 	return result;
 }
@@ -964,7 +962,7 @@
 	return 0;
 }
 
-int spi_nbyte_read(int address, uint8_t *bytes, int len)
+int spi_nbyte_read(unsigned int address, uint8_t *bytes, unsigned int len)
 {
 	const unsigned char cmd[JEDEC_READ_OUTSIZE] = {
 		JEDEC_READ,
@@ -982,12 +980,11 @@
  * 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)
+int spi_read_chunked(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len, unsigned int chunksize)
 {
-	int rc = 0;
-	int i, j, starthere, lenhere;
-	int page_size = flash->page_size;
-	int toread;
+	int rc = 0, chunk_status = 0;
+	unsigned int i, j, starthere, lenhere, toread;
+	unsigned int page_size = flash->page_size;
 
 	/* Warning: This loop has a very unusual condition and body.
 	 * The loop needs to go through each page with at least one affected
@@ -1006,11 +1003,22 @@
 		lenhere = min(start + len, (i + 1) * page_size) - starthere;
 		for (j = 0; j < lenhere; j += chunksize) {
 			toread = min(chunksize, lenhere - j);
-			rc = spi_nbyte_read(starthere + j, buf + starthere - start + j, toread);
-			if (rc)
-				break;
+			chunk_status = spi_nbyte_read(starthere + j, buf + starthere - start + j, toread);
+			if (chunk_status) {
+				if (ignore_error(chunk_status)) {
+					/* fill this chunk with 0xff bytes and
+					   let caller know about the error */
+					memset(buf + starthere - start + j, 0xff, toread);
+					rc = chunk_status;
+					chunk_status = 0;
+					continue;
+				} else {
+					rc = chunk_status;
+					break;
+				}
+			}
 		}
-		if (rc)
+		if (chunk_status)
 			break;
 	}
 
@@ -1022,17 +1030,16 @@
  * 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 spi_write_chunked(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len, unsigned int chunksize)
 {
 	int rc = 0;
-	int i, j, starthere, lenhere;
+	unsigned int i, j, starthere, lenhere, towrite;
 	/* 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;
+	unsigned int page_size = flash->page_size;
 
 	/* Warning: This loop has a very unusual condition and body.
 	 * The loop needs to go through each page with at least one affected
@@ -1071,9 +1078,10 @@
  * (e.g. due to size constraints in IT87* for over 512 kB)
  */
 /* real chunksize is 1, logical chunksize is 1 */
-int spi_chip_write_1(struct flashchip *flash, uint8_t *buf, int start, int len)
+int spi_chip_write_1(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len)
 {
-	int i, result = 0;
+	unsigned int i;
+	int result = 0;
 
 	for (i = start; i < start + len; i++) {
 		result = spi_byte_program(i, buf[i - start]);
@@ -1086,7 +1094,7 @@
 	return 0;
 }
 
-int spi_aai_write(struct flashchip *flash, uint8_t *buf, int start, int len)
+int spi_aai_write(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len)
 {
 	uint32_t pos = start;
 	int result;
diff --git a/sst28sf040.c b/sst28sf040.c
index d621cc7..2038d53 100644
--- a/sst28sf040.c
+++ b/sst28sf040.c
@@ -76,7 +76,7 @@
 }
 
 /* chunksize is 1 */
-int write_28sf040(struct flashchip *flash, uint8_t *src, int start, int len)
+int write_28sf040(struct flashchip *flash, uint8_t *src, unsigned int start, unsigned int len)
 {
 	int i;
 	chipaddr bios = flash->virtual_memory;
diff --git a/sst49lfxxxc.c b/sst49lfxxxc.c
index 28f6cd0..c3ef823 100644
--- a/sst49lfxxxc.c
+++ b/sst49lfxxxc.c
@@ -35,7 +35,7 @@
 static int write_lockbits_49lfxxxc(struct flashchip *flash, unsigned char bits)
 {
 	chipaddr registers = flash->virtual_registers;
-	int i, left = flash->total_size * 1024;
+	unsigned int i, left = flash->total_size * 1024;
 	unsigned long address;
 
 	msg_cdbg("\nbios=0x%08lx\n", registers);
diff --git a/sst_fwhub.c b/sst_fwhub.c
index 65bd2b3..a11cccb 100644
--- a/sst_fwhub.c
+++ b/sst_fwhub.c
@@ -23,7 +23,6 @@
 /* Adapted from the Intel FW hub stuff for 82802ax parts. */
 
 #include "flash.h"
-#include "chipdrivers.h"
 
 static int check_sst_fwhub_block_lock(struct flashchip *flash, int offset)
 {
diff --git a/tegra2_spi.c b/tegra2_spi.c
index 8168b79..793ba00 100644
--- a/tegra2_spi.c
+++ b/tegra2_spi.c
@@ -317,7 +317,7 @@
 	uint32_t *spi_cmd;
 	uint32_t *spi_sts;
 
-	buses_supported = CHIP_BUSTYPE_SPI;
+	buses_supported = BUS_SPI;
 	register_spi_programmer(&spi_programmer_tegra2);
 
 	gpio_base = physmap("GPIO", TEGRA2_GPIO_BASE, 4096);
diff --git a/util/use_big_lock.sh b/util/use_big_lock.sh
index a6632af..fd75455 100755
--- a/util/use_big_lock.sh
+++ b/util/use_big_lock.sh
@@ -14,14 +14,14 @@
 #ifdef _POSIX_C_SOURCE
 	if ((long)_POSIX_C_SOURCE >= 200112)
 	        exit(EXIT_SUCCESS);
+#else
+#error
 #endif
-        exit (EXIT_FAILURE);
+        exit(EXIT_FAILURE);
 }
 " > .test.c
 
-${CC} -o .test .test.c && ./.test || exit 1
+${CC} -o .test .test.c
 rc=$?
 echo $rc
-
-rm -f .test
-exit $rc
+exit
diff --git a/util/z60_flashrom.rules b/util/z60_flashrom.rules
index c298968..8456a04 100644
--- a/util/z60_flashrom.rules
+++ b/util/z60_flashrom.rules
@@ -56,6 +56,10 @@
 # http://www.ftdichip.com/Products/EvaluationKits/FT4232H_MiniModule.htm
 ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", MODE="664", GROUP="plugdev"
 
+# GOEPEL PicoTAP
+# http://www.goepel.com/jtagboundary-scan/hardware/picotap.html
+ATTRS{idVendor}=="096c", ATTRS{idProduct}=="1449", MODE="664", GROUP="plugdev"
+
 # Olimex ARM-USB-OCD
 # http://olimex.com/dev/arm-usb-ocd.html
 ATTRS{idVendor}=="15ba", ATTRS{idProduct}=="0003", MODE="664", GROUP="plugdev"
@@ -72,4 +76,8 @@
 # http://olimex.com/dev/arm-usb-tiny-h.html
 ATTRS{idVendor}=="15ba", ATTRS{idProduct}=="002a", MODE="664", GROUP="plugdev"
 
+# TIAO/DIYGADGET USB Multi-Protocol Adapter (TUMPA)
+# http://www.diygadget.com/tiao-usb-multi-protocol-adapter-jtag-spi-i2c-serial.html
+ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8a98", MODE="664", GROUP="plugdev"
+
 LABEL="flashrom_rules_end"
diff --git a/w29ee011.c b/w29ee011.c
index 4fc8853..ee5aa44 100644
--- a/w29ee011.c
+++ b/w29ee011.c
@@ -20,18 +20,20 @@
 
 #include <string.h>
 #include "flash.h"
-#include "chipdrivers.h"
 
+/* According to the Winbond W29EE011, W29EE012, W29C010M, W29C011A
+ * datasheets this is the only valid probe function for those chips.
+ */
 int probe_w29ee011(struct flashchip *flash)
 {
 	chipaddr bios = flash->virtual_memory;
 	uint8_t id1, id2;
 
-	if (!chip_to_probe || strcmp(chip_to_probe, "W29EE011")) {
-		msg_cdbg("Probing disabled for Winbond W29EE011 because "
-			     "the probing sequence puts the AMIC A49LF040A in "
-			     "a funky state. Use 'flashrom -c W29EE011' if you "
-			     "have a board with this chip.\n");
+	if (!chip_to_probe || strcmp(chip_to_probe, flash->name)) {
+		msg_cdbg("Old Winbond W29* probe method disabled because "
+			 "the probing sequence puts the AMIC A49LF040A in "
+			 "a funky state. Use 'flashrom -c %s' if you "
+			 "have a board with such a chip.\n", flash->name);
 		return 0;
 	}
 
diff --git a/w39.c b/w39.c
index 2635988..a2c1014 100644
--- a/w39.c
+++ b/w39.c
@@ -20,9 +20,8 @@
  */
 
 #include "flash.h"
-#include "chipdrivers.h"
 
-static int printlock_w39_fwh_block(struct flashchip *flash, int offset)
+static int printlock_w39_fwh_block(struct flashchip *flash, unsigned int offset)
 {
 	chipaddr wrprotect = flash->virtual_registers + offset + 2;
 	uint8_t locking;
@@ -60,7 +59,7 @@
 	return (locking & ((1 << 2) | (1 << 0))) ? -1 : 0;
 }
 
-static int unlock_w39_fwh_block(struct flashchip *flash, int offset)
+static int unlock_w39_fwh_block(struct flashchip *flash, unsigned int offset)
 {
 	chipaddr wrprotect = flash->virtual_registers + offset + 2;
 	uint8_t locking;
@@ -70,10 +69,10 @@
 	if (locking & ((1 << 2) | (1 << 0))) {
 		/* Lockdown active? */
 		if (locking & (1 << 1)) {
-			msg_cerr("Can't unlock block at 0x%x!\n", offset);
+			msg_cerr("Can't unlock block at 0x%08x!\n", offset);
 			return -1;
 		} else {
-			msg_cdbg("Unlocking block at 0x%x\n", offset);
+			msg_cdbg("Unlocking block at 0x%08x\n", offset);
 			chip_writeb(0, wrprotect);
 		}
 	}
@@ -81,7 +80,7 @@
 	return 0;
 }
 
-static uint8_t w39_idmode_readb(struct flashchip *flash, int offset)
+static uint8_t w39_idmode_readb(struct flashchip *flash, unsigned int offset)
 {
 	chipaddr bios = flash->virtual_memory;
 	uint8_t val;
@@ -128,7 +127,7 @@
 	return 0;
 }
 
-static int printlock_w39_common(struct flashchip *flash, int offset)
+static int printlock_w39_common(struct flashchip *flash, unsigned int offset)
 {
 	uint8_t lock;
 
@@ -139,7 +138,7 @@
 
 static int printlock_w39_fwh(struct flashchip *flash)
 {
-	int i, total_size = flash->total_size * 1024;
+	unsigned int i, total_size = flash->total_size * 1024;
 	int ret = 0;
 	
 	/* Print lock status of the complete chip */
@@ -151,7 +150,7 @@
 
 static int unlock_w39_fwh(struct flashchip *flash)
 {
-	int i, total_size = flash->total_size * 1024;
+	unsigned int i, total_size = flash->total_size * 1024;
 	
 	/* Unlock the complete chip */
 	for (i = 0; i < total_size; i += flash->page_size)
diff --git a/wbsio_spi.c b/wbsio_spi.c
index 851c87f..dbc7729 100644
--- a/wbsio_spi.c
+++ b/wbsio_spi.c
@@ -62,7 +62,7 @@
 
 static int wbsio_spi_send_command(unsigned int writecnt, unsigned int readcnt,
 		      const unsigned char *writearr, unsigned char *readarr);
-static int wbsio_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len);
+static int wbsio_spi_read(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len);
 
 static const struct spi_programmer spi_programmer_wbsio = {
 	.type = SPI_CONTROLLER_WBSIO,
@@ -82,10 +82,10 @@
 
 	msg_pspew("\nwbsio_spibase = 0x%x\n", wbsio_spibase);
 
-	register_spi_programmer(&spi_programmer_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;
+	register_spi_programmer(&spi_programmer_wbsio);
 
 	return 0;
 }
@@ -194,7 +194,7 @@
 	return 0;
 }
 
-static int wbsio_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+static int wbsio_spi_read(struct flashchip *flash, uint8_t *buf, unsigned int start, unsigned int len)
 {
 	return read_memmapped(flash, buf, start, len);
 }
diff --git a/wpce775x.c b/wpce775x.c
index e766e7f..ac8ffb4 100644
--- a/wpce775x.c
+++ b/wpce775x.c
@@ -709,7 +709,8 @@
 	return ret;
 }
 
-int wpce775x_spi_read(struct flashchip *flash, uint8_t * buf, int start, int len)
+int wpce775x_spi_read(struct flashchip *flash, uint8_t * buf,
+                      unsigned int start, unsigned int len)
 {
 	if (!initflash_cfg) {
 		initflash_cfg_setup(flash);
@@ -718,7 +719,8 @@
 	return spi_read_chunked(flash, buf, start, len, flash->page_size);
 }
 
-int wpce775x_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
+int wpce775x_spi_write_256(struct flashchip *flash, uint8_t *buf,
+                           unsigned int start, unsigned int len)
 {
 	if (!initflash_cfg) {
 		initflash_cfg_setup(flash);
@@ -975,7 +977,7 @@
 {
 	int ret;
 
-	if (!(buses_supported & CHIP_BUSTYPE_FWH)) {
+	if (!(buses_supported & BUS_FWH)) {
 		msg_pdbg("%s():%d buses not support FWH\n", __func__, __LINE__);
 		return 1;
 	}
@@ -984,9 +986,9 @@
 	if (!ret) {
 		msg_pdbg("%s():%d buses_supported=0x%x\n", __func__, __LINE__,
 		          buses_supported);
-		if (buses_supported & CHIP_BUSTYPE_FWH)
+		if (buses_supported & BUS_FWH)
 			msg_pdbg("Overriding chipset SPI with WPCE775x FWH|SPI.\n");
-		buses_supported |= CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI;
+		buses_supported |= BUS_FWH | BUS_SPI;
 	}
 	return ret;
 }
diff --git a/writeprotect.c b/writeprotect.c
index 0cad2ab..62ee2e7 100644
--- a/writeprotect.c
+++ b/writeprotect.c
@@ -56,13 +56,13 @@
 enum bit_state {
 	OFF	= 0,
 	ON	= 1,
-	X	= 0	/* don't care */
+	X	= -1	/* don't care. Must be bigger than max # of bp. */
 };
 
 struct w25q_range {
 	enum bit_state sec;		/* if 1, bp[2:0] describe sectors */
 	enum bit_state tb;		/* top/bottom select */
-	unsigned short int bp : 3;	/* block protect bitfield */
+	int bp;				/* block protect bitfield */
 	struct wp_range range;
 };
 
@@ -656,15 +656,19 @@
 	if (w25_range_table(flash, &w25q_ranges, &num_entries)) return -1;
 	for (i = 0; i < num_entries; i++) {
 		int bp;
+		int table_bp, table_tb, table_sec;
 
 		bp = status->bp0 | (status->bp1 << 1) | (status->bp2 << 2);
 		msg_cspew("comparing  0x%x 0x%x / 0x%x 0x%x / 0x%x 0x%x\n",
 		          bp, w25q_ranges[i].bp,
 		          status->tb, w25q_ranges[i].tb,
 		          status->sec, w25q_ranges[i].sec);
-		if ((bp == w25q_ranges[i].bp) &&
-		    (status->tb == w25q_ranges[i].tb) &&
-		    (status->sec == w25q_ranges[i].sec)) {
+		table_bp = w25q_ranges[i].bp;
+		table_tb = w25q_ranges[i].tb;
+		table_sec = w25q_ranges[i].sec;
+		if ((bp == table_bp || table_bp == X) &&
+		    (status->tb == table_tb || table_tb == X) &&
+		    (status->sec == table_sec || table_sec == X)) {
 			*start = w25q_ranges[i].range.start;
 			*len = w25q_ranges[i].range.len;
 
@@ -851,5 +855,3 @@
 	.disable	= w25_disable_writeprotect,
 	.wp_status	= w25_wp_status,
 };
-
-