
Subject: Try building for the same ms6119 board

Signed-off-by: Tadas Slotkus <devtadas@gmail.com>
---
 src/Kconfig                                 |    2 +-
 src/mainboard/msi/ms6119/Kconfig            |   72 +
 src/mainboard/msi/ms6119/Makefile.inc       |    2 +
 src/mainboard/msi/ms6119/f/Makefile.inc     |   34 +
 src/mainboard/msi/ms6119/f/arch.h           |   31 +
 src/mainboard/msi/ms6119/f/chipdrivers.h    |  147 +
 src/mainboard/msi/ms6119/f/chipset_enable.c | 1256 ++++
 src/mainboard/msi/ms6119/f/cli_classic.c    |  138 +
 src/mainboard/msi/ms6119/f/f.h              |    2 +
 src/mainboard/msi/ms6119/f/flash.h          |  307 +
 src/mainboard/msi/ms6119/f/flashchips.c     | 8657 +++++++++++++++++++++++++++
 src/mainboard/msi/ms6119/f/flashchips.h     |  641 ++
 src/mainboard/msi/ms6119/f/flashrom.c       | 1832 ++++++
 src/mainboard/msi/ms6119/f/hwaccess.c       |  266 +
 src/mainboard/msi/ms6119/f/hwaccess.h       |  338 ++
 src/mainboard/msi/ms6119/f/internal.c       |  265 +
 src/mainboard/msi/ms6119/f/jedec.c          |  507 ++
 src/mainboard/msi/ms6119/f/pcidev.c         |  310 +
 src/mainboard/msi/ms6119/f/physmap.c        |  525 ++
 src/mainboard/msi/ms6119/f/programmer.c     |  107 +
 src/mainboard/msi/ms6119/f/programmer.h     |  608 ++
 src/mainboard/msi/ms6119/romstage.c         |    7 +
 22 files changed, 16053 insertions(+), 1 deletions(-)
 create mode 100644 src/mainboard/msi/ms6119/Makefile.inc
 create mode 100644 src/mainboard/msi/ms6119/f/Makefile.inc
 create mode 100644 src/mainboard/msi/ms6119/f/arch.h
 create mode 100644 src/mainboard/msi/ms6119/f/chipdrivers.h
 create mode 100644 src/mainboard/msi/ms6119/f/chipset_enable.c
 create mode 100644 src/mainboard/msi/ms6119/f/cli_classic.c
 create mode 100644 src/mainboard/msi/ms6119/f/f.h
 create mode 100644 src/mainboard/msi/ms6119/f/flash.h
 create mode 100644 src/mainboard/msi/ms6119/f/flashchips.c
 create mode 100644 src/mainboard/msi/ms6119/f/flashchips.h
 create mode 100644 src/mainboard/msi/ms6119/f/flashrom.c
 create mode 100644 src/mainboard/msi/ms6119/f/hwaccess.c
 create mode 100644 src/mainboard/msi/ms6119/f/hwaccess.h
 create mode 100644 src/mainboard/msi/ms6119/f/internal.c
 create mode 100644 src/mainboard/msi/ms6119/f/jedec.c
 create mode 100644 src/mainboard/msi/ms6119/f/pcidev.c
 create mode 100644 src/mainboard/msi/ms6119/f/physmap.c
 create mode 100644 src/mainboard/msi/ms6119/f/programmer.c
 create mode 100644 src/mainboard/msi/ms6119/f/programmer.h

diff --git a/src/Kconfig b/src/Kconfig
index 7b4f487..cba0529 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -853,7 +853,7 @@ config ENABLE_APIC_EXT_ID
 
 config WARNINGS_ARE_ERRORS
 	bool
-	default y
+	default n
 
 config ID_SECTION_OFFSET
 	hex
diff --git a/src/mainboard/msi/ms6119/Kconfig b/src/mainboard/msi/ms6119/Kconfig
index b9a585e..9130ae8 100644
--- a/src/mainboard/msi/ms6119/Kconfig
+++ b/src/mainboard/msi/ms6119/Kconfig
@@ -41,4 +41,76 @@ config IRQ_SLOT_COUNT
 	int
 	default 7
 
+config INTERNAL
+	int
+	default 1
+
+config DUMMY
+	int
+	default 0
+
+config NIC3COM
+	int
+	default 0
+
+config NICREALTEK
+	int
+	default 0
+
+config NICNATSEMI
+	int
+	default 0
+
+config GFXNVIDIA
+	int
+	default 0
+
+config DRKAISER
+	int
+	default 0
+
+config SATASII
+	int
+	default 0
+
+config ATAHPT
+	int
+	default 0
+
+config FT2232_SPI
+	int
+	default 0
+
+config SERPROG
+	int
+	default 0
+
+config BUSPIRATE_SPI
+	int
+	default 0
+
+config DEDIPROG
+	int
+	default 0
+
+config RAYER_SPI
+	int
+	default 0
+
+config NICINTEL
+	int
+	default 0
+
+config NICINTEL_SPI
+	int
+	default 0
+
+config OGP_SPI
+	int
+	default 0
+
+config SATAMV
+	int
+	default 0
+
 endif # BOARD_MSI_MS_6119
diff --git a/src/mainboard/msi/ms6119/Makefile.inc b/src/mainboard/msi/ms6119/Makefile.inc
new file mode 100644
index 0000000..d760190
--- /dev/null
+++ b/src/mainboard/msi/ms6119/Makefile.inc
@@ -0,0 +1,2 @@
+include $(TOPLEVEL)/src/mainboard/msi/ms6119/f/Makefile.inc
+romstage-y += $(addprefix f/,$(OBJS))
diff --git a/src/mainboard/msi/ms6119/f/Makefile.inc b/src/mainboard/msi/ms6119/f/Makefile.inc
new file mode 100644
index 0000000..f218f42
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/Makefile.inc
@@ -0,0 +1,34 @@
+#
+# This file is part of the flashrom project.
+#
+# Copyright (C) 2005 coresystems GmbH <stepan@coresystems.de>
+# Copyright (C) 2009,2010 Carl-Daniel Hailfinger
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+#
+
+CHIP_OBJS = jedec.c flashchips.c
+
+CLI_OBJS = flashrom.c cli_classic.c
+
+PROGRAMMER_OBJS = programmer.c
+
+NEED_PCI := 1
+CONFIG_INTERNAL := 1
+
+PROGRAMMER_OBJS += chipset_enable.c internal.c
+PROGRAMMER_OBJS += physmap.c hwaccess.c pcidev.c
+
+LIBFLASHROM_OBJS = $(CHIP_OBJS) $(PROGRAMMER_OBJS)
+OBJS = $(CLI_OBJS) $(LIBFLASHROM_OBJS)
diff --git a/src/mainboard/msi/ms6119/f/arch.h b/src/mainboard/msi/ms6119/f/arch.h
new file mode 100644
index 0000000..980fba6
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/arch.h
@@ -0,0 +1,31 @@
+/*
+ * 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
+ */
+
+/*
+ * Header file for CPU architecture checking.
+ */
+
+#if defined (__i386__) || defined (__x86_64__)
+#define __FLASHROM_ARCH__ "x86"
+#elif defined (__mips) || defined (__mips__) || defined (_mips) || defined (mips)
+#define __FLASHROM_ARCH__ "mips"
+#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || defined(__ppc64__)
+#define __FLASHROM_ARCH__ "ppc"
+#endif
+__FLASHROM_ARCH__
diff --git a/src/mainboard/msi/ms6119/f/chipdrivers.h b/src/mainboard/msi/ms6119/f/chipdrivers.h
new file mode 100644
index 0000000..92ddbea
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/chipdrivers.h
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ *
+ * Header file for flash chip drivers. Included from flash.h.
+ * As a general rule, every function listed here should take a pointer to
+ * struct flashchip as first parameter.
+ */
+
+#ifndef __CHIPDRIVERS_H__
+#define __CHIPDRIVERS_H__ 1
+
+/* spi.c, should probably be in spi_chip.c */
+int probe_spi_rdid(struct flashchip *flash);
+int probe_spi_rdid4(struct flashchip *flash);
+int probe_spi_rems(struct flashchip *flash);
+int probe_spi_res1(struct flashchip *flash);
+int probe_spi_res2(struct flashchip *flash);
+int spi_write_enable(void);
+int spi_write_disable(void);
+int spi_block_erase_20(struct flashchip *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_52(struct flashchip *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_d7(struct flashchip *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_d8(struct flashchip *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_60(struct flashchip *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_c7(struct flashchip *flash, unsigned int addr, unsigned int blocklen);
+int spi_chip_write_1(struct flashchip *flash, uint8_t *buf, int 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);
+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);
+void spi_prettyprint_status_register_bp3210(uint8_t status, int bp);
+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);
+
+/* a25.c */
+int spi_prettyprint_status_register_amic_a25l05p(struct flashchip *flash);
+int spi_prettyprint_status_register_amic_a25l40p(struct flashchip *flash);
+int spi_prettyprint_status_register_amic_a25l032(struct flashchip *flash);
+int spi_prettyprint_status_register_amic_a25lq032(struct flashchip *flash);
+
+/* at25.c */
+int spi_prettyprint_status_register_at25df(struct flashchip *flash);
+int spi_prettyprint_status_register_at25df_sec(struct flashchip *flash);
+int spi_prettyprint_status_register_at25f(struct flashchip *flash);
+int spi_prettyprint_status_register_at25fs010(struct flashchip *flash);
+int spi_prettyprint_status_register_at25fs040(struct flashchip *flash);
+int spi_prettyprint_status_register_atmel_at26df081a(struct flashchip *flash);
+int spi_disable_blockprotect_at25df(struct flashchip *flash);
+int spi_disable_blockprotect_at25df_sec(struct flashchip *flash);
+int spi_disable_blockprotect_at25f(struct flashchip *flash);
+int spi_disable_blockprotect_at25fs010(struct flashchip *flash);
+int spi_disable_blockprotect_at25fs040(struct flashchip *flash);
+
+/* 82802ab.c */
+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);
+void print_status_82802ab(uint8_t status);
+int unlock_82802ab(struct flashchip *flash);
+int unlock_28f004s5(struct flashchip *flash);
+
+/* jedec.c */
+uint8_t oddparity(uint8_t val);
+void toggle_ready_jedec(chipaddr dst);
+void data_polling_jedec(chipaddr dst, uint8_t data);
+int write_byte_program_jedec(chipaddr bios, uint8_t *src,
+			     chipaddr dst);
+int probe_jedec(struct flashchip *flash);
+int 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 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);
+
+/* m29f400bt.c */
+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);
+void protect_m29f400bt(chipaddr bios);
+
+/* pm49fl00x.c */
+int unlock_49fl00x(struct flashchip *flash);
+int lock_49fl00x(struct flashchip *flash);
+
+/* 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 unprotect_28sf040(struct flashchip *flash);
+int protect_28sf040(struct flashchip *flash);
+
+/* sst49lfxxxc.c */
+int erase_sector_49lfxxxc(struct flashchip *flash, unsigned int address, unsigned int sector_size);
+int unlock_49lfxxxc(struct flashchip *flash);
+
+/* sst_fwhub.c */
+int printlock_sst_fwhub(struct flashchip *flash);
+int unlock_sst_fwhub(struct flashchip *flash);
+
+/* w39.c */
+int printlock_w39l040(struct flashchip * flash);
+int printlock_w39v040a(struct flashchip *flash);
+int printlock_w39v040b(struct flashchip *flash);
+int printlock_w39v040c(struct flashchip *flash);
+int printlock_w39v040fa(struct flashchip *flash);
+int printlock_w39v040fb(struct flashchip *flash);
+int printlock_w39v040fc(struct flashchip *flash);
+int printlock_w39v080a(struct flashchip *flash);
+int printlock_w39v080fa(struct flashchip *flash);
+int printlock_w39v080fa_dual(struct flashchip *flash);
+int unlock_w39v040fb(struct flashchip *flash);
+int unlock_w39v080fa(struct flashchip *flash);
+
+/* w29ee011.c */
+int probe_w29ee011(struct flashchip *flash);
+
+/* stm50flw0x0x.c */
+int erase_sector_stm50flw0x0x(struct flashchip *flash, unsigned int block, unsigned int blocksize);
+int unlock_stm50flw0x0x(struct flashchip *flash);
+
+#endif /* !__CHIPDRIVERS_H__ */
diff --git a/src/mainboard/msi/ms6119/f/chipset_enable.c b/src/mainboard/msi/ms6119/f/chipset_enable.c
new file mode 100644
index 0000000..d84a1c3
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/chipset_enable.c
@@ -0,0 +1,1256 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2005-2009 coresystems GmbH
+ * Copyright (C) 2006 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2007,2008,2009 Carl-Daniel Hailfinger
+ * Copyright (C) 2009 Kontron Modular Computers GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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 chipset specific flash enables.
+ */
+
+#define _LARGEFILE64_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "flash.h"
+#include "programmer.h"
+
+#define NOT_DONE_YET 1
+
+#if defined(__i386__) || defined(__x86_64__)
+#ifdef ts
+static int enable_flash_ali_m1533(struct pci_dev *dev, const char *name)
+{
+	uint8_t tmp;
+
+	/*
+	 * ROM Write enable, 0xFFFC0000-0xFFFDFFFF and
+	 * 0xFFFE0000-0xFFFFFFFF ROM select enable.
+	 */
+	tmp = pci_read_byte(dev, 0x47);
+	tmp |= 0x46;
+	rpci_write_byte(dev, 0x47, tmp);
+
+	return 0;
+}
+
+static int enable_flash_sis85c496(struct pci_dev *dev, const char *name)
+{
+	uint8_t tmp;
+
+	tmp = pci_read_byte(dev, 0xd0);
+	tmp |= 0xf8;
+	rpci_write_byte(dev, 0xd0, tmp);
+
+	return 0;
+}
+
+static int enable_flash_sis_mapping(struct pci_dev *dev, const char *name)
+{
+	uint8_t new, newer;
+
+	/* Extended BIOS enable = 1, Lower BIOS Enable = 1 */
+	/* This is 0xFFF8000~0xFFFF0000 decoding on SiS 540/630. */
+	new = pci_read_byte(dev, 0x40);
+	new &= (~0x04); /* No idea why we clear bit 2. */
+	new |= 0xb; /* 0x3 for some chipsets, bit 7 seems to be don't care. */
+	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("Stuck at 0x%x\n", newer);
+		return -1;
+	}
+	return 0;
+}
+
+static struct pci_dev *find_southbridge(uint16_t vendor, const char *name)
+{
+	struct pci_dev *sbdev;
+	
+	sbdev = pci_dev_find_vendorclass(vendor, 0x0601);
+	if (!sbdev)
+		sbdev = pci_dev_find_vendorclass(vendor, 0x0680);
+	if (!sbdev)
+		sbdev = pci_dev_find_vendorclass(vendor, 0x0000);
+	if (!sbdev)
+		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);
+	return sbdev;
+}
+
+static int enable_flash_sis501(struct pci_dev *dev, const char *name)
+{
+	uint8_t tmp;
+	int ret = 0;
+	struct pci_dev *sbdev;
+
+	sbdev = find_southbridge(dev->vendor_id, name);
+	if (!sbdev)
+		return -1;
+
+	ret = enable_flash_sis_mapping(sbdev, name);
+
+	tmp = sio_read(0x22, 0x80);
+	tmp &= (~0x20);
+	tmp |= 0x4;
+	sio_write(0x22, 0x80, tmp);
+
+	tmp = sio_read(0x22, 0x70);
+	tmp &= (~0x20);
+	tmp |= 0x4;
+	sio_write(0x22, 0x70, tmp);
+	
+	return ret;
+}
+
+static int enable_flash_sis5511(struct pci_dev *dev, const char *name)
+{
+	uint8_t tmp;
+	int ret = 0;
+	struct pci_dev *sbdev;
+
+	sbdev = find_southbridge(dev->vendor_id, name);
+	if (!sbdev)
+		return -1;
+
+	ret = enable_flash_sis_mapping(sbdev, name);
+
+	tmp = sio_read(0x22, 0x50);
+	tmp &= (~0x20);
+	tmp |= 0x4;
+	sio_write(0x22, 0x50, tmp);
+
+	return ret;
+}
+
+static int enable_flash_sis530(struct pci_dev *dev, const char *name)
+{
+	uint8_t new, newer;
+	int ret = 0;
+	struct pci_dev *sbdev;
+
+	sbdev = find_southbridge(dev->vendor_id, name);
+	if (!sbdev)
+		return -1;
+
+	ret = enable_flash_sis_mapping(sbdev, name);
+
+	new = pci_read_byte(sbdev, 0x45);
+	new &= (~0x20);
+	new |= 0x4;
+	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("Stuck at 0x%x\n", newer);
+		ret = -1;
+	}
+
+	return ret;
+}
+
+static int enable_flash_sis540(struct pci_dev *dev, const char *name)
+{
+	uint8_t new, newer;
+	int ret = 0;
+	struct pci_dev *sbdev;
+
+	sbdev = find_southbridge(dev->vendor_id, name);
+	if (!sbdev)
+		return -1;
+
+	ret = enable_flash_sis_mapping(sbdev, name);
+
+	new = pci_read_byte(sbdev, 0x45);
+	new &= (~0x80);
+	new |= 0x40;
+	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("Stuck at 0x%x\n", newer);
+		ret = -1;
+	}
+
+	return ret;
+}
+#endif
+/* Datasheet:
+ *   - Name: 82371AB PCI-TO-ISA / IDE XCELERATOR (PIIX4)
+ *   - URL: http://www.intel.com/design/intarch/datashts/290562.htm
+ *   - PDF: http://www.intel.com/design/intarch/datashts/29056201.pdf
+ *   - Order Number: 290562-001
+ */
+static int enable_flash_piix4(struct pci_dev *dev, const char *name)
+{
+#ifdef ts
+	uint16_t old, new;
+	uint16_t xbcs = 0x4e;	/* X-Bus Chip Select register. */
+
+	buses_supported = CHIP_BUSTYPE_PARALLEL;
+
+	old = pci_read_config16(dev, xbcs);
+
+	/* Set bit 9: 1-Meg Extended BIOS Enable (PCI master accesses to
+	 *            FFF00000-FFF7FFFF are forwarded to ISA).
+	 *            Note: This bit is reserved on PIIX/PIIX3/MPIIX.
+	 * Set bit 7: Extended BIOS Enable (PCI master accesses to
+	 *            FFF80000-FFFDFFFF are forwarded to ISA).
+	 * Set bit 6: Lower BIOS Enable (PCI master, or ISA master accesses to
+	 *            the lower 64-Kbyte BIOS block (E0000-EFFFF) at the top
+	 *            of 1 Mbyte, or the aliases at the top of 4 Gbyte
+	 *            (FFFE0000-FFFEFFFF) result in the generation of BIOSCS#.
+	 * Note: Accesses to FFFF0000-FFFFFFFF are always forwarded to ISA.
+	 * Set bit 2: BIOSCS# Write Enable (1=enable, 0=disable).
+	 */
+	if (dev->device_id == 0x122e || dev->device_id == 0x7000
+	    || dev->device_id == 0x1234)
+		new = old | 0x00c4; /* PIIX/PIIX3/MPIIX: Bit 9 is reserved. */
+	else
+		new = old | 0x02c4;
+
+	if (new == old)
+		return 0;
+
+	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);
+		return -1;
+	}
+#endif
+	return 0;
+}
+#ifdef ts
+/*
+ * See ie. page 375 of "Intel I/O Controller Hub 7 (ICH7) Family Datasheet"
+ * http://download.intel.com/design/chipsets/datashts/30701303.pdf
+ */
+static int enable_flash_ich(struct pci_dev *dev, const char *name,
+			    int bios_cntl)
+{
+	uint8_t old, new;
+
+	/*
+	 * 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);
+
+	msg_pdbg("\nBIOS Lock Enable: %sabled, ",
+		     (old & (1 << 1)) ? "en" : "dis");
+	msg_pdbg("BIOS Write Enable: %sabled, ",
+		     (old & (1 << 0)) ? "en" : "dis");
+	msg_pdbg("BIOS_CNTL is 0x%x\n", old);
+
+	/*
+	 * Quote from the 6 Series datasheet (Document Number: 324645-004):
+	 * "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. */
+	if (old & (1 << 5)) {
+		msg_pinfo("WARNING: BIOS region SMM protection is enabled!\n");
+	}
+
+	new = old | 1;
+	if (new == old)
+		return 0;
+
+	rpci_write_byte(dev, bios_cntl, new);
+
+	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);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int enable_flash_ich_4e(struct pci_dev *dev, const char *name)
+{
+	/*
+	 * Note: ICH5 has registers similar to FWH_SEL1, FWH_SEL2 and
+	 * FWH_DEC_EN1, but they are called FB_SEL1, FB_SEL2, FB_DEC_EN1 and
+	 * FB_DEC_EN2.
+	 */
+	buses_supported = CHIP_BUSTYPE_FWH;
+	return enable_flash_ich(dev, name, 0x4e);
+}
+
+static int enable_flash_ich_dc(struct pci_dev *dev, const char *name)
+{
+	uint32_t fwh_conf;
+	int i;
+	char *idsel = NULL;
+	int tmp;
+	int max_decode_fwh_idsel = 0;
+	int max_decode_fwh_decode = 0;
+	int contiguous = 1;
+
+	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);
+		/* FIXME: Decode settings are not changed. */
+	} else if (idsel) {
+		msg_perr("Error: idsel= specified, but no number given.\n");
+		free(idsel);
+		/* FIXME: Return failure here once internal_init() starts
+		 * to care about the return value of the chipset enable.
+		 */
+		exit(1);
+	}
+	free(idsel);
+
+	/* Ignore all legacy ranges below 1 MB.
+	 * We currently only support flashing the chip which responds to
+	 * IDSEL=0. To support IDSEL!=0, flashbase and decode size calculations
+	 * have to be adjusted.
+	 */
+	/* FWH_SEL1 */
+	fwh_conf = pci_read_long(dev, 0xd0);
+	for (i = 7; i >= 0; i--) {
+		tmp = (fwh_conf >> (i * 4)) & 0xf;
+		msg_pdbg("\n0x%08x/0x%08x FWH IDSEL: 0x%x",
+			     (0x1ff8 + i) * 0x80000,
+			     (0x1ff0 + i) * 0x80000,
+			     tmp);
+		if ((tmp == 0) && contiguous) {
+			max_decode_fwh_idsel = (8 - i) * 0x80000;
+		} else {
+			contiguous = 0;
+		}
+	}
+	/* FWH_SEL2 */
+	fwh_conf = pci_read_word(dev, 0xd4);
+	for (i = 3; i >= 0; i--) {
+		tmp = (fwh_conf >> (i * 4)) & 0xf;
+		msg_pdbg("\n0x%08x/0x%08x FWH IDSEL: 0x%x",
+			     (0xff4 + i) * 0x100000,
+			     (0xff0 + i) * 0x100000,
+			     tmp);
+		if ((tmp == 0) && contiguous) {
+			max_decode_fwh_idsel = (8 - i) * 0x100000;
+		} else {
+			contiguous = 0;
+		}
+	}
+	contiguous = 1;
+	/* FWH_DEC_EN1 */
+	fwh_conf = pci_read_word(dev, 0xd8);
+	for (i = 7; i >= 0; i--) {
+		tmp = (fwh_conf >> (i + 0x8)) & 0x1;
+		msg_pdbg("\n0x%08x/0x%08x FWH decode %sabled",
+			     (0x1ff8 + i) * 0x80000,
+			     (0x1ff0 + i) * 0x80000,
+			     tmp ? "en" : "dis");
+		if ((tmp == 1) && contiguous) {
+			max_decode_fwh_decode = (8 - i) * 0x80000;
+		} else {
+			contiguous = 0;
+		}
+	}
+	for (i = 3; i >= 0; i--) {
+		tmp = (fwh_conf >> i) & 0x1;
+		msg_pdbg("\n0x%08x/0x%08x FWH decode %sabled",
+			     (0xff4 + i) * 0x100000,
+			     (0xff0 + i) * 0x100000,
+			     tmp ? "en" : "dis");
+		if ((tmp == 1) && contiguous) {
+			max_decode_fwh_decode = (8 - i) * 0x100000;
+		} else {
+			contiguous = 0;
+		}
+	}
+	max_rom_decode.fwh = min(max_decode_fwh_idsel, max_decode_fwh_decode);
+	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.
+	 */
+	buses_supported = CHIP_BUSTYPE_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;
+
+       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;
+
+       if (new != old)
+               rpci_write_byte(dev, 0xd9, new);
+
+	buses_supported = CHIP_BUSTYPE_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)
+{
+	/* 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;
+	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" };
+
+	/* Enable Flash Writes */
+	ret = enable_flash_ich_dc(dev, name);
+
+	/* Get physical address of Root Complex Register Block */
+	tmp = pci_read_long(dev, 0xf0) & 0xffffc000;
+	msg_pdbg("\nRoot Complex Register Block address = 0x%x\n", tmp);
+
+	/* Map RCBA to virtual memory */
+	rcrb = physmap("ICH RCRB", tmp, 0x4000);
+
+	gcs = mmio_readl(rcrb + 0x3410);
+	msg_pdbg("GCS = 0x%x: ", gcs);
+	msg_pdbg("BIOS Interface Lock-Down: %sabled, ",
+		     (gcs & 0x1) ? "en" : "dis");
+	bbs = (gcs >> 10) & 0x3;
+	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");
+
+	/* 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 */
+			return ret;
+		}
+		else
+			/* Disable LPC/FWH if strapped to PCI or SPI */
+			buses_supported = 0;
+	}
+
+	/* this adds CHIP_BUSTYPE_SPI */
+	if (ich_init_spi(dev, tmp, rcrb, ich_generation) != 0) {
+	        if (!ret)
+		        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);
+}
+
+static int enable_flash_ich8(struct pci_dev *dev, const char *name)
+{
+	return enable_flash_ich_dc_spi(dev, name, 8);
+}
+
+static int enable_flash_ich9(struct pci_dev *dev, const char *name)
+{
+	return enable_flash_ich_dc_spi(dev, name, 9);
+}
+
+static int enable_flash_ich10(struct pci_dev *dev, const char *name)
+{
+	return enable_flash_ich_dc_spi(dev, name, 10);
+}
+
+static int via_no_byte_merge(struct pci_dev *dev, const char *name)
+{
+	uint8_t val;
+
+	val = pci_read_byte(dev, 0x71);
+	if (val & 0x40)
+	{
+		msg_pdbg("Disabling byte merging\n");
+		val &= ~0x40;
+		rpci_write_byte(dev, 0x71, val);
+	}
+	return NOT_DONE_YET;	/* need to find south bridge, too */
+}
+
+static int enable_flash_vt823x(struct pci_dev *dev, const char *name)
+{
+	uint8_t val;
+
+	/* enable ROM decode range (1MB) FFC00000 - FFFFFFFF */
+	rpci_write_byte(dev, 0x41, 0x7f);
+
+	/* ROM write enable */
+	val = pci_read_byte(dev, 0x40);
+	val |= 0x10;
+	rpci_write_byte(dev, 0x40, val);
+
+	if (pci_read_byte(dev, 0x40) != val) {
+		msg_pinfo("\nWARNING: Failed to enable flash write on \"%s\"\n",
+		       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);
+	}
+
+	return 0;
+}
+
+static int enable_flash_cs5530(struct pci_dev *dev, const char *name)
+{
+	uint8_t reg8;
+
+#define DECODE_CONTROL_REG2		0x5b	/* F0 index 0x5b */
+#define ROM_AT_LOGIC_CONTROL_REG	0x52	/* F0 index 0x52 */
+#define CS5530_RESET_CONTROL_REG	0x44	/* F0 index 0x44 */
+#define CS5530_USB_SHADOW_REG		0x43	/* F0 index 0x43 */
+
+#define LOWER_ROM_ADDRESS_RANGE		(1 << 0)
+#define ROM_WRITE_ENABLE		(1 << 1)
+#define UPPER_ROM_ADDRESS_RANGE		(1 << 2)
+#define BIOS_ROM_POSITIVE_DECODE	(1 << 5)
+#define CS5530_ISA_MASTER		(1 << 7)
+#define CS5530_ENABLE_SA2320		(1 << 2)
+#define CS5530_ENABLE_SA20		(1 << 6)
+
+	buses_supported = CHIP_BUSTYPE_PARALLEL;
+	/* Decode 0x000E0000-0x000FFFFF (128 kB), not just 64 kB, and
+	 * decode 0xFF000000-0xFFFFFFFF (16 MB), not just 256 kB.
+	 * FIXME: Should we really touch the low mapping below 1 MB? Flashrom
+	 * ignores that region completely.
+	 * Make the configured ROM areas writable.
+	 */
+	reg8 = pci_read_byte(dev, ROM_AT_LOGIC_CONTROL_REG);
+	reg8 |= LOWER_ROM_ADDRESS_RANGE;
+	reg8 |= UPPER_ROM_ADDRESS_RANGE;
+	reg8 |= ROM_WRITE_ENABLE;
+	rpci_write_byte(dev, ROM_AT_LOGIC_CONTROL_REG, reg8);
+
+	/* Set positive decode on ROM. */
+	reg8 = pci_read_byte(dev, DECODE_CONTROL_REG2);
+	reg8 |= BIOS_ROM_POSITIVE_DECODE;
+	rpci_write_byte(dev, DECODE_CONTROL_REG2, reg8);
+
+	reg8 = pci_read_byte(dev, CS5530_RESET_CONTROL_REG);
+	if (reg8 & CS5530_ISA_MASTER) {
+		/* We have A0-A23 available. */
+		max_rom_decode.parallel = 16 * 1024 * 1024;
+	} else {
+		reg8 = pci_read_byte(dev, CS5530_USB_SHADOW_REG);
+		if (reg8 & CS5530_ENABLE_SA2320) {
+			/* We have A0-19, A20-A23 available. */
+			max_rom_decode.parallel = 16 * 1024 * 1024;
+		} else if (reg8 & CS5530_ENABLE_SA20) {
+			/* We have A0-19, A20 available. */
+			max_rom_decode.parallel = 2 * 1024 * 1024;
+		} else {
+			/* A20 and above are not active. */
+			max_rom_decode.parallel = 1024 * 1024;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Geode systems write protect the BIOS via RCONFs (cache settings similar
+ * to MTRRs). To unlock, change MSR 0x1808 top byte to 0x22. 
+ *
+ * Geode systems also write protect the NOR flash chip itself via MSR_NORF_CTL.
+ * To enable write to NOR Boot flash for the benefit of systems that have such
+ * a setup, raise MSR 0x51400018 WE_CS3 (write enable Boot Flash Chip Select).
+ */
+static int enable_flash_cs5536(struct pci_dev *dev, const char *name)
+{
+#define MSR_RCONF_DEFAULT	0x1808
+#define MSR_NORF_CTL		0x51400018
+
+	msr_t msr;
+
+	/* Geode only has a single core */
+	if (setup_cpu_msr(0))
+		return -1;
+
+	msr = rdmsr(MSR_RCONF_DEFAULT);
+	if ((msr.hi >> 24) != 0x22) {
+		msr.hi &= 0xfbffffff;
+		wrmsr(MSR_RCONF_DEFAULT, msr);
+	}
+
+	msr = rdmsr(MSR_NORF_CTL);
+	/* Raise WE_CS3 bit. */
+	msr.lo |= 0x08;
+	wrmsr(MSR_NORF_CTL, msr);
+
+	cleanup_cpu_msr();
+
+#undef MSR_RCONF_DEFAULT
+#undef MSR_NORF_CTL
+	return 0;
+}
+
+static int enable_flash_sc1100(struct pci_dev *dev, const char *name)
+{
+	uint8_t new;
+
+	rpci_write_byte(dev, 0x52, 0xee);
+
+	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);
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Works for AMD-8111, VIA VT82C586A/B, VIA VT82C686A/B. */
+static int enable_flash_amd8111(struct pci_dev *dev, const char *name)
+{
+	uint8_t old, new;
+
+	/* Enable decoding at 0xffb00000 to 0xffffffff. */
+	old = pci_read_byte(dev, 0x43);
+	new = old | 0xC0;
+	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);
+		}
+	}
+
+	/* Enable 'ROM write' bit. */
+	old = pci_read_byte(dev, 0x40);
+	new = old | 0x01;
+	if (new == old)
+		return 0;
+	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);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int enable_flash_sb600(struct pci_dev *dev, const char *name)
+{
+	uint32_t prot;
+	uint8_t reg;
+	int ret;
+
+	/* Clear ROM protect 0-3. */
+	for (reg = 0x50; reg < 0x60; reg += 4) {
+		prot = pci_read_long(dev, reg);
+		/* No protection flags for this region?*/
+		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 &= 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));
+	}
+
+	buses_supported = CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH;
+
+	ret = sb600_probe_spi(dev);
+
+	/* Read ROM strap override register. */
+	OUTB(0x8f, 0xcd6);
+	reg = INB(0xcd7);
+	reg &= 0x0e;
+	msg_pdbg("ROM strap override is %sactive", (reg & 0x02) ? "" : "not ");
+	if (reg & 0x02) {
+		switch ((reg & 0x0c) >> 2) {
+		case 0x00:
+			msg_pdbg(": LPC");
+			break;
+		case 0x01:
+			msg_pdbg(": PCI");
+			break;
+		case 0x02:
+			msg_pdbg(": FWH");
+			break;
+		case 0x03:
+			msg_pdbg(": SPI");
+			break;
+		}
+	}
+	msg_pdbg("\n");
+
+	/* Force enable SPI ROM in SB600 PM register.
+	 * If we enable SPI ROM here, we have to disable it after we leave.
+	 * But how can we know which ROM we are going to handle? So we have
+	 * to trade off. We only access LPC ROM if we boot via LPC ROM. And
+	 * only SPI ROM if we boot via SPI ROM. If you want to access SPI on
+	 * boards with LPC straps, you have to use the code below.
+	 */
+	/*
+	OUTB(0x8f, 0xcd6);
+	OUTB(0x0e, 0xcd7);
+	*/
+
+	return ret;
+}
+
+static int enable_flash_nvidia_nforce2(struct pci_dev *dev, const char *name)
+{
+	uint8_t tmp;
+
+	rpci_write_byte(dev, 0x92, 0);
+
+	tmp = pci_read_byte(dev, 0x6d);
+	tmp |= 0x01;
+	rpci_write_byte(dev, 0x6d, tmp);
+
+	return 0;
+}
+
+static int enable_flash_ck804(struct pci_dev *dev, const char *name)
+{
+	uint8_t old, new;
+
+	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);
+		}
+	}
+
+	old = pci_read_byte(dev, 0x6d);
+	new = old | 0x01;
+	if (new == old)
+		return 0;
+	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);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int enable_flash_osb4(struct pci_dev *dev, const char *name)
+{
+	uint8_t tmp;
+
+	buses_supported = CHIP_BUSTYPE_PARALLEL;
+
+	tmp = INB(0xc06);
+	tmp |= 0x1;
+	OUTB(tmp, 0xc06);
+
+	tmp = INB(0xc6f);
+	tmp |= 0x40;
+	OUTB(tmp, 0xc6f);
+
+	return 0;
+}
+
+/* ATI Technologies Inc IXP SB400 PCI-ISA Bridge (rev 80) */
+static int enable_flash_sb400(struct pci_dev *dev, const char *name)
+{
+	uint8_t tmp;
+	struct pci_dev *smbusdev;
+
+	/* Look for the SMBus device. */
+	smbusdev = pci_dev_find(0x1002, 0x4372);
+
+	if (!smbusdev) {
+		msg_perr("ERROR: SMBus device not found. Aborting.\n");
+		exit(1);
+	}
+
+	/* Enable some SMBus stuff. */
+	tmp = pci_read_byte(smbusdev, 0x79);
+	tmp |= 0x01;
+	rpci_write_byte(smbusdev, 0x79, tmp);
+
+	/* Change southbridge. */
+	tmp = pci_read_byte(dev, 0x48);
+	tmp |= 0x21;
+	rpci_write_byte(dev, 0x48, tmp);
+
+	/* Now become a bit silly. */
+	tmp = INB(0xc6f);
+	OUTB(tmp, 0xeb);
+	OUTB(tmp, 0xeb);
+	tmp |= 0x40;
+	OUTB(tmp, 0xc6f);
+	OUTB(tmp, 0xeb);
+	OUTB(tmp, 0xeb);
+
+	return 0;
+}
+
+static int enable_flash_mcp55(struct pci_dev *dev, const char *name)
+{
+	uint8_t old, new, val;
+	uint16_t wordval;
+
+	/* Set the 0-16 MB enable bits. */
+	val = pci_read_byte(dev, 0x88);
+	val |= 0xff;		/* 256K */
+	rpci_write_byte(dev, 0x88, val);
+	val = pci_read_byte(dev, 0x8c);
+	val |= 0xff;		/* 1M */
+	rpci_write_byte(dev, 0x8c, val);
+	wordval = pci_read_word(dev, 0x90);
+	wordval |= 0x7fff;	/* 16M */
+	rpci_write_word(dev, 0x90, wordval);
+
+	old = pci_read_byte(dev, 0x6d);
+	new = old | 0x01;
+	if (new == old)
+		return 0;
+	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);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * The MCP6x/MCP7x code is based on cleanroom reverse engineering.
+ * It is assumed that LPC chips need the MCP55 code and SPI chips need the
+ * code provided in enable_flash_mcp6x_7x_common.
+ */
+static int enable_flash_mcp6x_7x(struct pci_dev *dev, const char *name)
+{
+	int ret = 0;
+	int want_spi = 0;
+	uint8_t val;
+
+	msg_pinfo("This chipset is not really supported yet. Guesswork...\n");
+
+	/* dev is the ISA bridge. No idea what the stuff below does. */
+	val = pci_read_byte(dev, 0x8a);
+	msg_pdbg("ISA/LPC bridge reg 0x8a contents: 0x%02x, bit 6 is %i, bit 5 "
+		 "is %i\n", val, (val >> 6) & 0x1, (val >> 5) & 0x1);
+
+	switch ((val >> 5) & 0x3) {
+	case 0x0:
+		ret = enable_flash_mcp55(dev, name);
+		buses_supported = CHIP_BUSTYPE_LPC;
+		msg_pdbg("Flash bus type is LPC\n");
+		break;
+	case 0x2:
+		want_spi = 1;
+		/* SPI is added in mcp6x_spi_init if it works.
+		 * Do we really want to disable LPC in this case?
+		 */
+		buses_supported = CHIP_BUSTYPE_NONE;
+		msg_pdbg("Flash bus type is SPI\n");
+		msg_perr("SPI on this chipset is WIP. Write is unsupported!\n");
+		programmer_may_write = 0;
+		break;
+	default:
+		/* Should not happen. */
+		buses_supported = CHIP_BUSTYPE_NONE;
+		msg_pdbg("Flash bus type is unknown (none)\n");
+		msg_pinfo("Something went wrong with bus type detection.\n");
+		goto out_msg;
+		break;
+	}
+
+	/* Force enable SPI and disable LPC? Not a good idea. */
+#if 0
+	val |= (1 << 6);
+	val &= ~(1 << 5);
+	rpci_write_byte(dev, 0x8a, val);
+#endif
+
+	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"
+		  "your board name: flashrom -V as the subject to help us "
+		  "finish support for your\n"
+		  "chipset. Thanks.\n");
+
+	return ret;
+}
+
+static int enable_flash_ht1000(struct pci_dev *dev, const char *name)
+{
+	uint8_t val;
+
+	/* Set the 4MB enable bit. */
+	val = pci_read_byte(dev, 0x41);
+	val |= 0x0e;
+	rpci_write_byte(dev, 0x41, val);
+
+	val = pci_read_byte(dev, 0x43);
+	val |= (1 << 4);
+	rpci_write_byte(dev, 0x43, val);
+
+	return 0;
+}
+
+/*
+ * Usually on the x86 architectures (and on other PC-like platforms like some
+ * Alphas or Itanium) the system flash is mapped right below 4G. On the AMD
+ * Elan SC520 only a small piece of the system flash is mapped there, but the
+ * complete flash is mapped somewhere below 1G. The position can be determined
+ * by the BOOTCS PAR register.
+ */
+static int get_flashbase_sc520(struct pci_dev *dev, const char *name)
+{
+	int i, bootcs_found = 0;
+	uint32_t parx = 0;
+	void *mmcr;
+
+	/* 1. Map MMCR */
+	mmcr = physmap("Elan SC520 MMCR", 0xfffef000, getpagesize());
+
+	/* 2. Scan PAR0 (0x88) - PAR15 (0xc4) for
+	 *    BOOTCS region (PARx[31:29] = 100b)e
+	 */
+	for (i = 0x88; i <= 0xc4; i += 4) {
+		parx = mmio_readl(mmcr + i);
+		if ((parx >> 29) == 4) {
+			bootcs_found = 1;
+			break; /* BOOTCS found */
+		}
+	}
+
+	/* 3. PARx[25] = 1b --> flashbase[29:16] = PARx[13:0]
+	 *    PARx[25] = 0b --> flashbase[29:12] = PARx[17:0]
+	 */
+	if (bootcs_found) {
+		if (parx & (1 << 25)) {
+			parx &= (1 << 14) - 1; /* Mask [13:0] */
+			flashbase = parx << 16;
+		} else {
+			parx &= (1 << 18) - 1; /* Mask [17:0] */
+			flashbase = parx << 12;
+		}
+	} else {
+		msg_pinfo("AMD Elan SC520 detected, but no BOOTCS. Assuming flash at 4G\n");
+	}
+
+	/* 4. Clean up */
+	physunmap(mmcr, getpagesize());
+	return 0;
+}
+#endif
+#endif
+
+/* Please keep this list numerically sorted by vendor/device ID. */
+const struct penable chipset_enables[] = {
+#if defined(__i386__) || defined(__x86_64__)
+#ifdef ts
+	{0x1002, 0x4377, OK, "ATI", "SB400",		enable_flash_sb400},
+	{0x1002, 0x438d, OK, "AMD", "SB600",		enable_flash_sb600},
+	{0x1002, 0x439d, OK, "AMD", "SB700/SB710/SB750/SB850", enable_flash_sb600},
+	{0x100b, 0x0510, NT, "AMD", "SC1100",		enable_flash_sc1100},
+	{0x1022, 0x2080, OK, "AMD", "CS5536",		enable_flash_cs5536},
+	{0x1022, 0x2090, OK, "AMD", "CS5536",		enable_flash_cs5536},
+	{0x1022, 0x3000, OK, "AMD", "Elan SC520",	get_flashbase_sc520},
+	{0x1022, 0x7440, OK, "AMD", "AMD-768",		enable_flash_amd8111},
+	{0x1022, 0x7468, OK, "AMD", "AMD8111",		enable_flash_amd8111},
+	{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, 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, 0x0648, NT, "SiS", "648",		enable_flash_sis540},
+	{0x1039, 0x0650, NT, "SiS", "650",		enable_flash_sis540},
+	{0x1039, 0x0651, NT, "SiS", "651",		enable_flash_sis540},
+	{0x1039, 0x0655, NT, "SiS", "655",		enable_flash_sis540},
+	{0x1039, 0x0661, OK, "SiS", "661",		enable_flash_sis540},
+	{0x1039, 0x0730, NT, "SiS", "730",		enable_flash_sis540},
+	{0x1039, 0x0733, NT, "SiS", "733",		enable_flash_sis540},
+	{0x1039, 0x0735, OK, "SiS", "735",		enable_flash_sis540},
+	{0x1039, 0x0740, NT, "SiS", "740",		enable_flash_sis540},
+	{0x1039, 0x0741, OK, "SiS", "741",		enable_flash_sis540},
+	{0x1039, 0x0745, OK, "SiS", "745",		enable_flash_sis540},
+	{0x1039, 0x0746, NT, "SiS", "746",		enable_flash_sis540},
+	{0x1039, 0x0748, NT, "SiS", "748",		enable_flash_sis540},
+	{0x1039, 0x0755, NT, "SiS", "755",		enable_flash_sis540},
+	{0x1039, 0x5511, NT, "SiS", "5511",		enable_flash_sis5511},
+	{0x1039, 0x5571, NT, "SiS", "5571",		enable_flash_sis530},
+	{0x1039, 0x5591, NT, "SiS", "5591/5592",	enable_flash_sis530},
+	{0x1039, 0x5596, NT, "SiS", "5596",		enable_flash_sis5511},
+	{0x1039, 0x5597, NT, "SiS", "5597/5598/5581/5120", enable_flash_sis530},
+	{0x1039, 0x5600, NT, "SiS", "600",		enable_flash_sis530},
+	{0x1078, 0x0100, OK, "AMD", "CS5530(A)",	enable_flash_cs5530},
+	{0x10b9, 0x1533, OK, "ALi", "M1533",		enable_flash_ali_m1533},
+	{0x10de, 0x0030, OK, "NVIDIA", "nForce4/MCP4",	enable_flash_nvidia_nforce2},
+	{0x10de, 0x0050, OK, "NVIDIA", "CK804",		enable_flash_ck804}, /* LPC */
+	{0x10de, 0x0051, OK, "NVIDIA", "CK804",		enable_flash_ck804}, /* Pro */
+	{0x10de, 0x0060, OK, "NVIDIA", "NForce2",	enable_flash_nvidia_nforce2},
+	{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, 0x0261, NT, "NVIDIA", "MCP51",		enable_flash_ck804},
+	{0x10de, 0x0262, NT, "NVIDIA", "MCP51",		enable_flash_ck804},
+	{0x10de, 0x0263, NT, "NVIDIA", "MCP51",		enable_flash_ck804},
+	{0x10de, 0x0360, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* M57SLI*/
+	/* 10de:0361 is present in Tyan S2915 OEM systems, but not connected to
+	 * the flash chip. Instead, 10de:0364 is connected to the flash chip.
+	 * Until we have PCI device class matching or some fallback mechanism,
+	 * this is needed to get flashrom working on Tyan S2915 and maybe other
+	 * dual-MCP55 boards.
+	 */
+#if 0
+	{0x10de, 0x0361, NT, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
+#endif
+	{0x10de, 0x0362, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
+	{0x10de, 0x0363, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
+	{0x10de, 0x0364, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
+	{0x10de, 0x0365, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
+	{0x10de, 0x0366, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
+	{0x10de, 0x0367, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* Pro */
+	{0x10de, 0x03e0, NT, "NVIDIA", "MCP61",		enable_flash_mcp6x_7x},
+	{0x10de, 0x03e1, NT, "NVIDIA", "MCP61",		enable_flash_mcp6x_7x},
+	{0x10de, 0x03e2, NT, "NVIDIA", "MCP61",		enable_flash_mcp6x_7x},
+	{0x10de, 0x03e3, NT, "NVIDIA", "MCP61",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0440, NT, "NVIDIA", "MCP65",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0441, NT, "NVIDIA", "MCP65",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0442, NT, "NVIDIA", "MCP65",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0443, NT, "NVIDIA", "MCP65",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0548, OK, "NVIDIA", "MCP67",		enable_flash_mcp6x_7x},
+	{0x10de, 0x075c, NT, "NVIDIA", "MCP78S",	enable_flash_mcp6x_7x},
+	{0x10de, 0x075d, NT, "NVIDIA", "MCP78S",	enable_flash_mcp6x_7x},
+	{0x10de, 0x07d7, NT, "NVIDIA", "MCP73",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0aac, NT, "NVIDIA", "MCP79",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0aad, NT, "NVIDIA", "MCP79",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0aae, NT, "NVIDIA", "MCP79",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0aaf, NT, "NVIDIA", "MCP79",		enable_flash_mcp6x_7x},
+	/* VIA northbridges */
+	{0x1106, 0x0585, NT, "VIA", "VT82C585VPX",	via_no_byte_merge},
+	{0x1106, 0x0595, NT, "VIA", "VT82C595",		via_no_byte_merge},
+	{0x1106, 0x0597, NT, "VIA", "VT82C597",		via_no_byte_merge},
+	{0x1106, 0x0601, NT, "VIA", "VT8601/VT8601A",	via_no_byte_merge},
+	{0x1106, 0x0691, NT, "VIA", "VT82C69x",		via_no_byte_merge}, /* 691, 693a, 694t, 694x checked */
+	{0x1106, 0x8601, NT, "VIA", "VT8601T",		via_no_byte_merge},
+	/* VIA southbridges */
+	{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, 0x3147, OK, "VIA", "VT8233A",		enable_flash_vt823x},
+	{0x1106, 0x3177, OK, "VIA", "VT8235",		enable_flash_vt823x},
+	{0x1106, 0x3227, OK, "VIA", "VT8237",		enable_flash_vt823x},
+	{0x1106, 0x3337, OK, "VIA", "VT8237A",		enable_flash_vt823x},
+	{0x1106, 0x3372, OK, "VIA", "VT8237S",		enable_flash_vt8237s_spi},
+	{0x1106, 0x8231, NT, "VIA", "VT8231",		enable_flash_vt823x},
+	{0x1106, 0x8324, OK, "VIA", "CX700",		enable_flash_vt823x},
+	{0x1106, 0x8353, OK, "VIA", "VX800/VX820",	enable_flash_vt8237s_spi},
+	{0x1106, 0x8409, OK, "VIA", "VX855/VX875",	enable_flash_vt823x},
+	{0x1166, 0x0200, OK, "Broadcom", "OSB4",	enable_flash_osb4},
+	{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, 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},
+	{0x8086, 0x244c, OK, "Intel", "ICH2-M",		enable_flash_ich_4e},
+	{0x8086, 0x2450, NT, "Intel", "C-ICH",		enable_flash_ich_4e},
+	{0x8086, 0x2480, OK, "Intel", "ICH3-S",		enable_flash_ich_4e},
+	{0x8086, 0x248c, OK, "Intel", "ICH3-M",		enable_flash_ich_4e},
+	{0x8086, 0x24c0, OK, "Intel", "ICH4/ICH4-L",	enable_flash_ich_4e},
+	{0x8086, 0x24cc, OK, "Intel", "ICH4-M",		enable_flash_ich_4e},
+	{0x8086, 0x24d0, OK, "Intel", "ICH5/ICH5R",	enable_flash_ich_4e},
+	{0x8086, 0x25a1, OK, "Intel", "6300ESB",	enable_flash_ich_4e},
+	{0x8086, 0x2640, OK, "Intel", "ICH6/ICH6R",	enable_flash_ich_dc},
+	{0x8086, 0x2641, OK, "Intel", "ICH6-M",		enable_flash_ich_dc},
+	{0x8086, 0x2642, NT, "Intel", "ICH6W/ICH6RW",	enable_flash_ich_dc},
+	{0x8086, 0x2670, OK, "Intel", "631xESB/632xESB/3100", enable_flash_ich_dc},
+	{0x8086, 0x27b0, OK, "Intel", "ICH7DH",		enable_flash_ich7},
+	{0x8086, 0x27b8, OK, "Intel", "ICH7/ICH7R",	enable_flash_ich7},
+	{0x8086, 0x27b9, OK, "Intel", "ICH7M",		enable_flash_ich7},
+	{0x8086, 0x27bc, OK, "Intel", "NM10",		enable_flash_ich7},
+	{0x8086, 0x27bd, OK, "Intel", "ICH7MDH",	enable_flash_ich7},
+	{0x8086, 0x2810, OK, "Intel", "ICH8/ICH8R",	enable_flash_ich8},
+	{0x8086, 0x2811, OK, "Intel", "ICH8M-E",	enable_flash_ich8},
+	{0x8086, 0x2812, OK, "Intel", "ICH8DH",		enable_flash_ich8},
+	{0x8086, 0x2814, OK, "Intel", "ICH8DO",		enable_flash_ich8},
+	{0x8086, 0x2815, OK, "Intel", "ICH8M",		enable_flash_ich8},
+	{0x8086, 0x2910, OK, "Intel", "ICH9 Engineering Sample", enable_flash_ich9},
+	{0x8086, 0x2912, OK, "Intel", "ICH9DH",		enable_flash_ich9},
+	{0x8086, 0x2914, OK, "Intel", "ICH9DO",		enable_flash_ich9},
+	{0x8086, 0x2916, OK, "Intel", "ICH9R",		enable_flash_ich9},
+	{0x8086, 0x2917, OK, "Intel", "ICH9M-E",	enable_flash_ich9},
+	{0x8086, 0x2918, OK, "Intel", "ICH9",		enable_flash_ich9},
+	{0x8086, 0x2919, OK, "Intel", "ICH9M",		enable_flash_ich9},
+	{0x8086, 0x3a10, NT, "Intel", "ICH10R Engineering Sample", enable_flash_ich10},
+	{0x8086, 0x3a14, OK, "Intel", "ICH10DO",	enable_flash_ich10},
+	{0x8086, 0x3a16, OK, "Intel", "ICH10R",		enable_flash_ich10},
+	{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, 0x5031, OK, "Intel", "EP80579",	enable_flash_ich7},
+	{0x8086, 0x7000, OK, "Intel", "PIIX3",		enable_flash_piix4},
+#endif
+	{0x8086, 0x7110, OK, "Intel", "PIIX4/4E/4M",	enable_flash_piix4},
+#ifdef ts
+	{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},
+#endif
+#endif
+	{},
+};
+
+int chipset_flash_enable(void)
+{
+	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++) {
+		dev = pci_dev_find(chipset_enables[i].vendor_id,
+				   chipset_enables[i].device_id);
+		if (!dev)
+			continue;
+		if (ret != -2) {
+			msg_pinfo("WARNING: unexpected second chipset match: "
+				    "\"%s %s\"\n"
+				  "ignoring, please report lspci and board URL "
+				    "to flashrom@flashrom.org\n"
+				  "with \'CHIPSET: your board name\' in the "
+				    "subject line.\n",
+				chipset_enables[i].vendor_name,
+					chipset_enables[i].device_name);
+			continue;
+		}
+		msg_pinfo("Found chipset \"%s %s\", enabling flash write... ",
+		       chipset_enables[i].vendor_name,
+		       chipset_enables[i].device_name);
+		msg_pdbg("chipset PCI ID is %04x:%04x, ",
+			 chipset_enables[i].vendor_id,
+			 chipset_enables[i].device_id);
+
+		ret = chipset_enables[i].doit(dev,
+					      chipset_enables[i].device_name);
+		if (ret == NOT_DONE_YET) {
+			ret = -2;
+			msg_pinfo("OK - searching further chips.\n");
+		} else if (ret < 0)
+			msg_pinfo("FAILED!\n");
+		else if(ret == 0)
+			msg_pinfo("OK.\n");
+		else if(ret == ERROR_NONFATAL)
+			msg_pinfo("PROBLEMS, continuing anyway\n");
+	}
+
+
+
+	return ret;
+}
diff --git a/src/mainboard/msi/ms6119/f/cli_classic.c b/src/mainboard/msi/ms6119/f/cli_classic.c
new file mode 100644
index 0000000..2866e5f
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/cli_classic.c
@@ -0,0 +1,138 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2004 Tyan Corp <yhlu@tyan.com>
+ * Copyright (C) 2005-2008 coresystems GmbH
+ * Copyright (C) 2008,2009,2010 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+#define HAVE_UTSNAME	0
+
+#include <stdio.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include "flash.h"
+#include "flashchips.h"
+#include "programmer.h"
+
+
+
+int cli_classic()
+{
+	unsigned long size;
+	/* Probe for up to three flash chips. */
+	struct flashchip flashes[3];
+	struct flashchip *fill_flash;
+	int startchip = 0;
+	int chipcount = 0;
+	int force = 0;
+	int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0;
+	int dont_verify_it = 0;
+
+	int i;
+
+	char *filename = NULL;
+
+	char *pparam = NULL;
+
+	print_version();
+	print_banner();
+
+	if (selfcheck())
+		return -1;
+
+	if (programmer_init(pparam)) {
+		fprintf(stderr, "Error: Programmer initialization failed.\n");
+		programmer_shutdown();
+		return -1;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(flashes); i++) {
+		startchip = probe_flash(startchip, &flashes[i], 0);
+		if (startchip == -1)
+			break;
+		chipcount++;
+		startchip++;
+	}
+
+	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();
+		return -1;
+	} 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");
+		}
+		if (force && read_it && chip_to_probe) {
+			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();
+				return -1;
+			}
+			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();
+		return -1;
+	}
+
+	fill_flash = &flashes[0];
+
+	check_chip_supported(fill_flash);
+
+	size = fill_flash->total_size * 1024;
+	if (check_max_decode((buses_supported & fill_flash->bustype), size) &&
+	    (!force)) {
+		fprintf(stderr, "Chip is too big for this programmer "
+			"(-V gives details). Use --force to override.\n");
+		programmer_shutdown();
+		return 1;
+	}
+
+	if (!(read_it | write_it | verify_it | erase_it)) {
+		printf("No operations were specified.\n");
+		// FIXME: flash writes stay enabled!
+		programmer_shutdown();
+		return -1;
+	}
+
+	if (!filename && !erase_it) {
+		printf("Error: No filename specified.\n");
+		// FIXME: flash writes stay enabled!
+		programmer_shutdown();
+		return -1;
+	}
+
+	/* Always verify write operations unless -n is used. */
+	if (write_it && !dont_verify_it)
+		verify_it = 1;
+
+	/* FIXME: We should issue an unconditional chip reset here. This can be
+	 * done once we have a .reset function in struct flashchip.
+	 * Give the chip time to settle.
+	 */
+	programmer_delay(100000);
+	return doit(fill_flash, force, filename, read_it, write_it, erase_it, verify_it);
+}
diff --git a/src/mainboard/msi/ms6119/f/f.h b/src/mainboard/msi/ms6119/f/f.h
new file mode 100644
index 0000000..1e9fc0d
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/f.h
@@ -0,0 +1,2 @@
+int main_fl(void);
+
diff --git a/src/mainboard/msi/ms6119/f/flash.h b/src/mainboard/msi/ms6119/f/flash.h
new file mode 100644
index 0000000..44488ea
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/flash.h
@@ -0,0 +1,307 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2000 Ronald G. Minnich <rminnich@gmail.com>
+ * Copyright (C) 2005-2009 coresystems GmbH
+ * Copyright (C) 2006-2009 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#ifndef __FLASH_H__
+#define __FLASH_H__ 1
+
+#include <stdint.h>
+#include <stddef.h>
+#include "hwaccess.h"
+#ifdef _WIN32
+#include <windows.h>
+#undef min
+#undef max
+#endif
+
+
+#define __LIBPAYLOAD__
+#include <stdarg.h>
+#include <console/console.h>
+
+#ifdef __LIBPAYLOAD__
+#undef __linux__
+#define fprintf(stream, ...) printf(__VA_ARGS__)
+#define vfprintf(output_type, fmt, ap) vprintf(fmt, ap)
+#define print(type, ...) printk(type, __VA_ARGS__)
+#define printf(...) printk(0, __VA_ARGS__)
+#endif
+
+
+#define ERROR_PTR ((void*)-1)
+
+/* Error codes */
+#define TIMEOUT_ERROR	-101
+
+typedef unsigned long chipaddr;
+
+
+
+
+
+int register_shutdown(int (*function) (void *data), void *data);
+void *programmer_map_flash_region(const char *descr, unsigned long phys_addr,
+				  size_t len);
+void programmer_unmap_flash_region(void *virt_addr, size_t len);
+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 programmer_delay(int usecs);
+
+#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,
+};
+
+/*
+ * How many different contiguous runs of erase blocks with one size each do
+ * we have for a given erase function?
+ */
+#define NUM_ERASEREGIONS 5
+
+/*
+ * How many different erase functions do we have per chip?
+ * Atmel AT25FS010 has 6 different functions.
+ */
+#define NUM_ERASEFUNCTIONS 6
+
+#define FEATURE_REGISTERMAP	(1 << 0)
+#define FEATURE_BYTEWRITES	(1 << 1)
+#define FEATURE_LONG_RESET	(0 << 4)
+#define FEATURE_SHORT_RESET	(1 << 4)
+#define FEATURE_EITHER_RESET	FEATURE_LONG_RESET
+#define FEATURE_RESET_MASK	(FEATURE_LONG_RESET | FEATURE_SHORT_RESET)
+#define FEATURE_ADDR_FULL	(0 << 2)
+#define FEATURE_ADDR_MASK	(3 << 2)
+#define FEATURE_ADDR_2AA	(1 << 2)
+#define FEATURE_ADDR_AAA	(2 << 2)
+#define FEATURE_ADDR_SHIFTED	(1 << 5)
+#define FEATURE_WRSR_EWSR	(1 << 6)
+#define FEATURE_WRSR_WREN	(1 << 7)
+#define FEATURE_WRSR_EITHER	(FEATURE_WRSR_EWSR | FEATURE_WRSR_WREN)
+
+struct flashchip {
+	const char *vendor;
+	const char *name;
+
+	enum chipbustype bustype;
+
+	/*
+	 * With 32bit manufacture_id and model_id we can cover IDs up to
+	 * (including) the 4th bank of JEDEC JEP106W Standard Manufacturer's
+	 * Identification code.
+	 */
+	uint32_t manufacture_id;
+	uint32_t model_id;
+
+	/* Total chip size in kilobytes */
+	int total_size;
+	/* Chip page size in bytes */
+	int page_size;
+	int feature_bits;
+
+	/*
+	 * Indicate if flashrom has been tested with this flash chip and if
+	 * everything worked correctly.
+	 */
+	uint32_t tested;
+
+	int (*probe) (struct flashchip *flash);
+
+	/* Delay after "enter/exit ID mode" commands in microseconds. */
+	int probe_timing;
+
+	/*
+	 * Erase blocks and associated erase function. Any chip erase function
+	 * is stored as chip-sized virtual block together with said function.
+	 * The first one that fits will be chosen. There is currently no way to
+	 * influence that behaviour. For testing just comment out the other
+	 * elements or set the function pointer to NULL.
+	 */
+	struct block_eraser {
+		struct eraseblock{
+			unsigned int size; /* Eraseblock size in bytes */
+			unsigned int count; /* Number of contiguous blocks with that size */
+		} eraseblocks[NUM_ERASEREGIONS];
+		/* a block_erase function should try to erase one block of size
+		 * 'blocklen' at address 'blockaddr' and return 0 on success. */
+		int (*block_erase) (struct flashchip *flash, unsigned int blockaddr, unsigned int blocklen);
+	} block_erasers[NUM_ERASEFUNCTIONS];
+
+	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);
+	struct {
+		uint16_t min;
+		uint16_t max;
+	} voltage;
+
+	/* Some flash devices have an additional register space. */
+	chipaddr virtual_memory;
+	chipaddr virtual_registers;
+};
+
+#define TEST_UNTESTED	0
+
+#define TEST_OK_PROBE	(1 << 0)
+#define TEST_OK_READ	(1 << 1)
+#define TEST_OK_ERASE	(1 << 2)
+#define TEST_OK_WRITE	(1 << 3)
+#define TEST_OK_PR	(TEST_OK_PROBE | TEST_OK_READ)
+#define TEST_OK_PRE	(TEST_OK_PROBE | TEST_OK_READ | TEST_OK_ERASE)
+#define TEST_OK_PRW	(TEST_OK_PROBE | TEST_OK_READ | TEST_OK_WRITE)
+#define TEST_OK_PREW	(TEST_OK_PROBE | TEST_OK_READ | TEST_OK_ERASE | TEST_OK_WRITE)
+#define TEST_OK_MASK	0x0f
+
+#define TEST_BAD_PROBE	(1 << 4)
+#define TEST_BAD_READ	(1 << 5)
+#define TEST_BAD_ERASE	(1 << 6)
+#define TEST_BAD_WRITE	(1 << 7)
+#define TEST_BAD_PREW	(TEST_BAD_PROBE | TEST_BAD_READ | TEST_BAD_ERASE | TEST_BAD_WRITE)
+#define TEST_BAD_MASK	0xf0
+
+/* Timing used in probe routines. ZERO is -2 to differentiate between an unset
+ * field and zero delay.
+ * 
+ * SPI devices will always have zero delay and ignore this field.
+ */
+#define TIMING_FIXME	-1
+/* this is intentionally same value as fixme */
+#define TIMING_IGNORED	-1
+#define TIMING_ZERO	-2
+
+extern const struct flashchip flashchips[];
+
+/* print.c */
+char *flashbuses_to_text(enum chipbustype bustype);
+void print_supported(void);
+void print_supported_wiki(void);
+
+/* flashrom.c */
+enum write_granularity {
+	write_gran_1bit,
+	write_gran_1byte,
+	write_gran_256bytes,
+};
+extern enum chipbustype buses_supported;
+extern int verbose;
+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 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);
+int min(int a, int b);
+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);
+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 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 */
+#define ERROR_NONFATAL 0x100
+
+/* 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_gerr(...)	print(MSG_ERROR, __VA_ARGS__)	/* general errors */
+#define msg_perr(...)	print(MSG_ERROR, __VA_ARGS__)	/* programmer errors */
+#define msg_cerr(...)	print(MSG_ERROR, __VA_ARGS__)	/* chip errors */
+#define msg_ginfo(...)	print(MSG_INFO, __VA_ARGS__)	/* general info */
+#define msg_pinfo(...)	print(MSG_INFO, __VA_ARGS__)	/* programmer info */
+#define msg_cinfo(...)	print(MSG_INFO, __VA_ARGS__)	/* chip info */
+#define msg_gdbg(...)	print(MSG_DEBUG, __VA_ARGS__)	/* general debug */
+#define msg_pdbg(...)	print(MSG_DEBUG, __VA_ARGS__)	/* programmer debug */
+#define msg_cdbg(...)	print(MSG_DEBUG, __VA_ARGS__)	/* chip debug */
+
+#define msg_gspew(...);//	print(MSG_BARF, __VA_ARGS__)	/* general debug barf  */
+#define msg_pspew(...);//	print(MSG_BARF, __VA_ARGS__)	/* programmer debug barf  */
+#define msg_cspew(...);//	print(MSG_BARF, __VA_ARGS__)	/* chip debug barf  */
+
+/* cli_classic.c */
+int cli_classic(void);
+
+/* layout.c */
+int read_romlayout(char *name);
+int find_romentry(char *name);
+int handle_romentries(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents);
+
+/* spi.c */
+struct spi_command {
+	unsigned int writecnt;
+	unsigned int readcnt;
+	const unsigned char *writearr;
+	unsigned char *readarr;
+};
+int spi_send_command(unsigned int writecnt, unsigned int readcnt,
+		const unsigned char *writearr, unsigned char *readarr);
+int spi_send_multicommand(struct spi_command *cmds);
+uint32_t spi_get_valid_read_addr(void);
+
+
+
+
+
+
+
+
+
+void print_sysinfo(void);
+int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents);
+void nonfatal_help_message(void);
+void emergency_help_message(void);
+
+
+
+
+
+
+#endif				/* !__FLASH_H__ */
diff --git a/src/mainboard/msi/ms6119/f/flashchips.c b/src/mainboard/msi/ms6119/f/flashchips.c
new file mode 100644
index 0000000..751e755
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/flashchips.c
@@ -0,0 +1,8657 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2004 Tyan Corp
+ * Copyright (C) 2005-2008 coresystems GmbH <stepan@openbios.org>
+ * Copyright (C) 2006-2009 Carl-Daniel Hailfinger
+ * Copyright (C) 2009 Sean Nelson <audiohacked@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include "flash.h"
+#include "flashchips.h"
+#include "chipdrivers.h"
+
+/**
+ * List of supported flash chips.
+ *
+ * Please keep the list sorted by vendor name and chip name, so that
+ * the output of 'flashrom -L' is alphabetically sorted.
+ */
+const struct flashchip flashchips[] = {
+
+	/*
+	 * .vendor		= Vendor name
+	 * .name		= Chip name
+	 * .bustype		= Supported flash bus types (Parallel, LPC...)
+	 * .manufacture_id	= Manufacturer chip ID
+	 * .model_id		= Model chip ID
+	 * .total_size		= Total size in (binary) kbytes
+	 * .page_size		= Page or eraseblock(?) size in bytes
+	 * .tested		= Test status
+	 * .probe		= Probe function
+	 * .probe_timing	= Probe function delay
+	 * .block_erasers[]	= Array of erase layouts and erase functions
+	 * {
+	 *	.eraseblocks[]	= Array of { blocksize, blockcount }
+	 *	.block_erase	= Block erase function
+	 * }
+	 * .printlock		= Chip lock status function
+	 * .unlock		= Chip unlock function
+	 * .write		= Chip write function
+	 * .read		= Chip read function
+	 * .voltage		= Voltage range in millivolt
+	 */
+#ifdef ts
+	{
+		.vendor		= "AMD",
+		.name		= "Am29F010A/B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29F010B,	/* Same as Am29F010A */
+		.total_size	= 128,
+		.page_size	= 16 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {16 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29F002(N)BB",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29F002BB,
+		.total_size	= 256,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_SHORT_RESET | FEATURE_ADDR_2AA,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{32 * 1024, 1},
+					{64 * 1024, 3},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4750, 5250}, /* -55 speed is +-5%, all others +-10% */
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29F002(N)BT",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29F002BT,
+		.total_size	= 256,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_EITHER_RESET | FEATURE_ADDR_2AA,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 3},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29F016D",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29F016D,
+		.total_size	= 2 * 1024,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {2048 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29F040B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29F040B,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29F080B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29F080B,
+		.total_size	= 1024,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29LV001BB",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29LV001BB,
+		.total_size	= 128,
+		.page_size	= 64 * 1024, /* unused */
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+					{16 * 1024, 7},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29LV001BT",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29LV001BT,
+		.total_size	= 128,
+		.page_size	= 64 * 1024, /* unused */
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{16 * 1024, 7},
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29LV002BB",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29LV002BB,
+		.total_size	= 256,
+		.page_size	= 64 * 1024, /* unused */
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{32 * 1024, 1},
+					{64 * 1024, 3},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29LV002BT",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29LV002BT,
+		.total_size	= 256,
+		.page_size	= 64 * 1024, /* unused */
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 3},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29LV004BB",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29LV004BB,
+		.total_size	= 512,
+		.page_size	= 64 * 1024, /* unused */
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{32 * 1024, 1},
+					{64 * 1024, 7},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29LV004BT",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29LV004BT,
+		.total_size	= 512,
+		.page_size	= 64 * 1024, /* unused */
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 7},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29LV008BB",
+		.bustype	= CHIP_BUSTYPE_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,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{32 * 1024, 1},
+					{64 * 1024, 15},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29LV008BT",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29LV008BT,
+		.total_size	= 1024,
+		.page_size	= 64 * 1024, /* unused */
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 15},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29LV040B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29LV040B,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMD",
+		.name		= "Am29LV081B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMD_ID,
+		.model_id	= AMD_AM29LV080B,
+		.total_size	= 1024,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, /* datasheet specifies address as don't care */
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L05PT",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID,
+		.model_id	= AMIC_A25L05PT,
+		.total_size	= 64,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid4,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{32 * 1024, 1},
+					{16 * 1024, 1},
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l05p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L05PU",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID,
+		.model_id	= AMIC_A25L05PU,
+		.total_size	= 64,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid4,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+					{16 * 1024, 1},
+					{32 * 1024, 1},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l05p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L10PT",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID,
+		.model_id	= AMIC_A25L10PT,
+		.total_size	= 128,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid4,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 1},
+					{32 * 1024, 1},
+					{16 * 1024, 1},
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l05p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L10PU",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID,
+		.model_id	= AMIC_A25L10PU,
+		.total_size	= 128,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid4,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+					{16 * 1024, 1},
+					{32 * 1024, 1},
+					{64 * 1024, 1},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l05p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L20PT",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID,
+		.model_id	= AMIC_A25L20PT,
+		.total_size	= 256,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid4,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 3},
+					{32 * 1024, 1},
+					{16 * 1024, 1},
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l05p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L20PU",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID,
+		.model_id	= AMIC_A25L20PU,
+		.total_size	= 256,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid4,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+					{16 * 1024, 1},
+					{32 * 1024, 1},
+					{64 * 1024, 3},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l05p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	/* The A25L40P{T,U} chips are distinguished by their
+	 * erase block layouts, but without any distinction in RDID.
+	 * This inexplicable quirk was verified by Rudolf Marek
+	 * and discussed on the flashrom mailing list on 2010-07-12.
+	 */
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L40PT",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID,
+		.model_id	= AMIC_A25L40PT,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_spi_rdid4,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 7},
+					{32 * 1024, 1},
+					{16 * 1024, 1},
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l40p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L40PU",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID,
+		.model_id	= AMIC_A25L40PU,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_spi_rdid4,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+					{16 * 1024, 1},
+					{32 * 1024, 1},
+					{64 * 1024, 7},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l40p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L80P",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID,
+		.model_id	= AMIC_A25L80P,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_spi_rdid4,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+					{16 * 1024, 1},
+					{32 * 1024, 1},
+					{64 * 1024, 15},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l40p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L16PT",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID,
+		.model_id	= AMIC_A25L16PT,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid4,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 31},
+					{32 * 1024, 1},
+					{16 * 1024, 1},
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2048 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2048 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l40p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L16PU",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID,
+		.model_id	= AMIC_A25L16PU,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_spi_rdid4,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+					{16 * 1024, 1},
+					{32 * 1024, 1},
+					{64 * 1024, 31},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2048 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2048 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l40p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L512",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID_NOPREFIX,
+		.model_id	= AMIC_A25L512,
+		.total_size	= 64,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { { 4 * 1024, 16 } },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { { 64 * 1024, 1 } },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { { 64 * 1024, 1 } },
+				.block_erase = spi_block_erase_c7,
+			}			
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l40p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L010",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID_NOPREFIX,
+		.model_id	= AMIC_A25L010,
+		.total_size	= 128,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { { 4 * 1024, 32 } },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { { 64 * 1024, 2 } },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { { 128 * 1024, 1 } },
+				.block_erase = spi_block_erase_c7,
+			}			
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l40p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L020",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID_NOPREFIX,
+		.model_id	= AMIC_A25L020,
+		.total_size	= 256,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { { 4 * 1024, 64 } },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { { 64 * 1024, 4 } },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { { 256 * 1024, 1 } },
+				.block_erase = spi_block_erase_c7,
+			}			
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l40p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L040",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID_NOPREFIX,
+		.model_id	= AMIC_A25L040,
+		.total_size	= 512,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { { 4 * 1024, 128 } },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { { 64 * 1024, 8 } },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { { 512 * 1024, 1 } },
+				.block_erase = spi_block_erase_c7,
+			}			
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l40p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L080",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID_NOPREFIX,
+		.model_id	= AMIC_A25L080,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { { 4 * 1024, 256 } },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { { 64 * 1024, 16 } },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { { 1024 * 1024, 1 } },
+				.block_erase = spi_block_erase_c7,
+			}			
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l40p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L016",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID_NOPREFIX,
+		.model_id	= AMIC_A25L016,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { { 4 * 1024, 512 } },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { { 64 * 1024, 32 } },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { { 2048 * 1024, 1 } },
+				.block_erase = spi_block_erase_c7,
+			}			
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l40p,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25L032",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID_NOPREFIX,
+		.model_id	= AMIC_A25L032,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_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_52,
+			}, {
+				.eraseblocks = { { 64 * 1024, 64 } },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { { 4096 * 1024, 1 } },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { { 4096 * 1024, 1 } },
+				.block_erase = spi_block_erase_c7,
+			}			
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25l032,
+		.unlock		= NULL, /* Two status reg bytes (read with 0x35 and 0x05) */
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A25LQ032",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID_NOPREFIX,
+		.model_id	= AMIC_A25LQ032,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_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_52,
+			}, {
+				.eraseblocks = { { 64 * 1024, 64 } },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { { 4096 * 1024, 1 } },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { { 4096 * 1024, 1 } },
+				.block_erase = spi_block_erase_c7,
+			}			
+		},
+		.printlock	= spi_prettyprint_status_register_amic_a25lq032,
+		.unlock		= NULL, /* Two status reg bytes (read with 0x35 and 0x05) */
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A29002B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMIC_ID_NOPREFIX,
+		.model_id	= AMIC_A29002B,
+		.total_size	= 256,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{32 * 1024, 1},
+					{64 * 1024, 3},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A29002T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMIC_ID_NOPREFIX,
+		.model_id	= AMIC_A29002T,
+		.total_size	= 256,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{64 * 1024, 3},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A29040B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= AMIC_ID_NOPREFIX,
+		.model_id	= AMIC_A29040B,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "A49LF040A",
+		.bustype	= CHIP_BUSTYPE_LPC,
+		.manufacture_id	= AMIC_ID_NOPREFIX,
+		.model_id	= AMIC_A49LF040A,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* routine is wrapper to probe_jedec (pm49fl00x.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.unlock		= unlock_49fl00x,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT25DF021",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT25DF021,
+		.total_size	= 256,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 64} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 8} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 4} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_at25df,
+		.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 */
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT25DF041A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT25DF041A,
+		.total_size	= 512,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 16} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_at25df,
+		.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 */
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT25DF081",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT25DF081,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 256} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 32} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_at25df,
+		.unlock		= spi_disable_blockprotect_at25df,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {1600, 2000}, /* Datasheet says range is 1.65-1.95 V */
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT25DF081A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT25DF081A,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 256} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 32} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_at25df_sec,
+		.unlock		= spi_disable_blockprotect_at25df_sec,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT25DF161",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT25DF161,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 64} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_at25df_sec,
+		.unlock		= spi_disable_blockprotect_at25df_sec,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT25DF321",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT25DF321,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 1024} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 128} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 64} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_at25df,
+		.unlock		= spi_disable_blockprotect_at25df,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT25DF321A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT25DF321A,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.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 = { {32 * 1024, 128} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 64} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_at25df_sec,
+		.unlock		= spi_disable_blockprotect_at25df_sec,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT25DF641",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT25DF641,
+		.total_size	= 8192,
+		.page_size	= 256,
+		.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 = { {32 * 1024, 256} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 128} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_at25df_sec,
+		.unlock		= spi_disable_blockprotect_at25df_sec,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT25DQ161",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT25DQ161,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 64} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_at25df_sec,
+		.unlock		= spi_disable_blockprotect_at25df_sec,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT25F512B",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT25F512B,
+		.total_size	= 64,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 16} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 2} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {32 * 1024, 2} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_at25f,
+		.unlock		= spi_disable_blockprotect_at25f,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT25FS010",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT25FS010,
+		.total_size	= 128,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 32} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {4 * 1024, 32} },
+				.block_erase = spi_block_erase_d7,
+			}, {
+				.eraseblocks = { {32 * 1024, 4} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {32 * 1024, 4} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_at25fs010,
+		.unlock		= spi_disable_blockprotect_at25fs010,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT25FS040",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT25FS040,
+		.total_size	= 512,
+		.page_size	= 256,
+		.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_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_at25fs040,
+		.unlock		= spi_disable_blockprotect_at25fs040,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT26DF041",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT26DF041,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = spi_block_erase_20,
+			}
+		},
+		.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 */
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT26DF081A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT26DF081A,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 256} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 32} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_atmel_at26df081a,
+		.unlock		= spi_disable_blockprotect_at25df,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT26DF161",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT26DF161,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 64} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_at25df,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT26DF161A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT26DF161A,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 64} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.printlock	= spi_prettyprint_status_register_atmel_at26df081a,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	/*The AT26DF321 has the same ID as the AT25DF321. */
+	/*{
+		.vendor		= "Atmel",
+		.name		= "AT26DF321",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT26DF321,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.printlock	= spi_prettyprint_status_register_atmel_at26df081a,
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	  },*/
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT26F004",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT26F004,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.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 = { {64 * 1024, 8} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.write		= NULL /* Incompatible Page write */,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT29C512",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT29C512,
+		.total_size	= 64,
+		.page_size	= 128,
+		.feature_bits	= FEATURE_LONG_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= 10000, /* 10mS, Enter=Exec */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT29C010A",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT29C010A,
+		.total_size	= 128,
+		.page_size	= 128,
+		.feature_bits	= FEATURE_LONG_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= 10000, /* 10mS, Enter=Exec */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec,	/* FIXME */
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT29C020",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT29C020,
+		.total_size	= 256,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_LONG_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= 10000,			/* 10ms */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT29C040A",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT29C040A,
+		.total_size	= 512,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_LONG_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= 10000,			/* 10 ms */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT45CS1282",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT45CS1282,
+		.total_size	= 16896 /* No power of two sizes */,
+		.page_size	= 1056 /* No power of two sizes */,
+		.tested		= TEST_BAD_READ,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL /* Incompatible Page write */,
+		.read		= NULL /* Incompatible read */,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT45DB011D",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT45DB011D,
+		.total_size	= 128 /* Size can only be determined from status register */,
+		.page_size	= 256 /* Size can only be determined from status register */,
+		.tested		= TEST_BAD_READ,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT45DB021D",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT45DB021D,
+		.total_size	= 256 /* Size can only be determined from status register */,
+		.page_size	= 256 /* Size can only be determined from status register */,
+		.tested		= TEST_BAD_READ,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT45DB041D",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT45DB041D,
+		.total_size	= 512 /* Size can only be determined from status register */,
+		.page_size	= 256 /* Size can only be determined from status register */,
+		.tested		= TEST_BAD_READ,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+		.voltage	= {2500, 3600}, /* Datasheet says 2.5-3.6 V or 2.7-3.6 V */
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT45DB081D",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT45DB081D,
+		.total_size	= 1024 /* Size can only be determined from status register */,
+		.page_size	= 256 /* Size can only be determined from status register */,
+		.tested		= TEST_BAD_READ,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+		.voltage	= {2500, 3600}, /* Datasheet says 2.5-3.6 V or 2.7-3.6 V */
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT45DB161D",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT45DB161D,
+		.total_size	= 2048 /* Size can only be determined from status register */,
+		.page_size	= 512 /* Size can only be determined from status register */,
+		.tested		= TEST_BAD_READ,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+		.voltage	= {2500, 3600}, /* Datasheet says 2.5-3.6 V or 2.7-3.6 V */
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT45DB321C",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT45DB321C,
+		.total_size	= 4224 /* No power of two sizes */,
+		.page_size	= 528 /* No power of two sizes */,
+		.tested		= TEST_BAD_READ,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL /* Incompatible read */,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT45DB321D",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT45DB321D,
+		.total_size	= 4096 /* Size can only be determined from status register */,
+		.page_size	= 512 /* Size can only be determined from status register */,
+		.tested		= TEST_BAD_READ,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT45DB642D",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT45DB642D,
+		.total_size	= 8192 /* Size can only be determined from status register */,
+		.page_size	= 1024 /* Size can only be determined from status register */,
+		.tested		= TEST_BAD_READ,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT49BV512",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT49BV512,
+		.total_size	= 64,
+		.page_size	= 64,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT49F020",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT49F020,
+		.total_size	= 256,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT49F002(N)",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT49F002N,
+		.total_size	= 256,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{96 * 1024, 1},
+					{128 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "AT49F002(N)T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT49F002NT,
+		.total_size	= 256,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{128 * 1024, 1},
+					{96 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Bright",
+		.name		= "BM29F040",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= BRIGHT_ID,
+		.model_id	= BRIGHT_BM29F040,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "EMST",
+		.name		= "F49B002UA",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= EMST_ID,
+		.model_id	= EMST_F49B002UA,
+		.total_size	= 256,
+		.page_size	= 4096,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{128 * 1024, 1},
+					{96 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "EMST",
+		.name		= "F25L008A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EMST_ID,
+		.model_id	= EMST_F25L008A,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.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_1,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25B05",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B05,
+		.total_size	= 64,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+					{16 * 1024, 1},
+					{32 * 1024, 1},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25B05T",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B05,
+		.total_size	= 64,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{32 * 1024, 1},
+					{16 * 1024, 1},
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25B10",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B10,
+		.total_size	= 128,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+					{16 * 1024, 1},
+					{32 * 1024, 3},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25B10T",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B10,
+		.total_size	= 128,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{32 * 1024, 3},
+					{16 * 1024, 1},
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25B20",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B20,
+		.total_size	= 256,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+					{16 * 1024, 1},
+					{32 * 1024, 1},
+					{64 * 1024, 3}
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25B20T",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B20,
+		.total_size	= 256,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 3},
+					{32 * 1024, 1},
+					{16 * 1024, 1},
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25B40",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B40,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+					{16 * 1024, 1},
+					{32 * 1024, 1},
+					{64 * 1024, 7}
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25B40T",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B40,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 7},
+					{32 * 1024, 1},
+					{16 * 1024, 1},
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25B80",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B80,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+					{16 * 1024, 1},
+					{32 * 1024, 1},
+					{64 * 1024, 15}
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25B80T",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B80,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 15},
+					{32 * 1024, 1},
+					{16 * 1024, 1},
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25B16",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B16,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+					{16 * 1024, 1},
+					{32 * 1024, 1},
+					{64 * 1024, 31},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 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		= "EN25B16T",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B16,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 31},
+					{32 * 1024, 1},
+					{16 * 1024, 1},
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 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		= "EN25B32",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B32,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+					{16 * 1024, 1},
+					{32 * 1024, 1},
+					{64 * 1024, 63},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25B32T",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B32,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 63},
+					{32 * 1024, 1},
+					{16 * 1024, 1},
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25B64",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B64,
+		.total_size	= 8192,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+					{16 * 1024, 1},
+					{32 * 1024, 1},
+					{64 * 1024, 127},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25B64T",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25B64,
+		.total_size	= 8192,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 127},
+					{32 * 1024, 1},
+					{16 * 1024, 1},
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+				},
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25D16",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25D16,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.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 = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25F05",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25F05,
+		.total_size	= 64,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 16} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 2} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {32 * 1024, 2} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25F10",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25F10,
+		.total_size	= 128,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 32} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 4} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {32 * 1024, 4} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25F20",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25F20,
+		.total_size	= 256,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 64} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 4} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {64 * 1024, 4} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN25F40",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25F40,
+		.total_size	= 512,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PROBE,
+		.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		= "EN25F80",
+		.bustype	= CHIP_BUSTYPE_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,
+		.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		= "EN25F16",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25F16,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2 * 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		= "EN25F32",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= EON_EN25F32,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.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		= "EN29F010",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= EON_ID,
+		.model_id	= EON_EN29F010,
+		.total_size	= 128,
+		.page_size	= 128,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {16 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			},
+			{
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN29F002(A)(N)B",
+		.bustype	= CHIP_BUSTYPE_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,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{32 * 1024, 1},
+					{64 * 1024, 3},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "EN29F002(A)(N)T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= EON_ID,
+		.model_id	= EON_EN29F002T,
+		.total_size	= 256,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_ADDR_AAA | FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{64 * 1024, 3},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Fujitsu",
+		.name		= "MBM29F004BC",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= FUJITSU_ID,
+		.model_id	= FUJITSU_MBM29F004BC,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{32 * 1024, 1},
+					{64 * 1024, 7},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= NULL,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Fujitsu",
+		.name		= "MBM29F004TC",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= FUJITSU_ID,
+		.model_id	= FUJITSU_MBM29F004TC,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{64 * 1024, 7},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= NULL,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		/* FIXME: this has WORD/BYTE sequences; 2AA for word, 555 for byte */
+		.vendor		= "Fujitsu",
+		.name		= "MBM29F400BC",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= FUJITSU_ID,
+		.model_id	= FUJITSU_MBM29F400BC,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_SHIFTED | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_m29f400bt,
+		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (m29f400bt.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{32 * 1024, 1},
+					{64 * 1024, 7},
+				},
+				.block_erase = block_erase_m29f400bt,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = block_erase_chip_m29f400bt,
+			},
+		},
+		.write		= write_m29f400bt,
+		.read		= read_memmapped,
+		.voltage	= {4750, 5250}, /* 5.0V +-5% for -55 model, +-10% for rest */
+	},
+
+	{
+		.vendor		= "Fujitsu",
+		.name		= "MBM29F400TC",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= FUJITSU_ID,
+		.model_id	= FUJITSU_MBM29F400TC,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_SHIFTED | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_m29f400bt,
+		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (m29f400bt.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{64 * 1024, 7},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = block_erase_m29f400bt,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = block_erase_chip_m29f400bt,
+			},
+		},
+		.write		= write_m29f400bt,
+		.read		= read_memmapped,
+		.voltage	= {4750, 5250}, /* 5.0V +-5% for -55 model, +-10% for rest */
+	},
+
+	{
+		.vendor		= "Hyundai",
+		.name		= "HY29F002T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= HYUNDAI_ID,
+		.model_id	= HYUNDAI_HY29F002T,
+		.total_size	= 256,
+		.page_size	= 256 * 1024,
+		.feature_bits	= FEATURE_EITHER_RESET, /* Some revisions may need FEATURE_ADDR_2AA */
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO, /* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 3},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4750, 5250}, /* 5.0V +-5% for -45 model, +-10% for rest */
+	},
+
+	{
+		.vendor		= "Hyundai",
+		.name		= "HY29F002B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= HYUNDAI_ID,
+		.model_id	= HYUNDAI_HY29F002B,
+		.total_size	= 256,
+		.page_size	= 256 * 1024,
+		.feature_bits	= FEATURE_EITHER_RESET, /* Some revisions may need FEATURE_ADDR_2AA */
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO, /* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{32 * 1024, 1},
+					{64 * 1024, 3},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4750, 5250}, /* 5.0V +-5% for -45 model, +-10% for rest */
+	},
+
+	{
+		.vendor		= "Hyundai",
+		.name		= "HY29F040A",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= HYUNDAI_ID,
+		.model_id	= HYUNDAI_HY29F040A,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "Intel",
+		.name		= "28F001BN/BX-B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= INTEL_ID,
+		.model_id	= INTEL_28F001B,
+		.total_size	= 128,
+		.page_size	= 128 * 1024, /* 8k + 2x4k + 112k */
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+					{112 * 1024, 1},
+				},
+				.block_erase = erase_block_82802ab,
+			},
+		},
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Intel",
+		.name		= "28F001BN/BX-T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= INTEL_ID,
+		.model_id	= INTEL_28F001T,
+		.total_size	= 128,
+		.page_size	= 128 * 1024, /* 112k + 2x4k + 8k */
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{112 * 1024, 1},
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+				},
+				.block_erase = erase_block_82802ab,
+			},
+		},
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Intel",
+		.name		= "28F002BC/BL/BV/BX-T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= INTEL_ID,
+		.model_id	= INTEL_28F002T,
+		.total_size	= 256,
+		.page_size	= 256 * 1024,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_ZERO, /* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{128 * 1024, 1},
+					{96 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_block_82802ab,
+			},
+		},
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.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,
+		.manufacture_id	= INTEL_ID,
+		.model_id	= INTEL_28F004S3,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_block_82802ab,
+			},
+		},
+		.unlock		= unlock_28f004s5,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "Intel",
+		.name		= "28F004B5/BE/BV/BX-B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= INTEL_ID,
+		.model_id	= INTEL_28F004B,
+		.total_size	= 512,
+		.page_size	= 128 * 1024, /* maximal block size */
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{96 * 1024, 1},
+					{128 * 1024, 3},
+				},
+				.block_erase = erase_block_82802ab,
+			},
+		},
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "Intel",
+		.name		= "28F004B5/BE/BV/BX-T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= INTEL_ID,
+		.model_id	= INTEL_28F004T,
+		.total_size	= 512,
+		.page_size	= 128 * 1024, /* maximal block size */
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{128 * 1024, 3},
+					{96 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_block_82802ab,
+			},
+		},
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "Intel",
+		.name		= "28F400BV/BX/CE/CV-B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= INTEL_ID,
+		.model_id	= INTEL_28F400B,
+		.total_size	= 512,
+		.page_size	= 128 * 1024, /* maximal block size */
+		.feature_bits	= FEATURE_ADDR_SHIFTED,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{96 * 1024, 1},
+					{128 * 1024, 3},
+				},
+				.block_erase = erase_block_82802ab,
+			},
+		},
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "Intel",
+		.name		= "28F400BV/BX/CE/CV-T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= INTEL_ID,
+		.model_id	= INTEL_28F400T,
+		.total_size	= 512,
+		.page_size	= 128 * 1024, /* maximal block size */
+		.feature_bits	= FEATURE_ADDR_SHIFTED,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{128 * 1024, 3},
+					{96 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_block_82802ab,
+			},
+		},
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "Intel",
+		.name		= "82802AB",
+		.bustype	= CHIP_BUSTYPE_FWH,
+		.manufacture_id	= INTEL_ID,
+		.model_id	= INTEL_82802AB,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_IGNORED, /* routine does not use probe_timing (82802ab.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_block_82802ab,
+			},
+		},
+		.unlock		= unlock_82802ab,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "Intel",
+		.name		= "82802AC",
+		.bustype	= CHIP_BUSTYPE_FWH,
+		.manufacture_id	= INTEL_ID,
+		.model_id	= INTEL_82802AC,
+		.total_size	= 1024,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_IGNORED, /* routine does not use probe_timing (82802ab.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = erase_block_82802ab,
+			},
+		},
+		.unlock		= unlock_82802ab,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX25L512",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX25L512,
+		.total_size	= 64,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 16} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			},
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX25L1005",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX25L1005,
+		.total_size	= 128,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 32} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 2} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {128 * 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		= "Macronix",
+		.name		= "MX25L2005",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX25L2005,
+		.total_size	= 256,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 64} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 4} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 4} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			},
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX25L4005",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX25L4005,
+		.total_size	= 512,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PREW,
+		.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_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			},
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX25L8005",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX25L8005,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PREW,
+		.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_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			},
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX25L1605",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX25L1605,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = spi_block_erase_20,	/* This erase function has 64k blocksize for eLiteFlash */
+			}, {
+				.eraseblocks = { {64 * 1024, 32} },	/* Not supported in MX25L1605 (eLiteFlash) and MX25L1605D */
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			},
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX25L1635D",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX25L1635D,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2 * 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		= "Macronix",
+		.name		= "MX25L1635E",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX25L1635E,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX25L3205",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX25L3205,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 1024} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {4 * 1024, 1024} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			},
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX25L3235D",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX25L3235D,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.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		= "Macronix",
+		.name		= "MX25L6405",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX25L6405,
+		.total_size	= 8192,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PROBE,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 128} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 128} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX25L12805",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX25L12805,
+		.total_size	= 16384,
+		.page_size	= 256,
+		.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,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX29F001B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX29F001B,
+		.total_size	= 128,
+		.page_size	= 32 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{8 * 1024, 1},
+					{4 * 1024, 2},
+					{8 * 1024, 2},
+					{32 * 1024, 1},
+					{64 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX29F001T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX29F001T,
+		.total_size	= 128,
+		.page_size	= 32 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { 
+					{64 * 1024, 1},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{4 * 1024, 2},
+					{8 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX29F002B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX29F002B,
+		.total_size	= 256,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{32 * 1024, 1},
+					{64 * 1024, 3},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX29F002T",
+		.bustype	= CHIP_BUSTYPE_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,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 3},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX29F040",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX29F040,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "MX29LV040",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= MACRONIX_MX29LV040,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8}, },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "MoselVitelic",
+		.name		= "V29C51000B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= MVC_V29C51000B,
+		.total_size	= 64,
+		.page_size	= 512,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {512, 128} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "MoselVitelic",
+		.name		= "V29C51000T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= MVC_V29C51000T,
+		.total_size	= 64,
+		.page_size	= 512,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {512, 128} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "MoselVitelic",
+		.name		= "V29C51400B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= MVC_V29C51400B,
+		.total_size	= 512,
+		.page_size	= 1024,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {1024, 512} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "MoselVitelic",
+		.name		= "V29C51400T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= MVC_V29C51400T,
+		.total_size	= 512,
+		.page_size	= 1024,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {1024, 512} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "MoselVitelic",
+		.name		= "V29LC51000",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= MVC_V29LC51000,
+		.total_size	= 64,
+		.page_size	= 512,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {512, 128} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "MoselVitelic",
+		.name		= "V29LC51001",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= MVC_V29LC51001,
+		.total_size	= 128,
+		.page_size	= 512,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {512, 256} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "MoselVitelic",
+		.name		= "V29LC51002",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= MVC_V29LC51002,
+		.total_size	= 256,
+		.page_size	= 512,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {512, 512} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Numonyx",
+		.name		= "M25PE10",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25PE10,
+		.total_size	= 128,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 32} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 2} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Numonyx",
+		.name		= "M25PE20",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25PE20,
+		.total_size	= 256,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 64} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 4} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Numonyx",
+		.name		= "M25PE40",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25PE40,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Numonyx",
+		.name		= "M25PE80",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25PE80,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 256} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Numonyx",
+		.name		= "M25PE16",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25PE16,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.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 = { {2 * 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		= "PMC",
+		.name		= "Pm25LV010",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= PMC_ID,
+		.model_id	= PMC_PM25LV010,
+		.total_size	= 128,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 32} },
+				.block_erase = spi_block_erase_d7,
+			}, {
+				.eraseblocks = { {32 * 1024, 4} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "PMC",
+		.name		= "Pm25LV016B",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= PMC_ID,
+		.model_id	= PMC_PM25LV016B,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = spi_block_erase_d7,
+			}, {
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "PMC",
+		.name		= "Pm25LV020",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= PMC_ID,
+		.model_id	= PMC_PM25LV020,
+		.total_size	= 256,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 64} },
+				.block_erase = spi_block_erase_d7,
+			}, {
+				.eraseblocks = { {64 * 1024, 4} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "PMC",
+		.name		= "Pm25LV040",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= PMC_ID,
+		.model_id	= PMC_PM25LV040,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = spi_block_erase_d7,
+			}, {
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "PMC",
+		.name		= "Pm25LV080B",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= PMC_ID,
+		.model_id	= PMC_PM25LV080B,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 256} },
+				.block_erase = spi_block_erase_d7,
+			}, {
+				.eraseblocks = { {4 * 1024, 256} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "PMC",
+		.name		= "Pm25LV512",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= PMC_ID,
+		.model_id	= PMC_PM25LV512,
+		.total_size	= 64,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 16} },
+				.block_erase = spi_block_erase_d7,
+			}, {
+				.eraseblocks = { {32 * 1024, 2} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "PMC",
+		.name		= "Pm29F002T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= PMC_ID_NOPREFIX,
+		.model_id	= PMC_PM29F002T,
+		.total_size	= 256,
+		.page_size	= 8 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_FIXME, 
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{128 * 1024, 1},
+					{96 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "PMC",
+		.name		= "Pm29F002B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= PMC_ID_NOPREFIX,
+		.model_id	= PMC_PM29F002B,
+		.total_size	= 256,
+		.page_size	= 8 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_FIXME, 
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{96 * 1024, 1},
+					{128 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "PMC",
+		.name		= "Pm39LV010",
+		.bustype	= CHIP_BUSTYPE_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,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 32} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 2} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "PMC",
+		.name		= "Pm39LV020",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= PMC_ID_NOPREFIX,
+		.model_id	= PMC_PM39LV020,
+		.total_size	= 256,
+		.page_size	= 4096,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers =
+		{
+			{
+				.eraseblocks = { {4 * 1024, 64} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 4} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "PMC",
+		.name		= "Pm39LV040",
+		.bustype	= CHIP_BUSTYPE_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,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers =
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+	
+	{
+		.vendor		= "PMC",
+		.name		= "Pm49FL002",
+		.bustype	= CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.manufacture_id	= PMC_ID_NOPREFIX,
+		.model_id	= PMC_PM49FL002,
+		.total_size	= 256,
+		.page_size	= 16 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* routine is wrapper to probe_jedec (pm49fl00x.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 64} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {16 * 1024, 16} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.unlock		= unlock_49fl00x,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "PMC",
+		.name		= "Pm49FL004",
+		.bustype	= CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.manufacture_id	= PMC_ID_NOPREFIX,
+		.model_id	= PMC_PM49FL004,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* routine is wrapper to probe_jedec (pm49fl00x.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.unlock		= unlock_49fl00x,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "Sanyo",
+		.name		= "LF25FW203A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SANYO_ID,
+		.model_id	= SANYO_LE25FW203A,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			},	{
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "Sharp",
+		.name		= "LHF00L04",
+		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.manufacture_id	= SHARP_ID,
+		.model_id	= SHARP_LHF00L04,
+		.total_size	= 1024,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_EITHER_RESET | FEATURE_REGISTERMAP,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 15},
+					{8 * 1024, 8}
+				 },
+				.block_erase = erase_block_82802ab,
+			}, {
+				.eraseblocks = {
+					{1024 * 1024, 1}
+				},
+				.block_erase = NULL, /* 30 D0, only in A/A mux mode */
+			},
+		},
+		.unlock		= unlock_82802ab,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "Spansion",
+		.name		= "S25FL004A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SPANSION_ID,
+		.model_id	= SPANSION_S25FL004A,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "Spansion",
+		.name		= "S25FL008A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SPANSION_ID,
+		.model_id	= SPANSION_S25FL008A,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Spansion",
+		.name		= "S25FL016A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SPANSION_ID,
+		.model_id	= SPANSION_S25FL016A,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 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		= "Spansion",
+		.name		= "S25FL032A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SPANSION_ID,
+		.model_id	= SPANSION_S25FL032A,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 64} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "Spansion",
+		.name		= "S25FL064A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SPANSION_ID,
+		.model_id	= SPANSION_S25FL064A,
+		.total_size	= 8192,
+		.page_size	= 256,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 128} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST25VF010.REMS",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST25VF010_REMS,
+		.total_size	= 128,
+		.page_size	= 256,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rems,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 32} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 4} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			},
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_1,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST25VF016B",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST25VF016B,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 64} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			},
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_1,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST25VF032B",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST25VF032B,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 1024} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 128} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 64} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			},
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_aai_write,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST25VF064C",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST25VF064C,
+		.total_size	= 8192,
+		.page_size	= 256,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 2048} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 256} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 128} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			},
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST25VF040.REMS",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST25VF040_REMS,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_spi_rems,
+		.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",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST25VF040B,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.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 = { {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_1,
+		.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,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST25VF040B_REMS,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_spi_rems,
+		.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 = { {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_1,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST25VF080B",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST25VF080B,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 256} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 32} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			},
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_1,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST28SF040A",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST28SF040,
+		.total_size	= 512,
+		.page_size	= 256,
+		.feature_bits	= 0,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (sst28sf040.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {128, 4096} },
+				.block_erase = erase_sector_28sf040,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_28sf040,
+			}
+		},
+		.unlock		= unprotect_28sf040,
+		.write		= write_28sf040,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST29EE010",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST29EE010,
+		.total_size	= 128,
+		.page_size	= 128,
+		.feature_bits	= FEATURE_LONG_RESET,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_jedec,
+		.probe_timing	= 10, 
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST29LE010",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST29LE010,
+		.total_size	= 128,
+		.page_size	= 128,
+		.feature_bits	= FEATURE_LONG_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= 10, 
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST29EE020A",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST29EE020A,
+		.total_size	= 256,
+		.page_size	= 128,
+		.feature_bits	= FEATURE_LONG_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= 10,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST29LE020",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST29LE020,
+		.total_size	= 256,
+		.page_size	= 128,
+		.feature_bits	= FEATURE_LONG_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= 10, 
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST39SF512",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST39SF512,
+		.total_size	= 64,
+		.page_size	= 4096,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,			/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 16} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST39SF010A",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST39SF010,
+		.total_size	= 128,
+		.page_size	= 4096,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,			/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 32} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST39SF020A",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST39SF020,
+		.total_size	= 256,
+		.page_size	= 4096,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,			/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 64} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST39SF040",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST39SF040,
+		.total_size	= 512,
+		.page_size	= 4096,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,			/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST39VF512",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST39VF512,
+		.total_size	= 64,
+		.page_size	= 4096,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,			/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 16} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST39VF010",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST39VF010,
+		.total_size	= 128,
+		.page_size	= 4096,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,			/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 32} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST39VF020",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST39VF020,
+		.total_size	= 256,
+		.page_size	= 4096,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,			/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 64} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST39VF040",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST39VF040,
+		.total_size	= 512,
+		.page_size	= 4096,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,			/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST39VF080",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST39VF080,
+		.total_size	= 1024,
+		.page_size	= 4096,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,			/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 256} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST49LF002A/B",
+		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST49LF002A,
+		.total_size	= 256,
+		.page_size	= 16 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,		/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 64} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {16 * 1024, 16} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = NULL, /* AA 55 80 AA 55 10, only in A/A mux mode */
+			}
+		},
+		.printlock	= printlock_sst_fwhub,
+		.unlock		= unlock_sst_fwhub,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST49LF003A/B",
+		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST49LF003A,
+		.total_size	= 384,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,		/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 96} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 6} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {384 * 1024, 1} },
+				.block_erase = NULL, /* AA 55 80 AA 55 10, only in A/A mux mode */
+			}
+		},
+		.printlock	= printlock_sst_fwhub,
+		.unlock		= unlock_sst_fwhub,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		/* Contrary to the data sheet, TBL# on the SST49LF004B affects the top 128kB (instead of 64kB)
+		 * and is only honored for 64k block erase, but not 4k sector erase.
+		 */
+		.vendor		= "SST",
+		.name		= "SST49LF004A/B",
+		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST49LF004A,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,		/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = NULL, /* AA 55 80 AA 55 10, only in A/A mux mode */
+			},
+		},
+		.printlock	= printlock_sst_fwhub,
+		.unlock		= unlock_sst_fwhub,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST49LF004C",
+		.bustype	= CHIP_BUSTYPE_FWH,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST49LF004C,
+		.total_size	= 512,
+		.page_size	= 4 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (sst49lfxxxc.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = erase_sector_49lfxxxc,
+			}, {
+				.eraseblocks = { 
+					{64 * 1024, 7},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_block_82802ab,
+			}
+		},
+		.unlock		= unlock_49lfxxxc,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST49LF008A",
+		.bustype	= CHIP_BUSTYPE_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,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,		/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 256} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = NULL, /* AA 55 80 AA 55 10, only in A/A mux mode */
+			}
+		},
+		.printlock	= printlock_sst_fwhub,
+		.unlock		= unlock_sst_fwhub,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST49LF008C",
+		.bustype	= CHIP_BUSTYPE_FWH,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST49LF008C,
+		.total_size	= 1024,
+		.page_size	= 4 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (sst49lfxxxc.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 256} },
+				.block_erase = erase_sector_49lfxxxc,
+			}, {
+				.eraseblocks = { 
+					{64 * 1024, 15},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_block_82802ab,
+			}
+		},
+		.unlock		= unlock_49lfxxxc,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST49LF016C",
+		.bustype	= CHIP_BUSTYPE_FWH,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST49LF016C,
+		.total_size	= 2048,
+		.page_size	= 4 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (sst49lfxxxc.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = erase_sector_49lfxxxc,
+			}, {
+				.eraseblocks = { 
+					{64 * 1024, 31},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_block_82802ab,
+			}
+		},
+		.unlock		= unlock_49lfxxxc,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST49LF020",
+		.bustype	= CHIP_BUSTYPE_LPC,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST49LF020,
+		.total_size	= 256,
+		.page_size	= 16 * 1024,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,			/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 64} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {16 * 1024, 16} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = NULL,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST49LF020A",
+		.bustype	= CHIP_BUSTYPE_LPC,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST49LF020A,
+		.total_size	= 256,
+		.page_size	= 4 * 1024,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,			/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 64} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {16 * 1024, 16} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = NULL,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST49LF040",
+		.bustype	= CHIP_BUSTYPE_LPC,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST49LF040,
+		.total_size	= 512,
+		.page_size	= 4096,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,			/* 150 ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = NULL,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST49LF040B",
+		.bustype	= CHIP_BUSTYPE_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,
+		.probe		= probe_jedec,
+		.probe_timing	= 1,		/* 150ns */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = NULL,
+			}
+		},
+		.unlock		= unlock_82802ab,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST49LF080A",
+		.bustype	= CHIP_BUSTYPE_LPC, /* A/A Mux */
+		.manufacture_id	= SST_ID,
+ 		.model_id	= SST_SST49LF080A,
+		.total_size	= 1024,
+		.page_size	= 4096,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_FIXME, 
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 256} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = NULL,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "SST49LF160C",
+		.bustype	= CHIP_BUSTYPE_LPC,
+		.manufacture_id	= SST_ID,
+		.model_id	= SST_SST49LF160C,
+		.total_size	= 2048,
+		.page_size	= 4 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (sst49lfxxxc.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = erase_sector_49lfxxxc,
+			}, {
+				.eraseblocks = { 
+					{64 * 1024, 31},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_block_82802ab,
+			}
+		},
+		.unlock		= unlock_49lfxxxc,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M25P05-A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25P05A,
+		.total_size	= 64,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {32 * 1024, 2} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	/* The ST M25P05 is a bit of a problem. It has the same ID as the
+	 * ST M25P05-A in RES mode, but supports only 128 byte writes instead
+	 * of 256 byte writes. We rely heavily on the fact that probe_spi_res1
+	 * only is successful if RDID does not work.
+	 */
+	{
+		.vendor		= "ST",
+		.name		= "M25P05.RES",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= 0, /* Not used. */
+		.model_id	= ST_M25P05_RES,
+		.total_size	= 64,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_res1,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {32 * 1024, 2} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_1, /* 128 */
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M25P10-A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25P10A,
+		.total_size	= 128,
+		.page_size	= 256,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {32 * 1024, 4} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	/* The ST M25P10 has the same problem as the M25P05. */
+	{
+		.vendor		= "ST",
+		.name		= "M25P10.RES",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= 0, /* Not used. */
+		.model_id	= ST_M25P10_RES,
+		.total_size	= 128,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_res1,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {32 * 1024, 4} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_1, /* 128 */
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M25P20",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25P20,
+		.total_size	= 256,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 4} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M25P40",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25P40,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M25P40-old",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= 0, /* Not used. */
+		.model_id	= ST_M25P40_RES,
+		.total_size	= 512,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_res1,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M25P80",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25P80,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M25P16",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25P16,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 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		= "ST",
+		.name		= "M25P32",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25P32,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 64} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.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		= "ST",
+		.name		= "M25P64",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25P64,
+		.total_size	= 8192,
+		.page_size	= 256,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 128} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.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		= "ST",
+		.name		= "M25P128",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25P128,
+		.total_size	= 16384,
+		.page_size	= 256,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {256 * 1024, 64} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {16 * 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		= "ST",
+		.name		= "M25PX16",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25PX16,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.tested		= TEST_OK_PREW,
+		.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 = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M25PX32",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25PX32,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.tested		= TEST_OK_PRE,
+		.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_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M25PX64",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M25PX64,
+		.total_size	= 8192,
+		.page_size	= 256,
+		.tested		= TEST_OK_PRE,
+		.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_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M29F002B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M29F002B,
+		.total_size	= 256,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_AAA | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{32 * 1024, 1},
+					{64 * 1024, 3},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4750, 5250}, /* Datasheet says some are only 4.75-5.25 V */
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M29F002T/NT",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M29F002T,
+		.total_size	= 256,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_AAA | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 3},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4750, 5250}, /* Datasheet says some are only 4.75-5.25 V */
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M29F040B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M29F040B,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO, /* datasheet specifies no timing */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8}, },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		/* FIXME: this has WORD/BYTE sequences; 2AA for word, 555 for byte */
+		.vendor		= "ST",
+		.name		= "M29F400BB",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M29F400BB,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_SHIFTED | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_m29f400bt,
+		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (m29f400bt.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{32 * 1024, 1},
+					{64 * 1024, 7},
+				},
+				.block_erase = block_erase_m29f400bt,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = block_erase_chip_m29f400bt,
+			}
+		},
+		.write		= write_m29f400bt,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+	{
+		/* FIXME: this has WORD/BYTE sequences; 2AA for word, 555 for byte */
+		.vendor		= "ST",
+		.name		= "M29F400BT",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M29F400BT,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_SHIFTED | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_m29f400bt,
+		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (m29f400bt.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 7},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = block_erase_m29f400bt,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = block_erase_chip_m29f400bt,
+			}
+		},
+		.write		= write_m29f400bt,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M29W010B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M29W010B,
+		.total_size	= 128,
+		.page_size	= 16 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {16 * 1024, 8}, },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M29W040B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M29W040B,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8}, },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {2700, 3600},
+	},
+
+        {
+                .vendor         = "ST",
+                .name           = "M29W512B",
+                .bustype        = CHIP_BUSTYPE_PARALLEL,
+                .manufacture_id = ST_ID,
+                .model_id       = ST_M29W512B,
+                .total_size     = 64,
+                .page_size      = 64 * 1024,
+                .feature_bits   = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PRE,
+                .probe          = probe_jedec,
+                .probe_timing   = TIMING_ZERO,
+                .block_erasers  =
+                {
+                        {
+                                .eraseblocks = { {64 * 1024, 1} },
+                                .block_erase = erase_chip_block_jedec,
+                        }
+                },
+                .write          = write_jedec_1,
+                .read           = read_memmapped,
+		.voltage	= {2700, 3600},
+        },
+
+	{
+		.vendor		= "ST",
+		.name		= "M50FLW040A",
+		.bustype	= CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_LPC, /* A/A Mux */
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M50FLW040A,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_FIXME,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 16}, /* sector */
+					{64 * 1024, 5}, /* block */
+					{4 * 1024, 16}, /* sector */
+					{4 * 1024, 16}, /* sector */
+				},
+				.block_erase = NULL,
+			}, {
+				.eraseblocks = { {64 * 1024, 8}, },
+				.block_erase = erase_block_82802ab,
+			}
+		},
+		.unlock		= unlock_stm50flw0x0x,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* Also has 12V fast program & erase */
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M50FLW040B",
+		.bustype	= CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_LPC, /* A/A Mux */
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M50FLW040B,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_FIXME,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 16}, /* sector */
+					{4 * 1024, 16}, /* sector */
+					{64 * 1024, 5}, /* block */
+					{4 * 1024, 16}, /* sector */
+				},
+				.block_erase = NULL,
+			}, {
+				.eraseblocks = { {64 * 1024, 8}, },
+				.block_erase = erase_block_82802ab,
+			}
+		},
+		.unlock		= unlock_stm50flw0x0x,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* Also has 12V fast program & erase */
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M50FLW080A",
+		.bustype	= CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_LPC, /* A/A Mux */
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M50FLW080A,
+		.total_size	= 1024,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_FIXME,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 16}, /* sector */
+					{64 * 1024, 13}, /* block */
+					{4 * 1024, 16}, /* sector */
+					{4 * 1024, 16}, /* sector */
+				},
+				.block_erase = NULL,
+			}, {
+				.eraseblocks = { {64 * 1024, 16}, },
+				.block_erase = erase_block_82802ab,
+			}
+		},
+		.unlock		= unlock_stm50flw0x0x,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* Also has 12V fast program & erase */
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M50FLW080B",
+		.bustype	= CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_LPC, /* A/A Mux */
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M50FLW080B,
+		.total_size	= 1024,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_FIXME,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 16}, /* sector */
+					{4 * 1024, 16}, /* sector */
+					{64 * 1024, 13}, /* block */
+					{4 * 1024, 16}, /* sector */
+				},
+				.block_erase = NULL,
+			}, {
+				.eraseblocks = { {64 * 1024, 16}, },
+				.block_erase = erase_block_82802ab,
+			}
+		},
+		.unlock		= unlock_stm50flw0x0x,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* Also has 12V fast program & erase */
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M50FW002",
+		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M50FW002,
+		.total_size	= 256,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (sst49lfxxxc.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 3},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_block_82802ab,
+			}
+		},
+		.unlock		= unlock_stm50flw0x0x,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* Also has 12V fast program & erase */
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M50FW016",
+		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M50FW016,
+		.total_size	= 2048,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (82802ab.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 32}, },
+				.block_erase = erase_block_82802ab,
+			}
+		},
+		.unlock		= unlock_stm50flw0x0x,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* Also has 12V fast program & erase */
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M50FW040",
+		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M50FW040,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (82802ab.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8}, },
+				.block_erase = erase_block_82802ab,
+			}
+		},
+		.unlock		= unlock_stm50flw0x0x,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* Also has 12V fast program & erase */
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M50FW080",
+		.bustype	= CHIP_BUSTYPE_FWH, /* A/A Mux */
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M50FW080,
+		.total_size	= 1024,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_IGNORED, /* routine doesn't use probe_timing (82802ab.c) */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 16}, },
+				.block_erase = erase_block_82802ab,
+			}
+		},
+		.unlock		= unlock_stm50flw0x0x,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* Also has 12V fast program & erase */
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "M50LPW116",
+		.bustype	= CHIP_BUSTYPE_LPC, /* A/A Mux */
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M50LPW116,
+		.total_size	= 2048,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_82802ab,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{4 * 1024, 16},
+					{64 * 1024, 30},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_block_82802ab,
+			}
+		},
+		.unlock		= unlock_stm50flw0x0x,
+		.write		= write_82802ab,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* Also has 12V fast program & erase */
+	},
+
+	{
+		.vendor		= "SyncMOS/MoselVitelic",
+		.name		= "{F,S,V}29C51001B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= SM_MVC_29C51001B,
+		.total_size	= 128,
+		.page_size	= 512,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {512, 256} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "SyncMOS/MoselVitelic",
+		.name		= "{F,S,V}29C51001T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= SM_MVC_29C51001T,
+		.total_size	= 128,
+		.page_size	= 512,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {512, 256} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "SyncMOS/MoselVitelic",
+		.name		= "{F,S,V}29C51002B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= SM_MVC_29C51002B,
+		.total_size	= 256,
+		.page_size	= 512,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {512, 512} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "SyncMOS/MoselVitelic",
+		.name		= "{F,S,V}29C51002T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= SM_MVC_29C51002T,
+		.total_size	= 256,
+		.page_size	= 512,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {512, 512} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "SyncMOS/MoselVitelic",
+		.name		= "{F,S,V}29C51004B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= SM_MVC_29C51004B,
+		.total_size	= 512,
+		.page_size	= 1024,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {1024, 512} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "SyncMOS/MoselVitelic",
+		.name		= "{F,S,V}29C51004T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= SM_MVC_29C51004T,
+		.total_size	= 512,
+		.page_size	= 1024,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {1024, 512} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "SyncMOS/MoselVitelic",
+		.name		= "{S,V}29C31004B",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= SM_MVC_29C31004B,
+		.total_size	= 512,
+		.page_size	= 1024,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {1024, 512} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "SyncMOS/MoselVitelic",
+		.name		= "{S,V}29C31004T",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= SYNCMOS_MVC_ID,
+		.model_id	= SM_MVC_29C31004T,
+		.total_size	= 512,
+		.page_size	= 1024,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {1024, 512} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "TI",
+		.name		= "TMS29F002RB",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= TI_OLD_ID,
+		.model_id	= TI_TMS29F002RB,
+		.total_size	= 256,
+		.page_size	= 16384, /* Non-uniform sectors */
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{16 * 1024, 1},
+					{8 * 1024, 2},
+					{32 * 1024, 1},
+					{64 * 1024, 3},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "TI",
+		.name		= "TMS29F002RT",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= TI_OLD_ID,
+		.model_id	= TI_TMS29F002RT,
+		.total_size	= 256,
+		.page_size	= 16384, /* Non-uniform sectors */
+		.feature_bits	= FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= TIMING_ZERO,	/* Datasheet has no timing info specified */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 3},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			},
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W25Q80",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= WINBOND_NEX_ID,
+		.model_id	= WINBOND_NEX_W25Q80,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 256} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 32} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W25Q16",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= WINBOND_NEX_ID,
+		.model_id	= WINBOND_NEX_W25Q16,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 64} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W25Q32",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= WINBOND_NEX_ID,
+		.model_id	= WINBOND_NEX_W25Q32,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 1024} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 128} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 64} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W25Q64",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= WINBOND_NEX_ID,
+		.model_id	= WINBOND_NEX_W25Q64,
+		.total_size	= 8192,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 2048} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 256} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 128} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W25Q128",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= WINBOND_NEX_ID,
+		.model_id	= WINBOND_NEX_W25Q128,
+		.total_size	= 16384,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PROBE,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 4096} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 512} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.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		= "Winbond",
+		.name		= "W25X10",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= WINBOND_NEX_ID,
+		.model_id	= WINBOND_NEX_W25X10,
+		.total_size	= 128,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 32} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 2} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W25X20",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= WINBOND_NEX_ID,
+		.model_id	= WINBOND_NEX_W25X20,
+		.total_size	= 256,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 64} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 4} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W25X40",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= WINBOND_NEX_ID,
+		.model_id	= WINBOND_NEX_W25X40,
+		.total_size	= 512,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W25X80",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= WINBOND_NEX_ID,
+		.model_id	= WINBOND_NEX_W25X80,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 256} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W25X16",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= WINBOND_NEX_ID,
+		.model_id	= WINBOND_NEX_W25X16,
+		.total_size	= 2048,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 512} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 64} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 32} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {2 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W25X32",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= WINBOND_NEX_ID,
+		.model_id	= WINBOND_NEX_W25X32,
+		.total_size	= 4096,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PROBE,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 1024} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 128} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 64} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {4 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W25X64",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= WINBOND_NEX_ID,
+		.model_id	= WINBOND_NEX_W25X64,
+		.total_size	= 8192,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PROBE,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 2048} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 256} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 128} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {8 * 1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2700, 3600},
+	},
+
+	{
+		.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_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= 10,		/* used datasheet for the W29C011A */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {128 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec,
+		.read		= read_memmapped,
+	},
+#endif
+	{
+		.vendor		= "Winbond",
+		.name		= "W29C020(C)/W29C022",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= WINBOND_ID,
+		.model_id	= WINBOND_W29C020,
+		.total_size	= 256,
+		.page_size	= 128,
+		.feature_bits	= FEATURE_LONG_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= 10,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+#ifdef ts
+	{
+		.vendor		= "Winbond",
+		.name		= "W29C040/P",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= WINBOND_ID,
+		.model_id	= WINBOND_W29C040,
+		.total_size	= 512,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_LONG_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= 10, 
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.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,
+		.manufacture_id	= WINBOND_ID,
+		.model_id	= WINBOND_W39L040,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_jedec,
+		.probe_timing	= 10,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.printlock	= printlock_w39l040,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W39V040A",
+		.bustype	= CHIP_BUSTYPE_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,
+		.probe		= probe_jedec,
+		.probe_timing	= 10, 
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.printlock	= printlock_w39v040a,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W39V040B",
+		.bustype	= CHIP_BUSTYPE_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,
+		.probe		= probe_jedec,
+		.probe_timing	= 10, 
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.printlock	= printlock_w39v040b,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W39V040C",
+		.bustype	= CHIP_BUSTYPE_LPC,
+		.manufacture_id	= WINBOND_ID,
+		.model_id	= WINBOND_W39V040C,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= 10,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.printlock	= printlock_w39v040c,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W39V040FA",
+		.bustype	= CHIP_BUSTYPE_FWH,
+		.manufacture_id	= WINBOND_ID,
+		.model_id	= WINBOND_W39V040FA,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= 10,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 128} },
+				.block_erase = erase_block_jedec,
+			}, {
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.printlock	= printlock_w39v040fa,
+		.unlock		= unlock_sst_fwhub,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W39V040FB",
+		.bustype	= CHIP_BUSTYPE_FWH,
+		.manufacture_id	= WINBOND_ID,
+		.model_id	= WINBOND_W39V040B,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= 10, 
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.printlock	= printlock_w39v040fb,
+		.unlock		= unlock_w39v040fb,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W39V040FC",
+		.bustype	= CHIP_BUSTYPE_FWH,
+		.manufacture_id	= WINBOND_ID,
+		.model_id	= WINBOND_W39V040C,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= 10,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.printlock	= printlock_w39v040fc,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W39V080A",
+		.bustype	= CHIP_BUSTYPE_LPC,
+		.manufacture_id	= WINBOND_ID,
+		.model_id	= WINBOND_W39V080A,
+		.total_size	= 1024,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= 10, 
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.printlock	= printlock_w39v080a,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W49F002U/N",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= WINBOND_ID,
+		.model_id	= WINBOND_W49F002U,
+		.total_size	= 256,
+		.page_size	= 128,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.probe_timing	= 10,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{128 * 1024, 1},
+					{96 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W49F020",
+		.bustype	= CHIP_BUSTYPE_PARALLEL,
+		.manufacture_id	= WINBOND_ID,
+		.model_id	= WINBOND_W49F020,
+		.total_size	= 256,
+		.page_size	= 128,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PROBE,
+		.probe		= probe_jedec,
+		.probe_timing	= 10,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {4500, 5500},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W49V002A",
+		.bustype	= CHIP_BUSTYPE_LPC,
+		.manufacture_id	= WINBOND_ID,
+		.model_id	= WINBOND_W49V002A,
+		.total_size	= 256,
+		.page_size	= 128,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_jedec,
+		.probe_timing	= 10,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 3},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W49V002FA",
+		.bustype	= CHIP_BUSTYPE_FWH,
+		.manufacture_id	= WINBOND_ID,
+		.model_id	= WINBOND_W49V002FA,
+		.total_size	= 256,
+		.page_size	= 128,
+		.feature_bits	= FEATURE_EITHER_RESET,
+		.tested		= TEST_OK_PR,
+		.probe		= probe_jedec,
+		.probe_timing	= 10,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = {
+					{64 * 1024, 3},
+					{32 * 1024, 1},
+					{8 * 1024, 2},
+					{16 * 1024, 1},
+				},
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600},
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W39V080FA",
+		.bustype	= CHIP_BUSTYPE_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,
+		.probe		= probe_jedec,
+		.probe_timing	= 10,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 16}, },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.printlock	= printlock_w39v080fa,
+		.unlock		= unlock_w39v080fa,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+		.voltage	= {3000, 3600}, /* 12 V fast program mode */
+	},
+
+	{
+		.vendor		= "Winbond",
+		.name		= "W39V080FA (dual mode)",
+		.bustype	= CHIP_BUSTYPE_FWH,
+		.manufacture_id	= WINBOND_ID,
+		.model_id	= WINBOND_W39V080FA_DM,
+		.total_size	= 512,
+		.page_size	= 64 * 1024,
+		.feature_bits	= FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+		.tested		= TEST_UNTESTED,
+		.probe		= probe_jedec,
+		.probe_timing	= 10,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {64 * 1024, 8}, },
+				.block_erase = erase_sector_jedec,
+			}, {
+				.eraseblocks = { {512 * 1024, 1} },
+				.block_erase = erase_chip_block_jedec,
+			}
+		},
+		.printlock	= printlock_w39v080fa_dual,
+		.write		= write_jedec_1,
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "AMIC",
+		.name		= "unknown AMIC SPI chip",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= AMIC_ID,
+		.model_id	= GENERIC_DEVICE_ID,
+		.total_size	= 0,
+		.page_size	= 256,
+		.tested		= TEST_BAD_PREW,
+		.probe		= probe_spi_rdid4,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+	},
+
+	{
+		.vendor		= "Atmel",
+		.name		= "unknown Atmel SPI chip",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= GENERIC_DEVICE_ID,
+		.total_size	= 0,
+		.page_size	= 256,
+		.tested		= TEST_BAD_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+	},
+
+	{
+		.vendor		= "Eon",
+		.name		= "unknown Eon SPI chip",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= EON_ID_NOPREFIX,
+		.model_id	= GENERIC_DEVICE_ID,
+		.total_size	= 0,
+		.page_size	= 256,
+		.tested		= TEST_BAD_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+	},
+
+	{
+		.vendor		= "Macronix",
+		.name		= "unknown Macronix SPI chip",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= MACRONIX_ID,
+		.model_id	= GENERIC_DEVICE_ID,
+		.total_size	= 0,
+		.page_size	= 256,
+		.tested		= TEST_BAD_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+	},
+
+	{
+		.vendor		= "PMC",
+		.name		= "unknown PMC SPI chip",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= PMC_ID,
+		.model_id	= GENERIC_DEVICE_ID,
+		.total_size	= 0,
+		.page_size	= 256,
+		.tested		= TEST_BAD_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+	},
+
+	{
+		.vendor		= "SST",
+		.name		= "unknown SST SPI chip",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SST_ID,
+		.model_id	= GENERIC_DEVICE_ID,
+		.total_size	= 0,
+		.page_size	= 256,
+		.tested		= TEST_BAD_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+	},
+
+	{
+		.vendor		= "ST",
+		.name		= "unknown ST SPI chip",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= GENERIC_DEVICE_ID,
+		.total_size	= 0,
+		.page_size	= 256,
+		.tested		= TEST_BAD_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+	},
+
+	{
+		.vendor		= "Sanyo",
+		.name		= "unknown Sanyo SPI chip",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= SANYO_ID,
+		.model_id	= GENERIC_DEVICE_ID,
+		.total_size	= 0,
+		.page_size	= 256,
+		.tested		= TEST_BAD_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+		.write		= NULL,
+		.read		= NULL,
+	},
+
+	{
+		.vendor		= "Generic",
+		.name		= "unknown SPI chip (RDID)",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= GENERIC_MANUF_ID,
+		.model_id	= GENERIC_DEVICE_ID,
+		.total_size	= 0,
+		.page_size	= 256,
+		.tested		= TEST_BAD_PREW,
+		.probe		= probe_spi_rdid,
+		.write		= NULL,
+	},
+	{
+		.vendor		= "Generic",
+		.name		= "unknown SPI chip (REMS)",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= GENERIC_MANUF_ID,
+		.model_id	= GENERIC_DEVICE_ID,
+		.total_size	= 0,
+		.page_size	= 256,
+		.tested		= TEST_BAD_PREW,
+		.probe		= probe_spi_rems,
+		.write		= NULL,
+	},
+#endif
+	{ NULL 	}
+};
diff --git a/src/mainboard/msi/ms6119/f/flashchips.h b/src/mainboard/msi/ms6119/f/flashchips.h
new file mode 100644
index 0000000..3b2b94f
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/flashchips.h
@@ -0,0 +1,641 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2000 Ronald G. Minnich <rminnich@gmail.com>
+ * Copyright (C) 2005-2007 coresystems GmbH <stepan@coresystems.de>
+ * Copyright (C) 2006-2009 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#ifndef __FLASHCHIPS_H__
+#define __FLASHCHIPS_H__ 1
+
+/*
+ * Please keep this list sorted alphabetically by manufacturer. The first
+ * entry of each section should be the manufacturer ID, followed by the
+ * list of devices from that manufacturer (sorted by device IDs).
+ *
+ * All LPC/FWH parts (parallel flash) have 8-bit device IDs if there is no
+ * continuation code.
+ * SPI parts have 16-bit device IDs if they support RDID.
+ */
+
+#define GENERIC_MANUF_ID	0xffff	/* Check if there is a vendor ID */
+#define GENERIC_DEVICE_ID	0xffff	/* Only match the vendor ID */
+
+#define ALLIANCE_ID		0x52	/* Alliance Semiconductor */
+#define ALLIANCE_AS29F002B	0x34
+#define ALLIANCE_AS29F002T	0xB0
+#define ALLIANCE_AS29F010	0x04
+#define ALLIANCE_AS29F040	0xA4
+#define ALLIANCE_AS29F200B	0x57
+#define ALLIANCE_AS29F200T	0x51
+#define ALLIANCE_AS29LV160B	0x49
+#define ALLIANCE_AS29LV160T	0xCA
+#define ALLIANCE_AS29LV400B	0xBA
+#define ALLIANCE_AS29LV400T	0xB9
+#define ALLIANCE_AS29LV800B	0x5B
+#define ALLIANCE_AS29LV800T	0xDA
+
+#define AMD_ID			0x01	/* AMD */
+#define AMD_AM29DL400BT		0x0C
+#define AMD_AM29DL400BB		0x0F
+#define AMD_AM29DL800BT		0x4A
+#define AMD_AM29DL800BB		0xCB
+#define AMD_AM29F002BB		0x34	/* Same as Am29F002NBB */
+#define AMD_AM29F002BT		0xB0	/* Same as Am29F002NBT */
+#define AMD_AM29F004BB		0x7B
+#define AMD_AM29F004BT		0x77
+#define AMD_AM29F016D		0xAD
+#define AMD_AM29F010B		0x20	/* Same as Am29F010A */
+#define AMD_AM29F040B		0xA4
+#define AMD_AM29F080B		0xD5
+#define AMD_AM29F200BB		0x57
+#define AMD_AM29F200BT		0x51
+#define AMD_AM29F400BB		0xAB
+#define AMD_AM29F400BT		0x23
+#define AMD_AM29F800BB		0x58
+#define AMD_AM29F800BT		0xD6
+#define AMD_AM29LV001BB		0x6D
+#define AMD_AM29LV001BT		0xED
+#define AMD_AM29LV002BB		0xC2
+#define AMD_AM29LV002BT		0x40
+#define AMD_AM29LV004BB		0xB6
+#define AMD_AM29LV004BT		0xB5
+#define AMD_AM29LV008BB		0x37
+#define AMD_AM29LV008BT		0x3E
+#define AMD_AM29LV040B		0x4F
+#define AMD_AM29LV080B		0x38	/* Same as Am29LV081B */
+#define AMD_AM29LV200BB		0xBF
+#define AMD_AM29LV200BT		0x3B
+#define AMD_AM29LV800BB		0x5B	/* Same as Am29LV800DB */
+#define AMD_AM29LV400BT		0xB9
+#define AMD_AM29LV400BB		0xBA
+#define AMD_AM29LV800BT		0xDA	/* Same as Am29LV800DT */
+
+#define AMIC_ID			0x7F37	/* AMIC */
+#define AMIC_ID_NOPREFIX	0x37	/* AMIC */
+#define AMIC_A25L05PT		0x2020
+#define AMIC_A25L05PU		0x2010
+#define AMIC_A25L10PT		0x2021
+#define AMIC_A25L10PU		0x2011
+#define AMIC_A25L20PT		0x2022
+#define AMIC_A25L20PU		0x2012
+#define AMIC_A25L40PT		0x2013	/* Datasheet says T and U have
+					   same device ID. Confirmed by
+					   hardware testing. */
+#define AMIC_A25L40PU		0x2013
+#define AMIC_A25L80P		0x2014	/* Seems that no A25L80PT exists */
+#define AMIC_A25L16PT		0x2025
+#define AMIC_A25L16PU		0x2015
+#define AMIC_A25L512		0x3010
+#define AMIC_A25L010		0x3011
+#define AMIC_A25L020		0x3012
+#define AMIC_A25L040		0x3013
+#define AMIC_A25L080		0x3014
+#define AMIC_A25L016		0x3015
+#define AMIC_A25L032		0x3016
+#define AMIC_A25LQ032		0x4016
+#define AMIC_A29002B		0x0d
+#define AMIC_A29002T		0x8C	/* Same as A290021T */
+#define AMIC_A29040B		0x86
+#define AMIC_A29400T		0xB0	/* Same as 294001T */
+#define AMIC_A29400U		0x31	/* Same as A294001U */
+#define AMIC_A29800T		0x0E
+#define AMIC_A29800U		0x8F
+#define AMIC_A29L004T		0x34	/* Same as A29L400T */
+#define AMIC_A29L004U		0xB5	/* Same as A29L400U */
+#define AMIC_A29L008T		0x1A	/* Same as A29L800T */
+#define AMIC_A29L008U		0x9B	/* Same as A29L800U */
+#define AMIC_A29L040		0x92
+#define AMIC_A49LF040A		0x9d
+
+#define ATMEL_ID		0x1F	/* Atmel */
+#define ATMEL_AT25DF021		0x4300
+#define ATMEL_AT25DF041A	0x4401
+#define ATMEL_AT25DF081		0x4502
+#define ATMEL_AT25DF081A	0x4501	/* Yes, 81A has a lower number than 81 */
+#define ATMEL_AT25DF161		0x4602
+#define ATMEL_AT25DF321		0x4700	/* Same as 26DF321 */
+#define ATMEL_AT25DF321A	0x4701
+#define ATMEL_AT25DF641		0x4800
+#define ATMEL_AT25DQ161		0x8600
+#define ATMEL_AT25F512		/* No device ID found in datasheet. Vendor ID
+				 * can be read with AT25F512A_RDID */
+#define ATMEL_AT25F512A		0x65 /* Needs AT25F512A_RDID */
+#define ATMEL_AT25F512B		0x6500
+#define ATMEL_AT25F1024		/* No device ID found in datasheet. Vendor ID
+				 * can be read with AT25F512A_RDID */
+#define ATMEL_AT25F1024A		0x60 /* Needs AT25F512A_RDID */
+#define ATMEL_AT25FS010		0x6601
+#define ATMEL_AT25FS040		0x6604
+#define ATMEL_AT26DF041		0x4400
+#define ATMEL_AT26DF081		0x4500	/* guessed, no datasheet available */
+#define ATMEL_AT26DF081A	0x4501
+#define ATMEL_AT26DF161		0x4600
+#define ATMEL_AT26DF161A	0x4601
+#define ATMEL_AT26DF321		0x4700	/* Same as 25DF321 */
+#define ATMEL_AT26F004		0x0400
+#define ATMEL_AT29C040A		0xA4
+#define ATMEL_AT29C010A		0xD5
+#define ATMEL_AT29C020		0xDA
+#define ATMEL_AT29C512		0x5D
+#define ATMEL_AT45BR3214B	/* No ID available */
+#define ATMEL_AT45CS1282	0x2920
+#define ATMEL_AT45D011		/* No ID available */
+#define ATMEL_AT45D021A		/* No ID available */
+#define ATMEL_AT45D041A		/* No ID available */
+#define ATMEL_AT45D081A		/* No ID available */
+#define ATMEL_AT45D161		/* No ID available */
+#define ATMEL_AT45DB011		/* No ID available */
+#define ATMEL_AT45DB011B	/* No ID available */
+#define ATMEL_AT45DB011D	0x2200
+#define ATMEL_AT45DB021A	/* No ID available */
+#define ATMEL_AT45DB021B	/* No ID available */
+#define ATMEL_AT45DB021D	0x2300
+#define ATMEL_AT45DB041A	/* No ID available */
+#define ATMEL_AT45DB041D	0x2400
+#define ATMEL_AT45DB081A	/* No ID available */
+#define ATMEL_AT45DB081D	0x2500
+#define ATMEL_AT45DB161		/* No ID available */
+#define ATMEL_AT45DB161B	/* No ID available */
+#define ATMEL_AT45DB161D	0x2600
+#define ATMEL_AT45DB321		/* No ID available */
+#define ATMEL_AT45DB321B	/* No ID available */
+#define ATMEL_AT45DB321C	0x2700
+#define ATMEL_AT45DB321D	0x2701 /* Buggy data sheet */
+#define ATMEL_AT45DB642		/* No ID available */
+#define ATMEL_AT45DB642D	0x2800
+#define ATMEL_AT49BV512		0x03
+#define ATMEL_AT49F020		0x0B
+#define ATMEL_AT49F002N		0x07	/* for AT49F002(N)  */
+#define ATMEL_AT49F002NT		0x08	/* for AT49F002(N)T */
+
+/* Bright Microelectronics has the same manufacturer ID as Hyundai... */
+#define BRIGHT_ID		0xAD	/* Bright Microelectronics */
+#define BRIGHT_BM29F040		0x40
+#define BRIGHT_BM29F400B	0xAB
+#define BRIGHT_BM29F400T	0xAD
+
+#define CATALYST_ID		0x31	/* Catalyst */
+
+#define EMST_ID			0x8C	/* EMST / EFST Elite Flash Storage */
+#define EMST_F25L008A		0x2014
+#define EMST_F49B002UA		0x00
+
+/*
+ * EN25 chips are SPI, first byte of device ID is memory type,
+ * second byte of device ID is log(bitsize)-9.
+ * Vendor and device ID of EN29 series are both prefixed with 0x7F, which
+ * is the continuation code for IDs in bank 2.
+ * Vendor ID of EN25 series is NOT prefixed with 0x7F, this results in
+ * a collision with Mitsubishi. Mitsubishi once manufactured flash chips.
+ * Let's hope they are not manufacturing SPI flash chips as well.
+ */
+#define EON_ID			0x7F1C	/* EON Silicon Devices */
+#define EON_ID_NOPREFIX		0x1C	/* EON, missing 0x7F prefix */
+#define EON_EN25B05		0x2010	/* Same as P05, 2^19 kbit or 2^16 kByte */
+#define EON_EN25B05T		0x25
+#define EON_EN25B05B		0x95
+#define EON_EN25B10		0x2011	/* Same as P10 */
+#define EON_EN25B10T		0x40
+#define EON_EN25B10B		0x30
+#define EON_EN25B20		0x2012	/* Same as P20 */
+#define EON_EN25B20T		0x41
+#define EON_EN25B20B		0x31
+#define EON_EN25B40		0x2013	/* Same as P40 */
+#define EON_EN25B40T		0x42
+#define EON_EN25B40B		0x32
+#define EON_EN25B80		0x2014	/* Same as P80 */
+#define EON_EN25B80T		0x43
+#define EON_EN25B80B		0x33
+#define EON_EN25B16		0x2015	/* Same as P16 */
+#define EON_EN25B16T		0x44
+#define EON_EN25B16B		0x34
+#define EON_EN25B32		0x2016	/* Same as P32 */
+#define EON_EN25B32T		0x45
+#define EON_EN25B32B		0x35
+#define EON_EN25B64		0x2017	/* Same as P64 */
+#define EON_EN25B64T		0x46
+#define EON_EN25B64B		0x36
+#define EON_EN25D16		0x3015
+#define EON_EN25F05		0x3110
+#define EON_EN25F10		0x3111
+#define EON_EN25F20		0x3112
+#define EON_EN25F40		0x3113
+#define EON_EN25F80		0x3114
+#define EON_EN25F16		0x3115
+#define EON_EN25F32		0x3116
+#define EON_EN29F512		0x7F21
+#define EON_EN29F010		0x20
+#define EON_EN29F040A		0x7F04
+#define EON_EN29LV010		0x7F6E
+#define EON_EN29LV040A		0x7F4F	/* EN29LV040(A) */
+#define EON_EN29F002T		0x7F92	/* Same as EN29F002A */
+#define EON_EN29F002B		0x7F97	/* Same as EN29F002AN */
+
+#define FUJITSU_ID		0x04	/* Fujitsu */
+#define FUJITSU_MBM29DL400BC	0x0F
+#define FUJITSU_MBM29DL400TC	0x0C
+#define FUJITSU_MBM29DL800BA	0xCB
+#define FUJITSU_MBM29DL800TA	0x4A
+#define FUJITSU_MBM29F002BC	0x34
+#define FUJITSU_MBM29F002TC	0xB0
+#define FUJITSU_MBM29F004BC	0x7B
+#define FUJITSU_MBM29F004TC	0x77
+#define FUJITSU_MBM29F040C	0xA4
+#define FUJITSU_MBM29F080A	0xD5
+#define FUJITSU_MBM29F200BC	0x57
+#define FUJITSU_MBM29F200TC	0x51
+#define FUJITSU_MBM29F400BC	0xAB
+#define FUJITSU_MBM29F400TC	0x23
+#define FUJITSU_MBM29F800BA	0x58
+#define FUJITSU_MBM29F800TA	0xD6
+#define FUJITSU_MBM29LV002BC	0xC2
+#define FUJITSU_MBM29LV002TC	0x40
+#define FUJITSU_MBM29LV004BC	0xB6
+#define FUJITSU_MBM29LV004TC	0xB5
+#define FUJITSU_MBM29LV008BA	0x37
+#define FUJITSU_MBM29LV008TA	0x3E
+#define FUJITSU_MBM29LV080A	0x38
+#define FUJITSU_MBM29LV200BC	0xBF
+#define FUJITSU_MBM29LV200TC	0x3B
+#define FUJITSU_MBM29LV400BC	0xBA
+#define FUJITSU_MBM29LV400TC	0xB9
+#define FUJITSU_MBM29LV800BA	0x5B	/* Same as MBM29LV800BE */
+#define FUJITSU_MBM29LV800TA	0xDA	/* Same as MBM29LV800TE */
+
+#define HYUNDAI_ID		0xAD	/* Hyundai */
+#define HYUNDAI_HY29F400T	0x23	/* Same as HY29F400AT */
+#define HYUNDAI_HY29F800B	0x58	/* Same as HY29F800AB */
+#define HYUNDAI_HY29LV800B	0x5B
+#define HYUNDAI_HY29F040A	0xA4
+#define HYUNDAI_HY29F400B	0xAB	/* Same as HY29F400AB */
+#define HYUNDAI_HY29F002B	0x34
+#define HYUNDAI_HY29F002T	0xB0
+#define HYUNDAI_HY29LV400T	0xB9
+#define HYUNDAI_HY29LV400B	0xBA
+#define HYUNDAI_HY29F080	0xD5
+#define HYUNDAI_HY29F800T	0xD6	/* Same as HY29F800AT */
+#define HYUNDAI_HY29LV800T	0xDA
+
+#define IMT_ID			0x7F1F	/* Integrated Memory Technologies */
+#define IMT_IM29F004B		0xAE
+#define IMT_IM29F004T		0xAF
+
+#define INTEL_ID		0x89	/* Intel */
+#define INTEL_28F320J5		0x14
+#define INTEL_28F640J5		0x15
+#define INTEL_28F320J3		0x16
+#define INTEL_28F640J3		0x17
+#define INTEL_28F128J3		0x18
+#define INTEL_28F256J3		0x1D
+#define INTEL_28F400T		0x70	/* 28F400BV/BX/CE/CV-T */
+#define INTEL_28F400B		0x71	/* 28F400BV/BX/CE/CV-B */
+#define INTEL_28F200T		0x74	/* 28F200BL/BV/BX/CV-T */
+#define INTEL_28F200B		0x75	/* 28F200BL/BV/BX/CV-B */
+#define INTEL_28F004T		0x78	/* 28F004B5/BE/BV/BX-T */
+#define INTEL_28F004B		0x79	/* 28F004B5/BE/BV/BX-B */
+#define INTEL_28F002T		0x7C	/* 28F002BC/BL/BV/BX-T */
+#define INTEL_28F002B		0x7D	/* 28F002BL/BV/BX-B */
+#define INTEL_28F001T		0x94	/* 28F001BN/BX-T */
+#define INTEL_28F001B		0x95	/* 28F001BN/BX-B */
+#define INTEL_28F008T		0x98	/* 28F008BE/BV-T */
+#define INTEL_28F008B		0x99	/* 28F008BE/BV-B */
+#define INTEL_28F800T		0x9C	/* 28F800B5/BV/CE/CV-T */
+#define INTEL_28F800B		0x9D	/* 28F800B5/BV/CE/CV-B */
+#define INTEL_28F016SV		0xA0	/* 28F016SA/SV */
+#define INTEL_28F008SA		0xA2
+#define INTEL_28F008S3		0xA6	/* 28F008S3/S5/SC */
+#define INTEL_28F004S3		0xA7	/* 28F008S3/S5/SC */
+#define INTEL_28F016XS		0xA8
+#define INTEL_28F016S3		0xAA	/* 28F016S3/S5/SC */
+#define INTEL_82802AC		0xAC
+#define INTEL_82802AB		0xAD
+#define INTEL_28F010		0xB4
+#define INTEL_28F512		0xB8
+#define INTEL_28F256A		0xB9
+#define INTEL_28F020		0xBD
+#define INTEL_28F016B3T		0xD0	/* 28F016B3-T */
+#define INTEL_28F016B3B		0xD1	/* 28F016B3-B */
+#define INTEL_28F008B3T		0xD2	/* 28F008B3-T */
+#define INTEL_28F008B3B		0xD3	/* 28F008B3-B */
+#define INTEL_28F004B3T		0xD4	/* 28F004B3-T */
+#define INTEL_28F004B3B		0xD5	/* 28F004B3-B */
+
+#define SHARP_LH28F008SA	0xA2	/* Sharp chip, Intel Vendor ID */
+#define SHARP_LH28F008SC	0xA6	/* Sharp chip, Intel Vendor ID */
+
+#define ISSI_ID			0xD5	/* ISSI Integrated Silicon Solutions */
+
+/*
+ * MX25 chips are SPI, first byte of device ID is memory type,
+ * second byte of device ID is log(bitsize)-9.
+ * Generalplus SPI chips seem to be compatible with Macronix
+ * and use the same set of IDs.
+ */
+#define MACRONIX_ID		0xC2	/* Macronix (MX) */
+#define MACRONIX_MX25L512	0x2010	/* Same as MX25V512 */
+#define MACRONIX_MX25L1005	0x2011
+#define MACRONIX_MX25L2005	0x2012
+#define MACRONIX_MX25L4005	0x2013	/* MX25L4005{,A} */
+#define MACRONIX_MX25L8005	0x2014	/* Same as MX25V8005 */
+#define MACRONIX_MX25L1605	0x2015	/* MX25L1605{,A,D} */
+#define MACRONIX_MX25L3205	0x2016	/* MX25L3205{,A} */
+#define MACRONIX_MX25L6405	0x2017	/* MX25L3205{,D} */
+#define MACRONIX_MX25L12805	0x2018	/* MX25L12805 */
+#define MACRONIX_MX25L1635D	0x2415
+#define MACRONIX_MX25L1635E	0x2515	/* MX25L1635{E} */
+#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_MX29F004B	0x46
+#define MACRONIX_MX29F004T	0x45
+#define MACRONIX_MX29F022T	0x36	/* Same as MX29F022NT */
+#define MACRONIX_MX29F040	0xA4	/* Same as MX29F040C */
+#define MACRONIX_MX29F080	0xD5
+#define MACRONIX_MX29F200B	0x57	/* Same as MX29F200CB */
+#define MACRONIX_MX29F200T	0x51	/* Same as MX29F200CT */
+#define MACRONIX_MX29F400B	0xAB	/* Same as MX29F400CB */
+#define MACRONIX_MX29F400T	0x23	/* Same as MX29F400CT */
+#define MACRONIX_MX29F800B	0x58
+#define MACRONIX_MX29F800T	0xD6
+#define MACRONIX_MX29LV002CB	0x5A
+#define MACRONIX_MX29LV002CT	0x59
+#define MACRONIX_MX29LV004B	0xB6	/* Same as MX29LV004CB */
+#define MACRONIX_MX29LV004T	0xB5	/* Same as MX29LV004CT */
+#define MACRONIX_MX29LV008B	0x37	/* Same as MX29LV008CB */
+#define MACRONIX_MX29LV008T	0x3E	/* Same as MX29LV008CT */
+#define MACRONIX_MX29LV040	0x4F	/* Same as MX29LV040C */
+#define MACRONIX_MX29LV081	0x38
+#define MACRONIX_MX29LV128DB	0x7A
+#define MACRONIX_MX29LV128DT	0x7E
+#define MACRONIX_MX29LV160DB	0x49	/* Same as MX29LV161DB/MX29LV160CB */
+#define MACRONIX_MX29LV160DT	0xC4	/* Same as MX29LV161DT/MX29LV160CT */
+#define MACRONIX_MX29LV320DB	0xA8	/* Same as MX29LV321DB */
+#define MACRONIX_MX29LV320DT	0xA7	/* Same as MX29LV321DT */
+#define MACRONIX_MX29LV400B	0xBA	/* Same as MX29LV400CB */
+#define MACRONIX_MX29LV400T	0xB9	/* Same as MX29LV400CT */
+#define MACRONIX_MX29LV640DB	0xCB	/* Same as MX29LV640EB */
+#define MACRONIX_MX29LV640DT	0xC9	/* Same as MX29LV640ET */
+#define MACRONIX_MX29LV800B	0x5B	/* Same as MX29LV800CB */
+#define MACRONIX_MX29LV800T	0xDA	/* Same as MX29LV800CT */
+#define MACRONIX_MX29SL402CB	0xF1
+#define MACRONIX_MX29SL402CT	0x70
+#define MACRONIX_MX29SL800CB	0x6B	/* Same as MX29SL802CB */
+#define MACRONIX_MX29SL800CT	0xEA	/* Same as MX29SL802CT */
+
+/*
+ * Programmable Micro Corp is listed in JEP106W in bank 2, so it should
+ * have a 0x7F continuation code prefix.
+ */
+#define PMC_ID			0x7F9D	/* PMC */
+#define PMC_ID_NOPREFIX		0x9D	/* PMC, missing 0x7F prefix */
+#define PMC_PM25LV512		0x7B
+#define PMC_PM25LV010		0x7C
+#define PMC_PM25LV020		0x7D
+#define PMC_PM25LV040		0x7E
+#define PMC_PM25LV080B		0x13
+#define PMC_PM25LV016B		0x14
+#define PMC_PM29F002T		0x1D
+#define PMC_PM29F002B		0x2D
+#define PMC_PM39LV512		0x1B
+#define PMC_PM39F010		0x1C	/* Same as Pm39LV010 */
+#define PMC_PM39LV020		0x3D
+#define PMC_PM39LV040		0x3E
+#define PMC_PM39F020		0x4D
+#define PMC_PM39F040		0x4E
+#define PMC_PM49FL002		0x6D
+#define PMC_PM49FL004		0x6E
+
+/* 
+ * The Sanyo chip found so far uses SPI, first byte is manufacture code,
+ * second byte is the device code,
+ * third byte is a dummy byte.
+ */
+#define SANYO_ID		0x62
+#define SANYO_LE25FW203A	0x1600
+
+#define SHARP_ID		0xB0	/* Sharp */
+#define SHARP_LH28F008BJxxPT	0xEC
+#define SHARP_LH28F008BJxxPB	0xED
+#define SHARP_LH28F800BVxxBTL	0x4B
+#define SHARP_LH28F800BVxxBV	0x4D
+#define SHARP_LH28F800BVxxTV	0x4C
+#define SHARP_LHF00L02		0xC9	/* Same as LHF00L06/LHF00L07 */
+#define SHARP_LHF00L04		0xCF	/* Same as LHF00L03/LHF00L05 */
+
+/*
+ * Spansion was previously a joint venture of AMD and Fujitsu.
+ * S25 chips are SPI. The first device ID byte is memory type and
+ * the second device ID byte is memory capacity.
+ */
+#define SPANSION_ID		0x01	/* Spansion, same ID as AMD */
+#define SPANSION_S25FL004A	0x0212
+#define SPANSION_S25FL008A	0x0213
+#define SPANSION_S25FL016A	0x0214
+#define SPANSION_S25FL032A	0x0215
+#define SPANSION_S25FL064A	0x0216
+
+/*
+ * SST25 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 SST_ID			0xBF	/* SST */
+#define SST_SST25WF512		0x2501
+#define SST_SST25WF010		0x2502
+#define SST_SST25WF020		0x2503
+#define SST_SST25WF040		0x2504
+#define SST_SST25VF512A_REMS	0x48	/* REMS or RES opcode */
+#define SST_SST25VF010_REMS	0x49	/* REMS or RES opcode */
+#define SST_SST25VF020_REMS	0x43	/* REMS or RES opcode */
+#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_SST25VF080B		0x258E
+#define SST_SST25VF080B_REMS	0x8E	/* REMS or RES opcode */
+#define SST_SST25VF016B		0x2541
+#define SST_SST25VF032B		0x254A
+#define SST_SST25VF032B_REMS	0x4A	/* REMS or RES opcode */
+#define SST_SST25VF064C		0x254B
+#define SST_SST26VF016		0x2601
+#define SST_SST26VF032		0x2602
+#define SST_SST27SF512		0xA4
+#define SST_SST27SF010		0xA5
+#define SST_SST27SF020		0xA6
+#define SST_SST27VF010		0xA9
+#define SST_SST27VF020		0xAA
+#define SST_SST28SF040		0x04
+#define SST_SST29EE512		0x5D
+#define SST_SST29EE010		0x07
+#define SST_SST29LE010		0x08	/* Same as SST29VE010 */
+#define SST_SST29EE020A		0x10	/* Same as SST29EE020 */
+#define SST_SST29LE020		0x12	/* Same as SST29VE020 */
+#define SST_SST29SF020		0x24
+#define SST_SST29VF020		0x25
+#define SST_SST29SF040		0x13
+#define SST_SST29VF040		0x14
+#define SST_SST39SF512		0xB4
+#define SST_SST39SF010		0xB5
+#define SST_SST39SF020		0xB6	/* Same as 39SF020A */
+#define SST_SST39SF040		0xB7
+#define SST_SST39VF512		0xD4
+#define SST_SST39VF010		0xD5
+#define SST_SST39VF020		0xD6	/* Same as 39LF020 */
+#define SST_SST39VF040		0xD7	/* Same as 39LF040 */
+#define SST_SST39VF080		0xD8	/* Same as 39LF080/39VF080/39VF088 */
+#define SST_SST49LF040B		0x50
+#define SST_SST49LF040		0x51
+#define SST_SST49LF020		0x61
+#define SST_SST49LF020A		0x52
+#define SST_SST49LF030A		0x1C
+#define SST_SST49LF080A		0x5B
+#define SST_SST49LF002A		0x57
+#define SST_SST49LF003A		0x1B
+#define SST_SST49LF004A		0x60	/* Same as 49LF004B */
+#define SST_SST49LF008A		0x5A
+#define SST_SST49LF004C		0x54
+#define SST_SST49LF008C		0x59
+#define SST_SST49LF016C		0x5C
+#define SST_SST49LF160C		0x4C
+
+/*
+ * 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_M25P05A		0x2010
+#define ST_M25P05_RES		0x10	/* Same code as M25P10. */
+#define ST_M25P10A		0x2011
+#define ST_M25P10_RES		0x10	/* Same code as M25P05. */
+#define ST_M25P20		0x2012
+#define ST_M25P40		0x2013
+#define ST_M25P40_RES		0x12
+#define ST_M25P80		0x2014
+#define ST_M25P16		0x2015
+#define ST_M25P32		0x2016
+#define ST_M25P64		0x2017
+#define ST_M25P128		0x2018
+#define ST_M25PX16		0x7115
+#define ST_M25PX32		0x7116
+#define ST_M25PX64		0x7117
+#define ST_M25PE10		0x8011
+#define ST_M25PE20		0x8012
+#define ST_M25PE40		0x8013
+#define ST_M25PE80		0x8014
+#define ST_M25PE16		0x8015
+#define ST_M50FLW040A		0x08
+#define ST_M50FLW040B		0x28
+#define ST_M50FLW080A		0x80
+#define ST_M50FLW080B		0x81
+#define ST_M50FW002		0x29
+#define ST_M50FW040		0x2C
+#define ST_M50FW080		0x2D
+#define ST_M50FW016		0x2E
+#define ST_M50LPW116		0x30
+#define ST_M29F002B		0x34	/* Same as M29F002BB */
+#define ST_M29F002T		0xB0	/* Same as M29F002BT/M29F002NT/M29F002BNT */
+#define ST_M29F040B		0xE2	/* Same as M29F040 */
+#define ST_M29F080		0xF1
+#define ST_M29F200BT		0xD3
+#define ST_M29F200BB		0xD4
+#define ST_M29F400BT		0xD5	/* Same as M29F400T */
+#define ST_M29F400BB		0xD6	/* Same as M29F400B */
+#define ST_M29F800DB		0x58
+#define ST_M29F800DT		0xEC
+#define ST_M29W010B		0x23
+#define ST_M29W040B		0xE3
+#define ST_M29W512B		0x27
+
+#define SYNCMOS_MVC_ID		0x40	/* SyncMOS (SM) and Mosel Vitelic Corporation (MVC) */
+#define MVC_V29C51000T		0x00
+#define MVC_V29C51400T		0x13
+#define MVC_V29LC51000		0x20
+#define MVC_V29LC51001		0x60
+#define MVC_V29LC51002		0x82
+#define MVC_V29C51000B		0xA0
+#define MVC_V29C51400B		0xB3
+#define SM_MVC_29C51001T	0x01	/* Identical chips: {F,S,V}29C51001T */
+#define SM_MVC_29C51002T	0x02	/* Identical chips: {F,S,V}29C51002T */
+#define SM_MVC_29C51004T	0x03	/* Identical chips: {F,S,V}29C51004T */
+#define SM_MVC_29C31004T	0x63	/* Identical chips: {S,V}29C31004T */
+#define SM_MVC_29C31004B	0x73	/* Identical chips: {S,V}29C31004B */
+#define SM_MVC_29C51001B	0xA1	/* Identical chips: {F,S,V}29C51001B */
+#define SM_MVC_29C51002B	0xA2	/* Identical chips: {F,S,V}29C51002B */
+#define SM_MVC_29C51004B	0xA3	/* Identical chips: {F,S,V}29C51004B */
+
+#define TI_ID			0x97	/* Texas Instruments */
+#define TI_OLD_ID		0x01	/* TI chips from last century */
+#define TI_TMS29F002RT		0xB0
+#define TI_TMS29F002RB		0x34
+
+/*
+ * W25X chips are SPI, first byte of device ID is memory type, second
+ * byte of device ID is related to log(bitsize).
+ */
+#define WINBOND_NEX_ID		0xEF	/* Winbond (ex Nexcom) serial flashes */
+#define WINBOND_NEX_W25X10	0x3011
+#define WINBOND_NEX_W25X20	0x3012
+#define WINBOND_NEX_W25X40	0x3013
+#define WINBOND_NEX_W25X80	0x3014
+#define WINBOND_NEX_W25X16	0x3015
+#define WINBOND_NEX_W25X32	0x3016
+#define WINBOND_NEX_W25X64	0x3017
+#define WINBOND_NEX_W25Q40	0x4013
+#define WINBOND_NEX_W25Q80	0x4014
+#define WINBOND_NEX_W25Q16	0x4015
+#define WINBOND_NEX_W25Q32	0x4016
+#define WINBOND_NEX_W25Q64	0x4017
+#define WINBOND_NEX_W25Q128	0x4018
+
+#define WINBOND_ID		0xDA	/* Winbond */
+#define WINBOND_W19B160BB	0x49
+#define WINBOND_W19B160BT	0xC4
+#define WINBOND_W19B320SB	0x2A    /* Same as W19L320SB */
+#define WINBOND_W19B320ST	0xBA    /* Same as W19L320ST */
+#define WINBOND_W19B322MB	0x92
+#define WINBOND_W19B322MT	0x10
+#define WINBOND_W19B323MB	0x94
+#define WINBOND_W19B323MT	0x13
+#define WINBOND_W19B324MB	0x97
+#define WINBOND_W19B324MT	0x16
+#define WINBOND_W29C010		0xC1    /* Same as W29C010M, W29C011A, W29EE011, W29EE012, and ASD AE29F1008 */
+#define WINBOND_W29C020		0x45    /* Same as W29C020C, W29C022 and ASD AE29F2008 */
+#define WINBOND_W29C040		0x46    /* Same as W29C040P */
+#define WINBOND_W29C512A	0xC8    /* Same as W29EE512 */
+#define WINBOND_W39L010		0x31
+#define WINBOND_W39L020		0xB5
+#define WINBOND_W39L040		0xB6
+#define WINBOND_W39L040A	0xD6
+#define WINBOND_W39L512		0x38
+#define WINBOND_W39V040A	0x3D
+#define WINBOND_W39V040FA	0x34
+#define WINBOND_W39V040B	0x54    /* Same as W39V040FB */
+#define WINBOND_W39V040C	0x50    /* Same as W39V040FC */
+#define WINBOND_W39V080A	0xD0
+#define WINBOND_W39V080FA	0xD3
+#define WINBOND_W39V080FA_DM	0x93    /* W39V080FA dual mode */
+#define WINBOND_W49F002		0x25    /* Same as W49F002B */
+#define WINBOND_W49F002U	0x0B    /* Same as W49F002N and ASD AE49F2008 */
+#define WINBOND_W49F020		0x8C
+#define WINBOND_W49V002A	0xB0
+#define WINBOND_W49V002FA	0x32
+
+#endif /* !FLASHCHIPS_H */
diff --git a/src/mainboard/msi/ms6119/f/flashrom.c b/src/mainboard/msi/ms6119/f/flashrom.c
new file mode 100644
index 0000000..617e537
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/flashrom.c
@@ -0,0 +1,1832 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2004 Tyan Corp <yhlu@tyan.com>
+ * Copyright (C) 2005-2008 coresystems GmbH
+ * Copyright (C) 2008,2009 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+#include "f.h"
+#define HAVE_UTSNAME	0
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <string.h>
+#include <stdlib.h>
+//#include <ctype.h>
+//#include <getopt.h>
+#if HAVE_UTSNAME == 1
+#include <sys/utsname.h>
+#endif
+#include "flash.h"
+#include "flashchips.h"
+#include "programmer.h"
+
+
+
+void internal_delay(int usecs);
+void internal_delay(int usecs)
+{
+	udelay(usecs);
+}
+
+//const char flashrom_version[] = FLASHROM_VERSION;
+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
+
+static char *programmer_param = NULL;
+
+/* Supported buses for the current programmer. */
+enum chipbustype buses_supported;
+
+/*
+ * Programmers supporting multiple buses can have differing size limits on
+ * each bus. Store the limits for each bus in a common struct.
+ */
+struct decode_sizes max_rom_decode;
+
+/* If nonzero, used as the start address of bottom-aligned flash. */
+unsigned long flashbase;
+
+/* Is writing allowed with this programmer? */
+int programmer_may_write;
+
+const struct programmer_entry programmer_table[] = {
+#if CONFIG_INTERNAL == 1
+	{
+		.name			= "internal",
+		.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
+
+#if CONFIG_DUMMY == 1
+	{
+		.name			= "dummy",
+		.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
+
+#if CONFIG_NIC3COM == 1
+	{
+		.name			= "nic3com",
+		.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
+
+#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,
+	},
+#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,
+	},
+#endif
+
+#if CONFIG_GFXNVIDIA == 1
+	{
+		.name			= "gfxnvidia",
+		.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
+
+#if CONFIG_DRKAISER == 1
+	{
+		.name			= "drkaiser",
+		.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
+
+#if CONFIG_SATASII == 1
+	{
+		.name			= "satasii",
+		.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
+
+#if CONFIG_ATAHPT == 1
+	{
+		.name			= "atahpt",
+		.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
+
+#if CONFIG_FT2232_SPI == 1
+	{
+		.name			= "ft2232_spi",
+		.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
+
+#if CONFIG_SERPROG == 1
+	{
+		.name			= "serprog",
+		.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
+
+#if CONFIG_BUSPIRATE_SPI == 1
+	{
+		.name			= "buspirate_spi",
+		.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
+
+#if CONFIG_DEDIPROG == 1
+	{
+		.name			= "dediprog",
+		.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
+
+#if CONFIG_RAYER_SPI == 1
+	{
+		.name			= "rayer_spi",
+		.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
+
+#if CONFIG_NICINTEL == 1
+	{
+		.name			= "nicintel",
+		.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,
+	},
+#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,
+	},
+#endif
+
+#if CONFIG_SATAMV == 1
+	{
+		.name			= "satamv",
+		.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
+
+	{}, /* This entry corresponds to PROGRAMMER_INVALID. */
+};
+
+#define SHUTDOWN_MAXFN 32
+static int shutdown_fn_count = 0;
+struct shutdown_func_data {
+	int (*func) (void *data);
+	void *data;
+} static shutdown_fn[SHUTDOWN_MAXFN];
+/* Initialize to 0 to make sure nobody registers a shutdown function before
+ * programmer init.
+ */
+static int may_register_shutdown = 0;
+
+static int check_block_eraser(const struct flashchip *flash, int k, int log);
+
+/* Register a function to be executed on programmer shutdown.
+ * The advantage over atexit() is that you can supply a void pointer which will
+ * be used as parameter to the registered function upon programmer shutdown.
+ * This pointer can point to arbitrary data used by said function, e.g. undo
+ * information for GPIO settings etc. If unneeded, set data=NULL.
+ * Please note that the first (void *data) belongs to the function signature of
+ * the function passed as first parameter.
+ */
+int register_shutdown(int (*function) (void *data), void *data)
+{
+	if (shutdown_fn_count >= SHUTDOWN_MAXFN) {
+		msg_perr("Tried to register more than %i shutdown functions.\n",
+			 SHUTDOWN_MAXFN);
+		return 1;
+	}
+	if (!may_register_shutdown) {
+		msg_perr("Tried to register a shutdown function before "
+			 "programmer init.\n");
+		return 1;
+	}
+	shutdown_fn[shutdown_fn_count].func = function;
+	shutdown_fn[shutdown_fn_count].data = data;
+	shutdown_fn_count++;
+
+	return 0;
+}
+
+int programmer_init(char *param)
+{
+	int ret;
+	/* Initialize all programmer specific data. */
+	/* Default to unlimited decode sizes. */
+	max_rom_decode = (const struct decode_sizes) {
+		.parallel	= 0xffffffff,
+		.lpc		= 0xffffffff,
+		.fwh		= 0xffffffff,
+		.spi		= 0xffffffff
+	};
+	buses_supported = CHIP_BUSTYPE_NONE;
+	/* Default to top aligned flash at 4 GB. */
+	flashbase = 0;
+	/* Registering shutdown functions is now allowed. */
+	may_register_shutdown = 1;
+	/* Default to allowing writes. Broken programmers set this to 0. */
+	programmer_may_write = 1;
+
+	programmer_param = param;
+	msg_pdbg("Initializing %s programmer\n",
+		 programmer_table[programmer].name);
+	ret = programmer_table[programmer].init();
+	if (programmer_param && strlen(programmer_param)) {
+		msg_perr("Unhandled programmer parameters: %s\n",
+			 programmer_param);
+		/* Do not error out here, the init itself was successful. */
+	}
+	return ret;
+}
+
+int programmer_shutdown(void)
+{
+	int ret = 0;
+
+	/* Registering shutdown functions is no longer allowed. */
+	may_register_shutdown = 0;
+	while (shutdown_fn_count > 0) {
+		int i = --shutdown_fn_count;
+		ret |= shutdown_fn[i].func(shutdown_fn[i].data);
+	}
+	return ret;
+}
+
+void *programmer_map_flash_region(const char *descr, unsigned long phys_addr,
+				  size_t len)
+{
+	return programmer_table[programmer].map_flash_region(descr,
+							     phys_addr, len);
+}
+
+void programmer_unmap_flash_region(void *virt_addr, size_t len)
+{
+	programmer_table[programmer].unmap_flash_region(virt_addr, len);
+}
+
+void chip_writeb(uint8_t val, chipaddr addr)
+{
+	programmer_table[programmer].chip_writeb(val, addr);
+}
+
+void chip_writew(uint16_t val, chipaddr addr)
+{
+	programmer_table[programmer].chip_writew(val, addr);
+}
+
+void chip_writel(uint32_t val, chipaddr addr)
+{
+	programmer_table[programmer].chip_writel(val, addr);
+}
+
+void chip_writen(uint8_t *buf, chipaddr addr, size_t len)
+{
+	programmer_table[programmer].chip_writen(buf, addr, len);
+}
+
+uint8_t chip_readb(const chipaddr addr)
+{
+	return programmer_table[programmer].chip_readb(addr);
+}
+
+uint16_t chip_readw(const chipaddr addr)
+{
+	return programmer_table[programmer].chip_readw(addr);
+}
+
+uint32_t chip_readl(const chipaddr addr)
+{
+	return programmer_table[programmer].chip_readl(addr);
+}
+
+void chip_readn(uint8_t *buf, chipaddr addr, size_t len)
+{
+	programmer_table[programmer].chip_readn(buf, addr, len);
+}
+
+void programmer_delay(int usecs)
+{
+	programmer_table[programmer].delay(usecs);
+}
+
+void map_flash_registers(struct flashchip *flash)
+{
+	size_t size = flash->total_size * 1024;
+	/* Flash registers live 4 MByte below the flash. */
+	/* FIXME: This is incorrect for nonstandard flashbase. */
+	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)
+{
+	chip_readn(buf, flash->virtual_memory + start, len);
+		
+	return 0;
+}
+
+int min(int a, int b)
+{
+	return (a < b) ? a : b;
+}
+
+int max(int a, int b)
+{
+	return (a > b) ? a : b;
+}
+
+int bitcount(unsigned long a);
+
+int bitcount(unsigned long a)
+{
+	int i = 0;
+	for (; a != 0; a >>= 1)
+		if (a & 1)
+			i++;
+	return i;
+}
+
+void tolower_string(char *str)
+{
+	for (; *str != '\0'; str++)
+		*str = (char)tolower((unsigned char)*str);
+}
+
+
+
+/* This is a somewhat hacked function similar in some ways to strtok().
+ * It will look for needle with a subsequent '=' in haystack, return a copy of
+ * needle and remove everything from the first occurrence of needle to the next
+ * delimiter from haystack.
+ */
+
+
+/* Returns the number of well-defined erasers for a chip. */
+static unsigned int count_usable_erasers(const struct flashchip *flash)
+{
+	unsigned int usable_erasefunctions = 0;
+	int k;
+	for (k = 0; k < NUM_ERASEFUNCTIONS; k++) {
+		if (!check_block_eraser(flash, k, 0))
+			usable_erasefunctions++;
+	}
+	return usable_erasefunctions;
+}
+
+/* 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, int start, int len)
+{
+/*	int ret;
+	uint8_t *cmpbuf = malloc(len);
+
+	if (!cmpbuf) {
+		msg_gerr("Could not allocate memory!\n");
+		return -1;
+	}
+	memset(cmpbuf, 0xff, len);
+	ret = verify_range(flash, cmpbuf, start, len, "ERASE");
+	free(cmpbuf);*/
+	return 0;//ret;
+}
+
+/*
+ * @cmpbuf	buffer to compare against, cmpbuf[0] is expected to match the
+		flash content at location start
+ * @start	offset to the base address of the flash chip
+ * @len		length of the verified area
+ * @message	string to print in the "FAILED" message
+ * @return	0 for success, -1 for failure
+ */
+
+
+/*
+ * Check if the buffer @have can be programmed to the content of @want without
+ * erasing. This is only possible if all chunks of size @gran are either kept
+ * as-is or changed from an all-ones state to any other state.
+ *
+ * The following write granularities (enum @gran) are known:
+ * - 1 bit. Each bit can be cleared individually.
+ * - 1 byte. A byte can be written once. Further writes to an already written
+ *   byte cause the contents to be either undefined or to stay unchanged.
+ * - 128 bytes. If less than 128 bytes are written, the rest will be
+ *   erased. Each write to a 128-byte region will trigger an automatic erase
+ *   before anything is written. Very uncommon behaviour and unsupported by
+ *   this function.
+ * - 256 bytes. If less than 256 bytes are written, the contents of the
+ *   unwritten bytes are undefined.
+ * Warning: This function assumes that @have and @want point to naturally
+ * aligned regions.
+ *
+ * @have        buffer with current content
+ * @want        buffer with desired content
+ * @len		length of the checked area
+ * @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 result = 0;
+	int i, j, limit;
+
+	switch (gran) {
+	case write_gran_1bit:
+		for (i = 0; i < len; i++)
+			if ((have[i] & want[i]) != want[i]) {
+				result = 1;
+				break;
+			}
+		break;
+	case write_gran_1byte:
+		for (i = 0; i < len; i++)
+			if ((have[i] != want[i]) && (have[i] != 0xff)) {
+				result = 1;
+				break;
+			}
+		break;
+	case write_gran_256bytes:
+		for (j = 0; j < len / 256; j++) {
+			limit = min (256, len - j * 256);
+			/* Are 'have' and 'want' identical? */
+			if (!memcmp(have + j * 256, want + j * 256, limit))
+				continue;
+			/* have needs to be in erased state. */
+			for (i = 0; i < limit; i++)
+				if (have[j * 256 + i] != 0xff) {
+					result = 1;
+					break;
+				}
+			if (result)
+				break;
+		}
+		break;
+	default:
+		msg_cerr("%s: Unsupported granularity! Please report a bug at "
+			 "flashrom@flashrom.org\n", __func__);
+	}
+	return result;
+}
+
+/**
+ * Check if the buffer @have needs to be programmed to get the content of @want.
+ * If yes, return 1 and fill in first_start with the start address of the
+ * write operation and first_len with the length of the first to-be-written
+ * chunk. If not, return 0 and leave first_start and first_len undefined.
+ *
+ * Warning: This function assumes that @have and @want point to naturally
+ * aligned regions.
+ *
+ * @have	buffer with current content
+ * @want	buffer with desired content
+ * @len		length of the checked area
+ * @gran	write granularity (enum, not count)
+ * @first_start	offset of the first byte which needs to be written (passed in
+ *		value is increased by the offset of the first needed write
+ *		relative to have/want or unchanged if no write is needed)
+ * @return	length of the first contiguous area which needs to be written
+ *		0 if no write is needed
+ *
+ * FIXME: This function needs a parameter which tells it about coalescing
+ * 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)
+{
+	int need_write = 0, rel_start = 0, first_len = 0;
+	int i, limit, stride;
+
+	switch (gran) {
+	case write_gran_1bit:
+	case write_gran_1byte:
+		stride = 1;
+		break;
+	case write_gran_256bytes:
+		stride = 256;
+		break;
+	default:
+		msg_cerr("%s: Unsupported granularity! Please report a bug at "
+			 "flashrom@flashrom.org\n", __func__);
+		/* Claim that no write was needed. A write with unknown
+		 * granularity is too dangerous to try.
+		 */
+		return 0;
+	}
+	for (i = 0; i < len / stride; i++) {
+		limit = min(stride, len - i * stride);
+		/* Are 'have' and 'want' identical? */
+		if (memcmp(have + i * stride, want + i * stride, limit)) {
+			if (!need_write) {
+				/* First location where have and want differ. */
+				need_write = 1;
+				rel_start = i * stride;
+			}
+		} else {
+			if (need_write) {
+				/* First location where have and want
+				 * do not differ anymore.
+				 */
+				break;
+			}
+		}
+	}
+	if (need_write)
+		first_len = min(i * stride - rel_start, len);
+	*first_start += rel_start;
+	return first_len;
+}
+
+/* This function generates various test patterns useful for testing controller
+ * and chip communication as well as chip behaviour.
+ *
+ * If a byte can be written multiple times, each time keeping 0-bits at 0
+ * and changing 1-bits to 0 if the new value for that bit is 0, the effect
+ * is essentially an AND operation. That's also the reason why this function
+ * provides the result of AND between various patterns.
+ *
+ * Below is a list of patterns (and their block length).
+ * Pattern 0 is 05 15 25 35 45 55 65 75 85 95 a5 b5 c5 d5 e5 f5 (16 Bytes)
+ * Pattern 1 is 0a 1a 2a 3a 4a 5a 6a 7a 8a 9a aa ba ca da ea fa (16 Bytes)
+ * Pattern 2 is 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f (16 Bytes)
+ * Pattern 3 is a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af (16 Bytes)
+ * Pattern 4 is 00 10 20 30 40 50 60 70 80 90 a0 b0 c0 d0 e0 f0 (16 Bytes)
+ * Pattern 5 is 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f (16 Bytes)
+ * Pattern 6 is 00 (1 Byte)
+ * Pattern 7 is ff (1 Byte)
+ * Patterns 0-7 have a big-endian block number in the last 2 bytes of each 256
+ * byte block.
+ *
+ * Pattern 8 is 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11... (256 B)
+ * Pattern 9 is ff fe fd fc fb fa f9 f8 f7 f6 f5 f4 f3 f2 f1 f0 ef ee... (256 B)
+ * Pattern 10 is 00 00 00 01 00 02 00 03 00 04... (128 kB big-endian counter)
+ * Pattern 11 is ff ff ff fe ff fd ff fc ff fb... (128 kB big-endian downwards)
+ * Pattern 12 is 00 (1 Byte)
+ * Pattern 13 is ff (1 Byte)
+ * Patterns 8-13 have no block number.
+ *
+ * Patterns 0-3 are created to detect and efficiently diagnose communication
+ * slips like missed bits or bytes and their repetitive nature gives good visual
+ * cues to the person inspecting the results. In addition, the following holds:
+ * AND Pattern 0/1 == Pattern 4
+ * AND Pattern 2/3 == Pattern 5
+ * AND Pattern 0/1/2/3 == AND Pattern 4/5 == Pattern 6
+ * A weakness of pattern 0-5 is the inability to detect swaps/copies between
+ * any two 16-byte blocks except for the last 16-byte block in a 256-byte bloc.
+ * They work perfectly for detecting any swaps/aliasing of blocks >= 256 bytes.
+ * 0x5 and 0xa were picked because they are 0101 and 1010 binary.
+ * Patterns 8-9 are best for detecting swaps/aliasing of blocks < 256 bytes.
+ * Besides that, they provide for bit testing of the last two bytes of every
+ * 256 byte block which contains the block number for patterns 0-6.
+ * Patterns 10-11 are special purpose for detecting subblock aliasing with
+ * block sizes >256 bytes (some Dataflash chips etc.)
+ * AND Pattern 8/9 == Pattern 12
+ * AND Pattern 10/11 == Pattern 12
+ * Pattern 13 is the completely erased state.
+ * None of the patterns can detect aliasing at boundaries which are a multiple
+ * of 16 MBytes (but such chips do not exist anyway for Parallel/LPC/FWH/SPI).
+ */
+int generate_testpattern(uint8_t *buf, uint32_t size, int variant);
+
+int generate_testpattern(uint8_t *buf, uint32_t size, int variant)
+{
+	int i;
+
+	if (!buf) {
+		msg_gerr("Invalid buffer!\n");
+		return 1;
+	}
+
+	switch (variant) {
+	case 0:
+		for (i = 0; i < size; i++)
+			buf[i] = (i & 0xf) << 4 | 0x5;
+		break;
+	case 1:
+		for (i = 0; i < size; i++)
+			buf[i] = (i & 0xf) << 4 | 0xa;
+		break;
+	case 2:
+		for (i = 0; i < size; i++)
+			buf[i] = 0x50 | (i & 0xf);
+		break;
+	case 3:
+		for (i = 0; i < size; i++)
+			buf[i] = 0xa0 | (i & 0xf);
+		break;
+	case 4:
+		for (i = 0; i < size; i++)
+			buf[i] = (i & 0xf) << 4;
+		break;
+	case 5:
+		for (i = 0; i < size; i++)
+			buf[i] = i & 0xf;
+		break;
+	case 6:
+		memset(buf, 0x00, size);
+		break;
+	case 7:
+		memset(buf, 0xff, size);
+		break;
+	case 8:
+		for (i = 0; i < size; i++)
+			buf[i] = i & 0xff;
+		break;
+	case 9:
+		for (i = 0; i < size; i++)
+			buf[i] = ~(i & 0xff);
+		break;
+	case 10:
+		for (i = 0; i < size % 2; i++) {
+			buf[i * 2] = (i >> 8) & 0xff;
+			buf[i * 2 + 1] = i & 0xff;
+		}
+		if (size & 0x1)
+			buf[i * 2] = (i >> 8) & 0xff;
+		break;
+	case 11:
+		for (i = 0; i < size % 2; i++) {
+			buf[i * 2] = ~((i >> 8) & 0xff);
+			buf[i * 2 + 1] = ~(i & 0xff);
+		}
+		if (size & 0x1)
+			buf[i * 2] = ~((i >> 8) & 0xff);
+		break;
+	case 12:
+		memset(buf, 0x00, size);
+		break;
+	case 13:
+		memset(buf, 0xff, size);
+		break;
+	}
+
+	if ((variant >= 0) && (variant <= 7)) {
+		/* Write block number in the last two bytes of each 256-byte
+		 * block, big endian for easier reading of the hexdump.
+		 * Note that this wraps around for chips larger than 2^24 bytes
+		 * (16 MB).
+		 */
+		for (i = 0; i < size / 256; i++) {
+			buf[i * 256 + 254] = (i >> 8) & 0xff;
+			buf[i * 256 + 255] = i & 0xff;
+		}
+	}
+
+	return 0;
+}
+
+int check_max_decode(enum chipbustype buses, uint32_t size)
+{
+	int limitexceeded = 0;
+	if ((buses & CHIP_BUSTYPE_PARALLEL) &&
+	    (max_rom_decode.parallel < size)) {
+		limitexceeded++;
+		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");
+	}
+	if ((buses & CHIP_BUSTYPE_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");
+	}
+	if ((buses & CHIP_BUSTYPE_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");
+	}
+	if ((buses & CHIP_BUSTYPE_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");
+	}
+	if (!limitexceeded)
+		return 0;
+	/* Sometimes chip and programmer have more than one bus in common,
+	 * and the limit is not exceeded on all buses. Tell the user.
+	 */
+	if (bitcount(buses) > limitexceeded)
+		/* FIXME: This message is designed towards CLI users. */
+		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");
+	return 1;
+}
+
+int probe_flash(int startchip, struct flashchip *fill_flash, int force)
+{
+	const struct flashchip *flash;
+	unsigned long base = 0;
+	char location[64];
+	uint32_t size;
+	enum chipbustype buses_common;
+//	char *tmp;
+
+	for (flash = flashchips + startchip; flash && flash->name; flash++) {
+		if (chip_to_probe && strcmp(flash->name, chip_to_probe) != 0)
+			continue;
+		buses_common = buses_supported & flash->bustype;
+		if (!buses_common) {
+			msg_gspew("Probing for %s %s, %d kB: skipped. ",
+			         flash->vendor, flash->name, flash->total_size);
+			msg_gspew("\n");
+			continue;
+		}
+		msg_gdbg("Probing for %s %s, %d kB: ",
+			     flash->vendor, flash->name, flash->total_size);
+		if (!flash->probe && !force) {
+			msg_gdbg("failed! flashrom has no probe function for "
+				 "this flash chip.\n");
+			continue;
+		}
+
+		size = flash->total_size * 1024;
+		check_max_decode(buses_common, size);
+
+		/* Start filling in the dynamic data. */
+		*fill_flash = *flash;
+
+		base = flashbase ? flashbase : (0xffffffff - size + 1);
+		fill_flash->virtual_memory = (chipaddr)programmer_map_flash_region("flash chip", base, size);
+
+		if (force)
+			break;
+
+		if (fill_flash->probe(fill_flash) != 1)
+			goto notfound;
+
+		/* If this is the first chip found, accept it.
+		 * If this is not the first chip found, accept it only if it is
+		 * a non-generic match.
+		 * We could either make chipcount global or provide it as
+		 * parameter, or we assume that startchip==0 means this call to
+		 * probe_flash() is the first one and thus no chip has been
+		 * found before.
+		 */
+		if (startchip == 0 || fill_flash->model_id != GENERIC_DEVICE_ID)
+			break;
+
+notfound:
+		programmer_unmap_flash_region((void *)fill_flash->virtual_memory, size);
+	}
+
+	if (!flash || !flash->name)
+		return -1;
+
+#if CONFIG_INTERNAL == 1
+	//if (programmer_table[programmer].map_flash_region == physmap)
+	//	snprintf(location, sizeof(location), "at physical address 0x%lx", base);
+	//else
+#endif
+		//snprintf(location, sizeof(location), "on %s", programmer_table[programmer].name);
+
+	//tmp = flashbuses_to_text(flash->bustype);
+	msg_cinfo("%s chip \"%s %s\" (%d kB, bustype :) %s.\n",
+		  force ? "Assuming" : "Found", fill_flash->vendor,
+		  fill_flash->name, fill_flash->total_size, location);
+	//free(tmp);
+
+	/* Flash registers will not be mapped if the chip was forced. Lock info
+	 * may be stored in registers, so avoid lock info printing.
+	 */
+	if (!force)
+		if (fill_flash->printlock)
+			fill_flash->printlock(fill_flash);
+
+	/* Return position of matching chip. */
+	return flash - flashchips;
+}
+
+int verify_flash(struct flashchip *flash, uint8_t *buf);
+int verify_flash(struct flashchip *flash, uint8_t *buf)
+{
+	int ret;
+	int total_size = flash->total_size * 1024;
+
+	msg_cinfo("Verifying flash... ");
+
+	ret = 0;//verify_range(flash, buf, 0, total_size, NULL);
+
+	if (!ret)
+		msg_cinfo("VERIFIED.          \n");
+
+	return ret;
+}
+
+int read_buf_from_file(unsigned char *buf, unsigned long size, const char *filename)
+{
+#ifndef __LIBPAYLOAD__
+
+	unsigned long numbytes;
+	FILE *image;
+	struct stat image_stat;
+
+	if ((image = fopen(filename, "rb")) == NULL) {
+		perror(filename);
+		return 1;
+	}
+	if (fstat(fileno(image), &image_stat) != 0) {
+		perror(filename);
+		fclose(image);
+		return 1;
+	}
+	if (image_stat.st_size != size) {
+		msg_gerr("Error: Image size doesn't match\n");
+		fclose(image);
+		return 1;
+	}
+	numbytes = fread(buf, 1, size, image);
+	if (fclose(image)) {
+		perror(filename);
+		return 1;
+	}
+	if (numbytes != size) {
+		msg_gerr("Error: Failed to read complete file. Got %ld bytes, "
+			 "wanted %ld!\n", numbytes, size);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+int write_buf_to_file(unsigned char *buf, unsigned long size, const char *filename)
+{
+#ifndef __LIBPAYLOAD__
+
+	unsigned long numbytes;
+	FILE *image;
+
+	if (!filename) {
+		msg_gerr("No filename specified.\n");
+		return 1;
+	}
+	if ((image = fopen(filename, "wb")) == NULL) {
+		perror(filename);
+		return 1;
+	}
+
+	numbytes = fwrite(buf, 1, size, image);
+	fclose(image);
+	if (numbytes != size) {
+		msg_gerr("File %s could not be written completely.\n",
+			 filename);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+int read_flash_to_file(struct flashchip *flash, const char *filename)
+{
+/*	unsigned long size = flash->total_size * 1024;
+	unsigned char *buf = calloc(size, sizeof(char));
+	int ret = 0;
+
+	msg_cinfo("Reading flash... ");
+	if (!buf) {
+		msg_gerr("Memory allocation failed!\n");
+		msg_cinfo("FAILED.\n");
+		return 1;
+	}
+	if (!flash->read) {
+		msg_cerr("No read function available for this flash chip.\n");
+		ret = 1;
+		goto out_free;
+	}
+	if (flash->read(flash, buf, 0, size)) {
+		msg_cerr("Read operation failed!\n");
+		ret = 1;
+		goto out_free;
+	}
+
+	ret = write_buf_to_file(buf, size, filename);
+out_free:
+	free(buf);
+	msg_cinfo("%s.\n", ret ? "FAILED" : "done");*/
+	return 0;
+}
+
+/* This function shares a lot of its structure with erase_and_write_flash() and
+ * walk_eraseregions().
+ * Even if an error is found, the function will keep going and check the rest.
+ */
+static int selfcheck_eraseblocks(const struct flashchip *flash)
+{
+	int i, j, k;
+	int ret = 0;
+
+	for (k = 0; k < NUM_ERASEFUNCTIONS; k++) {
+		unsigned int done = 0;
+		struct block_eraser eraser = flash->block_erasers[k];
+
+		for (i = 0; i < NUM_ERASEREGIONS; i++) {
+			/* Blocks with zero size are bugs in flashchips.c. */
+			if (eraser.eraseblocks[i].count &&
+			    !eraser.eraseblocks[i].size) {
+				msg_gerr("ERROR: Flash chip %s erase function "
+					"%i region %i has size 0. Please report"
+					" a bug at flashrom@flashrom.org\n",
+					flash->name, k, i);
+				ret = 1;
+			}
+			/* Blocks with zero count are bugs in flashchips.c. */
+			if (!eraser.eraseblocks[i].count &&
+			    eraser.eraseblocks[i].size) {
+				msg_gerr("ERROR: Flash chip %s erase function "
+					"%i region %i has count 0. Please report"
+					" a bug at flashrom@flashrom.org\n",
+					flash->name, k, i);
+				ret = 1;
+			}
+			done += eraser.eraseblocks[i].count *
+				eraser.eraseblocks[i].size;
+		}
+		/* Empty eraseblock definition with erase function.  */
+		if (!done && eraser.block_erase)
+			msg_gspew("Strange: Empty eraseblock definition with "
+				"non-empty erase function. Not an error.\n");
+		if (!done)
+			continue;
+		if (done != flash->total_size * 1024) {
+			msg_gerr("ERROR: Flash chip %s erase function %i "
+				"region walking resulted in 0x%06x bytes total,"
+				" expected 0x%06x bytes. Please report a bug at"
+				" flashrom@flashrom.org\n", flash->name, k,
+				done, flash->total_size * 1024);
+			ret = 1;
+		}
+		if (!eraser.block_erase)
+			continue;
+		/* Check if there are identical erase functions for different
+		 * layouts. That would imply "magic" erase functions. The
+		 * easiest way to check this is with function pointers.
+		 */
+		for (j = k + 1; j < NUM_ERASEFUNCTIONS; j++) {
+			if (eraser.block_erase ==
+			    flash->block_erasers[j].block_erase) {
+				msg_gerr("ERROR: Flash chip %s erase function "
+					"%i and %i are identical. Please report"
+					" a bug at flashrom@flashrom.org\n",
+					flash->name, k, j);
+				ret = 1;
+			}
+		}
+	}
+	return ret;
+}
+
+static int erase_and_write_block_helper(struct flashchip *flash,
+					unsigned int start, unsigned int len,
+					uint8_t *curcontents,
+					uint8_t *newcontents,
+					int (*erasefn) (struct flashchip *flash,
+							unsigned int addr,
+							unsigned int len))
+{
+	int starthere = 0;
+	int lenhere = 0;
+	int ret = 0;
+	int skip = 1;
+	int writecount = 0;
+	enum write_granularity gran = write_gran_256bytes; /* FIXME */
+
+	/* curcontents and newcontents are opaque to walk_eraseregions, and
+	 * need to be adjusted here to keep the impression of proper abstraction
+	 */
+	curcontents += start;
+	newcontents += start;
+	msg_cdbg(":");
+	/* FIXME: Assume 256 byte granularity for now to play it safe. */
+	if (need_erase(curcontents, newcontents, len, gran)) {
+		msg_cdbg("E");
+		ret = erasefn(flash, start, len);
+		if (ret)
+			return ret;
+		if (check_erased_range(flash, start, len)) {
+			msg_cerr("ERASE FAILED!\n");
+			return -1;
+		}
+		/* Erase was successful. Adjust curcontents. */
+		memset(curcontents, 0xff, len);
+		skip = 0;
+	}
+	/* get_next_write() sets starthere to a new value after the call. */
+	while ((lenhere = get_next_write(curcontents + starthere,
+					 newcontents + starthere,
+					 len - starthere, &starthere, gran))) {
+		if (!writecount++)
+			msg_cdbg("W");
+		/* Needs the partial write function signature. */
+		ret = flash->write(flash, newcontents + starthere,
+				   start + starthere, lenhere);
+		if (ret)
+			return ret;
+		starthere += lenhere;
+		skip = 0;
+	}
+	if (skip)
+		msg_cdbg("S");
+	return ret;
+}
+
+static int walk_eraseregions(struct flashchip *flash, int erasefunction,
+			     int (*do_something) (struct flashchip *flash,
+						  unsigned int addr,
+						  unsigned int len,
+						  uint8_t *param1,
+						  uint8_t *param2,
+						  int (*erasefn) (
+							struct flashchip *flash,
+							unsigned int addr,
+							unsigned int len)),
+			     void *param1, void *param2)
+{
+	int i, j;
+	unsigned int start = 0;
+	unsigned int len;
+	struct block_eraser eraser = flash->block_erasers[erasefunction];
+	for (i = 0; i < NUM_ERASEREGIONS; i++) {
+		/* count==0 for all automatically initialized array
+		 * members so the loop below won't be executed for them.
+		 */
+		len = eraser.eraseblocks[i].size;
+		for (j = 0; j < eraser.eraseblocks[i].count; j++) {
+			/* Print this for every block except the first one. */
+			if (i || j)
+				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;
+			}
+			start += len;
+		}
+	}
+	msg_cdbg("\n");
+	return 0;
+}
+
+static int check_block_eraser(const struct flashchip *flash, int k, int log)
+{
+	struct block_eraser eraser = flash->block_erasers[k];
+
+	if (!eraser.block_erase && !eraser.eraseblocks[0].count) {
+		if (log)
+			msg_cdbg("not defined. ");
+		return 1;
+	}
+	if (!eraser.block_erase && eraser.eraseblocks[0].count) {
+		if (log)
+			msg_cdbg("eraseblock layout is known, but matching "
+				 "block erase function is not implemented. ");
+		return 1;
+	}
+	if (eraser.block_erase && !eraser.eraseblocks[0].count) {
+		if (log)
+			msg_cdbg("block erase function found, but "
+				 "eraseblock layout is not defined. ");
+		return 1;
+	}
+	return 0;
+}
+
+int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents)
+{
+	int k, ret = 0;
+	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);
+	/* 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) {
+			msg_cdbg("Looking for another erase function.\n");
+			continue;
+		}
+		usable_erasefunctions--;
+		msg_cdbg("trying... ");
+		ret = walk_eraseregions(flash, k, &erase_and_write_block_helper, curcontents, newcontents);
+		msg_cdbg("\n");
+		/* 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.
+		 */
+		if (!usable_erasefunctions)
+			continue;
+		if (flash->read(flash, curcontents, 0, size)) {
+			/* Now we are truly screwed. Read failed as well. */
+			msg_cerr("Can't read anymore!\n");
+			/* We have no idea about the flash chip contents, so
+			 * retrying with another erase function is pointless.
+			 */
+			break;
+		}
+	}
+	/* Free the scratchpad. */
+//	free(curcontents);
+
+	if (ret) {
+		msg_cerr("FAILED!\n");
+	} else {
+		msg_cinfo("Done.\n");
+	}
+	return ret;
+}
+
+void nonfatal_help_message(void)
+{
+	msg_gerr("Writing to the flash chip apparently didn't do anything.\n"
+		"This means we have to add special support for your board, "
+		  "programmer or flash chip.\n"
+		"Please report this on IRC at irc.freenode.net (channel "
+		  "#flashrom) or\n"
+		"mail flashrom@flashrom.org!\n"
+		"-------------------------------------------------------------"
+		  "------------------\n"
+		"You may now reboot or simply leave the machine running.\n");
+}
+
+void emergency_help_message(void)
+{
+	msg_gerr("Your flash chip is in an unknown state.\n"
+		"Get help on IRC at irc.freenode.net (channel #flashrom) or\n"
+		"mail flashrom@flashrom.org with FAILED: your board name in "
+		  "the subject line!\n"
+		"-------------------------------------------------------------"
+		  "------------------\n"
+		"DO NOT REBOOT OR POWEROFF!\n");
+}
+
+/* The way to go if you want a delimited list of programmers*/
+void list_programmers(const char *delim);
+void list_programmers(const char *delim)
+{
+	enum programmer p;
+	for (p = 0; p < PROGRAMMER_INVALID; p++) {
+		msg_ginfo("%s", programmer_table[p].name);
+		if (p < PROGRAMMER_INVALID - 1)
+			msg_ginfo("%s", delim);
+	}
+	msg_ginfo("\n");	
+}
+
+void list_programmers_linebreak(int startcol, int cols, int paren)
+{
+	const char *pname;
+	int pnamelen;
+	int remaining = 0;
+	int firstline = 1;
+	enum programmer p;
+	int i;
+
+	for (p = 0; p < PROGRAMMER_INVALID; p++) {
+		pname = programmer_table[p].name;
+		pnamelen = strlen(pname);
+		if (remaining - pnamelen - 2 < 0) {
+			if (firstline)
+				firstline = 0;
+			else
+				printf("\n");
+			for (i = 0; i < startcol; i++)
+				printf(" ");
+			remaining = cols - startcol;
+		} else {
+			printf(" ");
+			remaining--;
+		}
+		if (paren && (p == 0)) {
+			printf("(");
+			remaining--;
+		}
+		printf("%s", pname);
+		remaining -= pnamelen;
+		if (p < PROGRAMMER_INVALID - 1) {
+			printf(",");
+			remaining--;
+		} else {
+			if (paren)
+				printf(")");
+			printf("\n");
+		}
+	}
+}
+
+void print_sysinfo(void)
+{
+#if HAVE_UTSNAME == 1
+	struct utsname osinfo;
+	uname(&osinfo);
+
+	msg_ginfo(" on %s %s (%s)", osinfo.sysname, osinfo.release,
+		  osinfo.machine);
+#else
+	msg_ginfo(" on unknown machine");
+#endif
+	msg_ginfo(", built with");
+#if NEED_PCI == 1
+#ifdef PCILIB_VERSION
+	msg_ginfo(" libpci %s,", PCILIB_VERSION);
+#else
+	msg_ginfo(" unknown PCI library,");
+#endif
+#endif
+#ifdef __clang__
+	msg_ginfo(" LLVM Clang");
+#ifdef __clang_version__
+	msg_ginfo(" %s,", __clang_version__);
+#else
+	msg_ginfo(" unknown version (before r102686),");
+#endif
+#elif defined(__GNUC__)
+	msg_ginfo(" GCC");
+#ifdef __VERSION__
+	msg_ginfo(" %s,", __VERSION__);
+#else
+	msg_ginfo(" unknown version,");
+#endif
+#else
+	msg_ginfo(" unknown compiler,");
+#endif
+#if defined (__FLASHROM_LITTLE_ENDIAN__)
+	msg_ginfo(" little endian");
+#else
+	msg_ginfo(" big endian");
+#endif
+	msg_ginfo("\n");
+}
+
+void print_version(void)
+{
+	//msg_ginfo("flashrom v%s", flashrom_version);
+	//print_sysinfo();
+}
+
+void print_banner(void)
+{
+	msg_ginfo("flashrom is free software, get the source code at "
+		    "http://www.flashrom.org\n");
+	msg_ginfo("\n");
+}
+
+int selfcheck(void)
+{
+	int ret = 0;
+	const struct flashchip *flash;
+
+	/* Safety check. Instead of aborting after the first error, check
+	 * if more errors exist.
+	 */
+	if (ARRAY_SIZE(programmer_table) - 1 != PROGRAMMER_INVALID) {
+		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...
+	 * 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. */
+	if (flashchips == NULL || flashchips[0].vendor == NULL) {
+		msg_gerr("Flashchips table miscompilation!\n");
+		ret = 1;
+	}
+	for (flash = flashchips; flash && flash->name; flash++)
+		if (selfcheck_eraseblocks(flash))
+			ret = 1;
+
+#if CONFIG_INTERNAL == 1
+	if (chipset_enables == NULL) {
+		msg_gerr("Chipset enables table does not exist!\n");
+		ret = 1;
+	}
+	if (board_pciid_enables == NULL) {
+		msg_gerr("Board enables table does not exist!\n");
+		ret = 1;
+	}
+	if (boards_known == NULL) {
+		msg_gerr("Known boards table does not exist!\n");
+		ret = 1;
+	}
+	if (laptops_known == NULL) {
+		msg_gerr("Known laptops table does not exist!\n");
+		ret = 1;
+	}
+#endif // CONFIG_INTERNAL == 1
+	return ret;
+}
+
+void check_chip_supported(const struct flashchip *flash)
+{
+	if (TEST_OK_MASK != (flash->tested & TEST_OK_MASK)) {
+		msg_cinfo("===\n");
+		if (flash->tested & TEST_BAD_MASK) {
+			msg_cinfo("This flash part has status NOT WORKING for operations:");
+			if (flash->tested & TEST_BAD_PROBE)
+				msg_cinfo(" PROBE");
+			if (flash->tested & TEST_BAD_READ)
+				msg_cinfo(" READ");
+			if (flash->tested & TEST_BAD_ERASE)
+				msg_cinfo(" ERASE");
+			if (flash->tested & TEST_BAD_WRITE)
+				msg_cinfo(" WRITE");
+			msg_cinfo("\n");
+		}
+		if ((!(flash->tested & TEST_BAD_PROBE) && !(flash->tested & TEST_OK_PROBE)) ||
+		    (!(flash->tested & TEST_BAD_READ) && !(flash->tested & TEST_OK_READ)) ||
+		    (!(flash->tested & TEST_BAD_ERASE) && !(flash->tested & TEST_OK_ERASE)) ||
+		    (!(flash->tested & TEST_BAD_WRITE) && !(flash->tested & TEST_OK_WRITE))) {
+			msg_cinfo("This flash part has status UNTESTED for operations:");
+			if (!(flash->tested & TEST_BAD_PROBE) && !(flash->tested & TEST_OK_PROBE))
+				msg_cinfo(" PROBE");
+			if (!(flash->tested & TEST_BAD_READ) && !(flash->tested & TEST_OK_READ))
+				msg_cinfo(" READ");
+			if (!(flash->tested & TEST_BAD_ERASE) && !(flash->tested & TEST_OK_ERASE))
+				msg_cinfo(" ERASE");
+			if (!(flash->tested & TEST_BAD_WRITE) && !(flash->tested & TEST_OK_WRITE))
+				msg_cinfo(" WRITE");
+			msg_cinfo("\n");
+		}
+		/* FIXME: This message is designed towards CLI users. */
+		msg_cinfo("The test status of this chip may have been updated "
+			    "in the latest development\n"
+			  "version of flashrom. If you are running the latest "
+			    "development version,\n"
+			  "please email a report to flashrom@flashrom.org if "
+			    "any of the above operations\n"
+			  "work correctly for you with this flash part. Please "
+			    "include the flashrom\n"
+			  "output with the additional -V option for all "
+			    "operations you tested (-V, -Vr,\n"
+			  "-Vw, -VE), and mention which mainboard or "
+			    "programmer you tested.\n"
+			  "Please mention your board in the subject line. "
+			    "Thanks for your help!\n");
+	}
+}
+
+int main_fl()
+{
+	return cli_classic();
+}
+
+/* FIXME: This function signature needs to be improved once doit() has a better
+ * function signature.
+ */
+int chip_safety_check(struct flashchip *flash, int force, int read_it, int write_it, int erase_it, int verify_it);
+int chip_safety_check(struct flashchip *flash, int force, int read_it, int write_it, int erase_it, int verify_it)
+{
+	if (!programmer_may_write && (write_it || erase_it)) {
+		msg_perr("Write/erase is not working yet on your programmer in "
+			 "its current configuration.\n");
+		/* --force is the wrong approach, but it's the best we can do
+		 * until the generic programmer parameter parser is merged.
+		 */
+		if (!force)
+			return 1;
+		msg_cerr("Continuing anyway.\n");
+	}
+
+	if (read_it || erase_it || write_it || verify_it) {
+		/* Everything needs read. */
+		if (flash->tested & TEST_BAD_READ) {
+			msg_cerr("Read is not working on this chip. ");
+			if (!force)
+				return 1;
+			msg_cerr("Continuing anyway.\n");
+		}
+		if (!flash->read) {
+			msg_cerr("flashrom has no read function for this "
+				 "flash chip.\n");
+			return 1;
+		}
+	}
+	if (erase_it || write_it) {
+		/* Write needs erase. */
+		if (flash->tested & TEST_BAD_ERASE) {
+			msg_cerr("Erase is not working on this chip. ");
+			if (!force)
+				return 1;
+			msg_cerr("Continuing anyway.\n");
+		}
+		if(count_usable_erasers(flash) == 0) {
+			msg_cerr("flashrom has no erase function for this "
+				 "flash chip.\n");
+			return 1;
+		}
+	}
+	if (write_it) {
+		if (flash->tested & TEST_BAD_WRITE) {
+			msg_cerr("Write is not working on this chip. ");
+			if (!force)
+				return 1;
+			msg_cerr("Continuing anyway.\n");
+		}
+		if (!flash->write) {
+			msg_cerr("flashrom has no write function for this "
+				 "flash chip.\n");
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/* This function signature is horrible. We need to design a better interface,
+ * 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)
+{
+	uint8_t *oldcontents;
+	uint8_t *newcontents;
+	int ret = 0;
+	unsigned long size = flash->total_size * 1024;
+
+	if (chip_safety_check(flash, force, read_it, write_it, erase_it, verify_it)) {
+		msg_cerr("Aborting.\n");
+		ret = 1;
+		goto out_nofree;
+	}
+
+	/* Given the existence of read locks, we want to unlock for read,
+	 * erase and write.
+	 */
+	if (flash->unlock)
+		flash->unlock(flash);
+
+	
+
+	if (erase_it) {
+		/* FIXME: Do we really want the scary warning if erase failed?
+		 * After all, after erase the chip is either blank or partially
+		 * blank or it has the old contents. A blank chip won't boot,
+		 * so if the user wanted erase and reboots afterwards, the user
+		 * knows very well that booting won't work.
+		 */
+		if (erase_and_write_flash(flash, oldcontents, newcontents)) {
+			emergency_help_message();
+			ret = 1;
+		}
+		goto out;
+	}
+
+	if (write_it || verify_it) {
+		if (read_buf_from_file(newcontents, size, filename)) {
+			ret = 1;
+			goto out;
+		}
+
+	}
+
+	/* Read the whole chip to be able to 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.
+	 */
+
+	// This should be moved into each flash part's code to do it 
+	// cleanly. This does the job.
+	//handle_romentries(flash, oldcontents, newcontents);
+
+	// ////////////////////////////////////////////////////////////
+
+	if (write_it) {
+		if (erase_and_write_flash(flash, oldcontents, newcontents)) {
+			msg_cerr("Uh oh. Erase/write failed. Checking if "
+				 "anything changed.\n");
+			if (!flash->read(flash, newcontents, 0, size)) {
+				if (!memcmp(oldcontents, newcontents, size)) {
+					msg_cinfo("Good. It seems nothing was "
+						  "changed.\n");
+					nonfatal_help_message();
+					ret = 1;
+					goto out;
+				}
+			}
+			emergency_help_message();
+			ret = 1;
+			goto out;
+		}
+	}
+
+
+out:
+//	free(oldcontents);
+//	free(newcontents);
+out_nofree:
+	programmer_shutdown();
+	return ret;
+}
diff --git a/src/mainboard/msi/ms6119/f/hwaccess.c b/src/mainboard/msi/ms6119/f/hwaccess.c
new file mode 100644
index 0000000..417b3a7
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/hwaccess.c
@@ -0,0 +1,266 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+//#define __LIBPAYLOAD__	1
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#if !defined (__DJGPP__)
+#include <errno.h>
+#endif
+#include "flash.h"
+
+#if defined(__i386__) || defined(__x86_64__)
+
+/* sync primitive is not needed because x86 uses uncached accesses
+ * which have a strongly ordered memory model.
+ */
+static inline void sync_primitive(void)
+{
+}
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+int io_fd;
+#endif
+
+void get_io_perms(void)
+{
+#if defined(__DJGPP__) || defined(__LIBPAYLOAD__)
+	/* We have full permissions by default. */
+	return;
+#else
+#if defined (__sun) && (defined(__i386) || defined(__amd64))
+	if (sysi86(SI86V86, V86SC_IOPL, PS_IOPL) != 0) {
+#elif defined(__FreeBSD__) || defined (__DragonFly__)
+	if ((io_fd = open("/dev/io", O_RDWR)) < 0) {
+#else 
+	if (iopl(3) != 0) {
+#endif
+		msg_perr("ERROR: Could not get I/O privileges (%s).\n"
+			"You need to be root.\n", strerror(errno));
+#if defined (__OpenBSD__)
+		msg_perr("Please set securelevel=-1 in /etc/rc.securelevel "
+			   "and reboot, or reboot into \n");
+		msg_perr("single user mode.\n");
+#endif
+		exit(1);
+	}
+#endif
+}
+
+void release_io_perms(void)
+{
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+	close(io_fd);
+#endif
+}
+
+#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || defined(__ppc64__)
+
+static inline void sync_primitive(void)
+{
+	/* Prevent reordering and/or merging of reads/writes to hardware.
+	 * Such reordering and/or merging would break device accesses which
+	 * depend on the exact access order.
+	 */
+	asm("eieio" : : : "memory");
+}
+
+/* PCI port I/O is not yet implemented on PowerPC. */
+void get_io_perms(void)
+{
+}
+
+/* PCI port I/O is not yet implemented on PowerPC. */
+void release_io_perms(void)
+{
+}
+
+#elif defined (__mips) || defined (__mips__) || defined (_mips) || defined (mips)
+
+/* sync primitive is not needed because /dev/mem on MIPS uses uncached accesses
+ * in mode 2 which has a strongly ordered memory model.
+ */
+static inline void sync_primitive(void)
+{
+}
+
+/* PCI port I/O is not yet implemented on MIPS. */
+void get_io_perms(void)
+{
+}
+
+/* PCI port I/O is not yet implemented on MIPS. */
+void release_io_perms(void)
+{
+}
+
+#else
+
+#error Unknown architecture
+
+#endif
+
+void mmio_writeb(uint8_t val, void *addr)
+{
+	*(volatile uint8_t *) addr = val;
+	sync_primitive();
+}
+
+void mmio_writew(uint16_t val, void *addr)
+{
+	*(volatile uint16_t *) addr = val;
+	sync_primitive();
+}
+
+void mmio_writel(uint32_t val, void *addr)
+{
+	*(volatile uint32_t *) addr = val;
+	sync_primitive();
+}
+
+uint8_t mmio_readb(void *addr)
+{
+	return *(volatile uint8_t *) addr;
+}
+
+uint16_t mmio_readw(void *addr)
+{
+	return *(volatile uint16_t *) addr;
+}
+
+uint32_t mmio_readl(void *addr)
+{
+	return *(volatile uint32_t *) addr;
+}
+
+void mmio_le_writeb(uint8_t val, void *addr)
+{
+	mmio_writeb(cpu_to_le8(val), addr);
+}
+
+void mmio_le_writew(uint16_t val, void *addr)
+{
+	mmio_writew(cpu_to_le16(val), addr);
+}
+
+void mmio_le_writel(uint32_t val, void *addr)
+{
+	mmio_writel(cpu_to_le32(val), addr);
+}
+
+uint8_t mmio_le_readb(void *addr)
+{
+	return le_to_cpu8(mmio_readb(addr));
+}
+
+uint16_t mmio_le_readw(void *addr)
+{
+	return le_to_cpu16(mmio_readw(addr));
+}
+
+uint32_t mmio_le_readl(void *addr)
+{
+	return le_to_cpu32(mmio_readl(addr));
+}
+
+enum mmio_write_type {
+	mmio_write_type_b,
+	mmio_write_type_w,
+	mmio_write_type_l,
+};
+
+struct undo_mmio_write_data {
+	void *addr;
+	int reg;
+	enum mmio_write_type type;
+	union {
+		uint8_t bdata;
+		uint16_t wdata;
+		uint32_t ldata;
+	};
+};
+
+int undo_mmio_write(void *p)
+{
+	struct undo_mmio_write_data *data = p;
+	msg_pdbg("Restoring MMIO space at %p\n", data->addr);
+	switch (data->type) {
+	case mmio_write_type_b:
+		mmio_writeb(data->bdata, data->addr);
+		break;
+	case mmio_write_type_w:
+		mmio_writew(data->wdata, data->addr);
+		break;
+	case mmio_write_type_l:
+		mmio_writel(data->ldata, data->addr);
+		break;
+	}
+	/* p was allocated in register_undo_mmio_write. */
+	return 0;
+}
+
+
+
+void rmmio_writeb(uint8_t val, void *addr)
+{
+	mmio_writeb(val, addr);
+}
+
+void rmmio_writew(uint16_t val, void *addr)
+{
+	mmio_writew(val, addr);
+}
+
+void rmmio_writel(uint32_t val, void *addr)
+{
+	mmio_writel(val, addr);
+}
+
+void rmmio_le_writeb(uint8_t val, void *addr)
+{
+	mmio_le_writeb(val, addr);
+}
+
+void rmmio_le_writew(uint16_t val, void *addr)
+{
+	mmio_le_writew(val, addr);
+}
+
+void rmmio_le_writel(uint32_t val, void *addr)
+{
+	mmio_le_writel(val, addr);
+}
+
+void rmmio_valb(void *addr)
+{
+	//register_undo_mmio_writeb(addr);
+}
+
+void rmmio_valw(void *addr)
+{
+	//register_undo_mmio_writew(addr);
+}
+
+void rmmio_vall(void *addr)
+{
+	//register_undo_mmio_writel(addr);
+}
diff --git a/src/mainboard/msi/ms6119/f/hwaccess.h b/src/mainboard/msi/ms6119/f/hwaccess.h
new file mode 100644
index 0000000..2b3194d
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/hwaccess.h
@@ -0,0 +1,338 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+/*
+ * Header file for hardware access and OS abstraction. Included from flash.h.
+ */
+
+#ifndef __HWACCESS_H__
+#define __HWACCESS_H__ 1
+
+#define NEED_PCI	1
+
+#if defined (__i386__) || defined (__x86_64__)
+#if defined(__GLIBC__)
+#include <sys/io.h>
+#endif
+#endif
+
+#if NEED_PCI == 1
+/*
+ * libpci headers use the variable name "index" which triggers shadowing
+ * warnings on systems which have the index() function in a default #include
+ * or as builtin.
+ */
+#define index shadow_workaround_index
+//#include <pci/pci.h>
+#undef index
+#endif
+
+#if defined (__i386__) || defined (__x86_64__)
+
+/* All x86 is little-endian. */
+#define __FLASHROM_LITTLE_ENDIAN__ 1
+
+#elif defined (__mips) || defined (__mips__) || defined (_mips) || defined (mips)
+
+/* MIPS can be either endian. */
+#if defined (__MIPSEL) || defined (__MIPSEL__) || defined (_MIPSEL) || defined (MIPSEL)
+#define __FLASHROM_LITTLE_ENDIAN__ 1
+#elif defined (__MIPSEB) || defined (__MIPSEB__) || defined (_MIPSEB) || defined (MIPSEB)
+#define __FLASHROM_BIG_ENDIAN__ 1
+#endif
+
+#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || defined(__ppc64__)
+
+/* PowerPC can be either endian. */
+#if defined (_BIG_ENDIAN) || defined (__BIG_ENDIAN__)
+#define __FLASHROM_BIG_ENDIAN__ 1
+/* Error checking in case some weird header has #defines for LE as well. */
+#if defined (_LITTLE_ENDIAN) || defined (__LITTLE_ENDIAN__)
+#error Conflicting endianness #define
+#endif
+#else
+#error Little-endian PowerPC #defines are unknown
+#endif
+
+#endif
+
+#if !defined (__FLASHROM_BIG_ENDIAN__) && !defined (__FLASHROM_LITTLE_ENDIAN__)
+/* Nonstandard libc-specific macros for determining endianness. */
+#if defined(__GLIBC__)
+#include <endian.h>
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define __FLASHROM_LITTLE_ENDIAN__ 1
+#elif BYTE_ORDER == BIG_ENDIAN
+#define __FLASHROM_BIG_ENDIAN__ 1
+#endif
+#endif
+#endif
+
+#if !defined (__FLASHROM_BIG_ENDIAN__) && !defined (__FLASHROM_LITTLE_ENDIAN__)
+#error Unable to determine endianness. Please add support for your arch or libc.
+#endif
+
+#define ___constant_swab8(x) ((uint8_t) (				\
+	(((uint8_t)(x) & (uint8_t)0xffU))))
+
+#define ___constant_swab16(x) ((uint16_t) (				\
+	(((uint16_t)(x) & (uint16_t)0x00ffU) << 8) |			\
+	(((uint16_t)(x) & (uint16_t)0xff00U) >> 8)))
+
+#define ___constant_swab32(x) ((uint32_t) (				\
+	(((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) |		\
+	(((uint32_t)(x) & (uint32_t)0x0000ff00UL) <<  8) |		\
+	(((uint32_t)(x) & (uint32_t)0x00ff0000UL) >>  8) |		\
+	(((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24)))
+
+#define ___constant_swab64(x) ((uint64_t) (				\
+	(((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) |	\
+	(((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) |	\
+	(((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) |	\
+	(((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) <<  8) |	\
+	(((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >>  8) |	\
+	(((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) |	\
+	(((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) |	\
+	(((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56)))
+
+#if defined (__FLASHROM_BIG_ENDIAN__)
+
+#define cpu_to_le(bits)							\
+static inline uint##bits##_t cpu_to_le##bits(uint##bits##_t val)	\
+{									\
+	return ___constant_swab##bits(val);				\
+}
+
+cpu_to_le(8)
+cpu_to_le(16)
+cpu_to_le(32)
+cpu_to_le(64)
+
+#define cpu_to_be8
+#define cpu_to_be16
+#define cpu_to_be32
+#define cpu_to_be64
+
+#elif defined (__FLASHROM_LITTLE_ENDIAN__)
+
+#define cpu_to_be(bits)							\
+static inline uint##bits##_t cpu_to_be##bits(uint##bits##_t val)	\
+{									\
+	return ___constant_swab##bits(val);				\
+}
+
+cpu_to_be(8)
+cpu_to_be(16)
+cpu_to_be(32)
+cpu_to_be(64)
+
+#define cpu_to_le8
+#define cpu_to_le16
+#define cpu_to_le32
+#define cpu_to_le64
+
+#else
+
+#error Could not determine endianness.
+
+#endif
+
+#define be_to_cpu8 cpu_to_be8
+#define be_to_cpu16 cpu_to_be16
+#define be_to_cpu32 cpu_to_be32
+#define be_to_cpu64 cpu_to_be64
+#define le_to_cpu8 cpu_to_le8
+#define le_to_cpu16 cpu_to_le16
+#define le_to_cpu32 cpu_to_le32
+#define le_to_cpu64 cpu_to_le64
+
+#if NEED_PCI == 1
+#if defined (__i386__) || defined (__x86_64__)
+
+#define __FLASHROM_HAVE_OUTB__ 1
+
+/* for iopl and outb under Solaris */
+#if defined (__sun) && (defined(__i386) || defined(__amd64))
+#include <strings.h>
+#include <sys/sysi86.h>
+#include <sys/psw.h>
+#include <asm/sunddi.h>
+#endif
+
+#if (defined(__MACH__) && defined(__APPLE__))
+#define __DARWIN__
+#endif
+
+/* Clarification about OUTB/OUTW/OUTL argument order:
+ * OUT[BWL](val, port)
+ */
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+  #include <machine/cpufunc.h>
+  #define off64_t off_t
+  #define lseek64 lseek
+  #define OUTB(x, y) do { u_int outb_tmp = (y); outb(outb_tmp, (x)); } while (0)
+  #define OUTW(x, y) do { u_int outw_tmp = (y); outw(outw_tmp, (x)); } while (0)
+  #define OUTL(x, y) do { u_int outl_tmp = (y); outl(outl_tmp, (x)); } while (0)
+  #define INB(x) __extension__ ({ u_int inb_tmp = (x); inb(inb_tmp); })
+  #define INW(x) __extension__ ({ u_int inw_tmp = (x); inw(inw_tmp); })
+  #define INL(x) __extension__ ({ u_int inl_tmp = (x); inl(inl_tmp); })
+#else
+#if defined(__DARWIN__)
+    /* Header is part of the DirectHW library. */
+    #include <DirectHW/DirectHW.h>
+    #define off64_t off_t
+    #define lseek64 lseek
+#endif
+#if defined (__sun) && (defined(__i386) || defined(__amd64))
+  /* Note different order for outb */
+  #define OUTB(x,y) outb(y, x)
+  #define OUTW(x,y) outw(y, x)
+  #define OUTL(x,y) outl(y, x)
+  #define INB  inb
+  #define INW  inw
+  #define INL  inl
+#else
+
+#ifdef __DJGPP__
+
+#include <pc.h>
+
+  #define OUTB(x,y) outportb(y, x)
+  #define OUTW(x,y) outportw(y, x)
+  #define OUTL(x,y) outportl(y, x)
+
+  #define INB  inportb
+  #define INW  inportw
+  #define INL  inportl
+
+#else 
+
+  #define OUTB outb
+  #define OUTW outw
+  #define OUTL outl
+  #define INB  inb
+  #define INW  inw
+  #define INL  inl
+
+#endif
+
+#endif
+#endif
+
+#if defined(__NetBSD__) || defined (__OpenBSD__)
+  #define off64_t off_t
+  #define lseek64 lseek
+  #if defined(__i386__) || defined(__x86_64__)
+    #include <sys/types.h>
+    #include <machine/sysarch.h>
+#if defined(__NetBSD__)
+    #if defined(__i386__)
+      #define iopl i386_iopl
+    #elif defined(__x86_64__)
+      #define iopl x86_64_iopl
+    #endif
+#elif defined (__OpenBSD__)
+    #if defined(__i386__)
+      #define iopl i386_iopl
+    #elif defined(__amd64__)
+      #define iopl amd64_iopl
+    #endif
+#endif
+
+static inline void outb(uint8_t value, uint16_t port)
+{
+	asm volatile ("outb %b0,%w1": :"a" (value), "Nd" (port));
+}
+
+static inline uint8_t inb(uint16_t port)
+{
+	uint8_t value;
+	asm volatile ("inb %w1,%0":"=a" (value):"Nd" (port));
+	return value;
+}
+
+static inline void outw(uint16_t value, uint16_t port)
+{
+	asm volatile ("outw %w0,%w1": :"a" (value), "Nd" (port));
+}
+
+static inline uint16_t inw(uint16_t port)
+{
+	uint16_t value;
+	asm volatile ("inw %w1,%0":"=a" (value):"Nd" (port));
+	return value;
+}
+
+static inline void outl(uint32_t value, uint16_t port)
+{
+	asm volatile ("outl %0,%w1": :"a" (value), "Nd" (port));
+}
+
+static inline uint32_t inl(uint16_t port)
+{
+	uint32_t value;
+	asm volatile ("inl %1,%0":"=a" (value):"Nd" (port));
+	return value;
+}
+  #endif
+#endif
+
+#if !defined(__DARWIN__) && !defined(__FreeBSD__) && !defined(__DragonFly__) && !defined(__LIBPAYLOAD__)
+typedef struct { uint32_t hi, lo; } msr_t;
+msr_t rdmsr(int addr);
+int wrmsr(int addr, msr_t msr);
+#endif
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+/* FreeBSD already has conflicting definitions for wrmsr/rdmsr. */
+#undef rdmsr
+#undef wrmsr
+#define rdmsr freebsd_rdmsr
+#define wrmsr freebsd_wrmsr
+typedef struct { uint32_t hi, lo; } msr_t;
+msr_t freebsd_rdmsr(int addr);
+int freebsd_wrmsr(int addr, msr_t msr);
+#endif
+#if defined(__LIBPAYLOAD__)
+#include <arch/io.h>
+#include <arch/msr.h>
+typedef struct { uint32_t hi, lo; } msr_t;
+msr_t libpayload_rdmsr(int addr);
+int libpayload_wrmsr(int addr, msr_t msr);
+#undef rdmsr
+#define rdmsr libpayload_rdmsr
+#define wrmsr libpayload_wrmsr
+#endif
+
+#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || defined(__ppc64__)
+
+/* PCI port I/O is not yet implemented on PowerPC. */
+
+#elif defined (__mips) || defined (__mips__) || defined (_mips) || defined (mips)
+
+/* PCI port I/O is not yet implemented on MIPS. */
+
+#else
+
+#error Unknown architecture, please check if it supports PCI port IO.
+
+#endif
+#endif
+
+#endif /* !__HWACCESS_H__ */
diff --git a/src/mainboard/msi/ms6119/f/internal.c b/src/mainboard/msi/ms6119/f/internal.c
new file mode 100644
index 0000000..f42199d
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/internal.c
@@ -0,0 +1,265 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include "flash.h"
+#include "programmer.h"
+
+#if NEED_PCI == 1
+struct pci_dev *pci_dev_find_filter(struct pci_filter filter)
+{
+	struct pci_dev *temp;
+
+	for (temp = pacc->devices; temp; temp = temp->next)
+		if (pci_filter_match(&filter, temp))
+			return temp;
+
+	return NULL;
+}
+
+struct pci_dev *pci_dev_find_vendorclass(uint16_t vendor, uint16_t class)
+{
+	struct pci_dev *temp;
+	struct pci_filter filter;
+	uint16_t tmp2;
+
+	pci_filter_init(NULL, &filter);
+	filter.vendor = vendor;
+
+	for (temp = pacc->devices; temp; temp = temp->next)
+		if (pci_filter_match(&filter, temp)) {
+			/* Read PCI class */
+			tmp2 = pci_read_word(temp, 0x0a);
+			if (tmp2 == class)
+				return temp;
+		}
+
+	return NULL;
+}
+
+struct pci_dev *pci_dev_find(uint16_t vendor, uint16_t device)
+{
+	struct pci_dev *temp;
+	struct pci_filter filter;
+
+	pci_filter_init(NULL, &filter);
+	filter.vendor = vendor;
+	filter.device = device;
+
+	for (temp = pacc->devices; temp; temp = temp->next)
+		if (pci_filter_match(&filter, temp))
+			return temp;
+
+	return NULL;
+}
+
+struct pci_dev *pci_card_find(uint16_t vendor, uint16_t device,
+			      uint16_t card_vendor, uint16_t card_device)
+{
+	struct pci_dev *temp;
+	struct pci_filter filter;
+
+	pci_filter_init(NULL, &filter);
+	filter.vendor = vendor;
+	filter.device = device;
+
+	for (temp = pacc->devices; temp; temp = temp->next)
+		if (pci_filter_match(&filter, temp)) {
+			if ((card_vendor ==
+			     pci_read_word(temp, PCI_SUBSYSTEM_VENDOR_ID))
+			    && (card_device ==
+				pci_read_word(temp, PCI_SUBSYSTEM_ID)))
+				return temp;
+		}
+
+	return NULL;
+}
+#endif
+
+#if CONFIG_INTERNAL == 1
+int force_boardenable = 0;
+int force_boardmismatch = 0;
+
+#if defined(__i386__) || defined(__x86_64__)
+void probe_superio(void)
+{
+	//probe_superio_ite();
+#if 0
+	/* Winbond Super I/O code is not yet available. */
+	if (superio.vendor == SUPERIO_VENDOR_NONE)
+		superio = probe_superio_winbond();
+#endif
+}
+
+int superio_count = 0;
+#define SUPERIO_MAX_COUNT 3
+
+struct superio superios[SUPERIO_MAX_COUNT];
+
+int register_superio(struct superio s)
+{
+	if (superio_count == SUPERIO_MAX_COUNT)
+		return 1;
+	superios[superio_count++] = s;
+	return 0;
+}
+
+#endif
+
+int is_laptop = 0;
+int laptop_ok = 0;
+
+static int internal_shutdown(void *data)
+{
+	release_io_perms();
+	return 0;
+}
+
+int internal_init(void)
+{
+#if __FLASHROM_LITTLE_ENDIAN__
+	int ret = 0;
+#endif
+	int force_laptop = 0;
+	char *arg;
+
+
+
+	/* Default to Parallel/LPC/FWH flash devices. If a known host controller
+	 * is found, the init routine sets the buses_supported bitfield.
+	 */
+	buses_supported = CHIP_BUSTYPE_NONSPI;
+
+	/* Initialize PCI access for flash enables */
+	pacc = pci_alloc();	/* Get the pci_access structure */
+	/* Set all options you want -- here we stick with the defaults */
+	pci_init(pacc);		/* Initialize the PCI library */
+	pci_scan_bus(pacc);	/* We want to get the list of devices */
+
+
+
+#if defined(__i386__) || defined(__x86_64__)
+	/* We look at the cbtable first to see if we need a
+	 * mainboard specific flash enable sequence.
+	 */
+	//coreboot_init();
+
+	//dmi_init();
+
+	/* In case Super I/O probing would cause pretty explosions. */
+//	board_handle_before_superio();
+
+	/* Probe for the Super I/O chip and fill global struct superio. */
+	//probe_superio();
+#else
+	/* FIXME: Enable cbtable searching on all non-x86 platforms supported
+	 *        by coreboot.
+	 * FIXME: Find a replacement for DMI on non-x86.
+	 * FIXME: Enable Super I/O probing once port I/O is possible.
+	 */
+#endif
+
+	/* Check laptop whitelist. */
+//	board_handle_before_laptop();
+
+
+
+#if __FLASHROM_LITTLE_ENDIAN__
+	/* 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 "
+			 "will most likely fail.\n");
+	}
+
+#if defined(__i386__) || defined(__x86_64__)
+	/* Probe unconditionally for IT87* LPC->SPI translation and for
+	 * IT87* Parallel write enable.
+	 */
+	//init_superio_ite();
+#endif
+
+	//board_flash_enable(lb_vendor, lb_part);
+
+	/* Even if chipset init returns an error code, we don't want to abort.
+	 * 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)
+	return 0;
+#else
+	msg_perr("Your platform is not supported yet for the internal "
+		 "programmer due to missing\n"
+		 "flash_base and top/bottom alignment information.\n"
+		 "Aborting.\n");
+	return 1;
+#endif
+#else
+	/* FIXME: Remove this unconditional abort once all PCI drivers are
+	 * converted to use little-endian accesses for memory BARs.
+	 */
+	msg_perr("Your platform is not supported yet for the internal "
+		 "programmer because it has\n"
+		 "not been converted from native endian to little endian "
+		 "access yet.\n"
+		 "Aborting.\n");
+	return 1;
+#endif
+}
+#endif
+
+void internal_chip_writeb(uint8_t val, chipaddr addr)
+{
+	mmio_writeb(val, (void *) addr);
+}
+
+void internal_chip_writew(uint16_t val, chipaddr addr)
+{
+	mmio_writew(val, (void *) addr);
+}
+
+void internal_chip_writel(uint32_t val, chipaddr addr)
+{
+	mmio_writel(val, (void *) addr);
+}
+
+uint8_t internal_chip_readb(const chipaddr addr)
+{
+	return mmio_readb((void *) addr);
+}
+
+uint16_t internal_chip_readw(const chipaddr addr)
+{
+	return mmio_readw((void *) addr);
+}
+
+uint32_t internal_chip_readl(const chipaddr addr)
+{
+	return mmio_readl((void *) addr);
+}
+
+void internal_chip_readn(uint8_t *buf, const chipaddr addr, size_t len)
+{
+	memcpy(buf, (void *)addr, len);
+	return;
+}
diff --git a/src/mainboard/msi/ms6119/f/jedec.c b/src/mainboard/msi/ms6119/f/jedec.c
new file mode 100644
index 0000000..4950bd4
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/jedec.c
@@ -0,0 +1,507 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2006 Giampiero Giancipoli <gianci@email.it>
+ * Copyright (C) 2006 coresystems GmbH <info@coresystems.de>
+ * Copyright (C) 2007 Carl-Daniel Hailfinger
+ * Copyright (C) 2009 Sean Nelson <audiohacked@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include "flash.h"
+#include "chipdrivers.h"
+
+#define MAX_REFLASH_TRIES 0x10
+#define MASK_FULL 0xffff
+#define MASK_2AA 0x7ff
+#define MASK_AAA 0xfff
+
+/* Check one byte for odd parity */
+uint8_t oddparity(uint8_t val)
+{
+	val = (val ^ (val >> 4)) & 0xf;
+	val = (val ^ (val >> 2)) & 0x3;
+	return (val ^ (val >> 1)) & 0x1;
+}
+
+static void toggle_ready_jedec_common(chipaddr dst, int delay)
+{
+	unsigned int i = 0;
+	uint8_t tmp1, tmp2;
+
+	tmp1 = chip_readb(dst) & 0x40;
+
+	while (i++ < 0xFFFFFFF) {
+		if (delay)
+			programmer_delay(delay);
+		tmp2 = chip_readb(dst) & 0x40;
+		if (tmp1 == tmp2) {
+			break;
+		}
+		tmp1 = tmp2;
+	}
+	if (i > 0x100000)
+		msg_cdbg("%s: excessive loops, i=0x%x\n", __func__, i);
+}
+
+void toggle_ready_jedec(chipaddr dst)
+{
+	toggle_ready_jedec_common(dst, 0);
+}
+
+/* Some chips require a minimum delay between toggle bit reads.
+ * The Winbond W39V040C wants 50 ms between reads on sector erase toggle,
+ * but experiments show that 2 ms are already enough. Pick a safety factor
+ * of 4 and use an 8 ms delay.
+ * Given that erase is slow on all chips, it is recommended to use 
+ * toggle_ready_jedec_slow in erase functions.
+ */
+static void toggle_ready_jedec_slow(chipaddr dst)
+{
+	toggle_ready_jedec_common(dst, 8 * 1000);
+}
+
+void data_polling_jedec(chipaddr dst, uint8_t data)
+{
+	unsigned int i = 0;
+	uint8_t tmp;
+
+	data &= 0x80;
+
+	while (i++ < 0xFFFFFFF) {
+		tmp = chip_readb(dst) & 0x80;
+		if (tmp == data) {
+			break;
+		}
+	}
+	if (i > 0x100000)
+		msg_cdbg("%s: excessive loops, i=0x%x\n", __func__, i);
+}
+
+static int getaddrmask(struct flashchip *flash)
+{
+	switch (flash->feature_bits & FEATURE_ADDR_MASK) {
+	case FEATURE_ADDR_FULL:
+		return MASK_FULL;
+		break;
+	case FEATURE_ADDR_2AA:
+		return MASK_2AA;
+		break;
+	case FEATURE_ADDR_AAA:
+		return MASK_AAA;
+		break;
+	default:
+		msg_cerr("%s called with unknown mask\n", __func__);
+		return 0;
+		break;
+	}
+}
+
+static void start_program_jedec_common(struct flashchip *flash, unsigned int mask)
+{
+	chipaddr bios = flash->virtual_memory;
+	chip_writeb(0xAA, bios + (0x5555 & mask));
+	chip_writeb(0x55, bios + (0x2AAA & mask));
+	chip_writeb(0xA0, bios + (0x5555 & mask));
+}
+
+static int probe_jedec_common(struct flashchip *flash, unsigned int mask)
+{
+	chipaddr bios = flash->virtual_memory;
+	uint8_t id1, id2;
+	uint32_t largeid1, largeid2;
+	uint32_t flashcontent1, flashcontent2;
+	int probe_timing_enter, probe_timing_exit;
+
+	if (flash->probe_timing > 0) 
+		probe_timing_enter = probe_timing_exit = flash->probe_timing;
+	else if (flash->probe_timing == TIMING_ZERO) { /* No delay. */
+		probe_timing_enter = probe_timing_exit = 0;
+	} else if (flash->probe_timing == TIMING_FIXME) { /* == _IGNORED */
+		msg_cdbg("Chip lacks correct probe timing information, "
+			     "using default 10mS/40uS. ");
+		probe_timing_enter = 10000;
+		probe_timing_exit = 40;
+	} else {
+		msg_cerr("Chip has negative value in probe_timing, failing "
+		       "without chip access\n");
+		return 0;
+	}
+
+	/* Earlier probes might have been too fast for the chip to enter ID
+	 * mode completely. Allow the chip to finish this before seeing a
+	 * reset command.
+	 */
+	if (probe_timing_enter)
+		programmer_delay(probe_timing_enter);
+	/* Reset chip to a clean slate */
+	if ((flash->feature_bits & FEATURE_RESET_MASK) == FEATURE_LONG_RESET)
+	{
+		chip_writeb(0xAA, bios + (0x5555 & mask));
+		if (probe_timing_exit)
+			programmer_delay(10);
+		chip_writeb(0x55, bios + (0x2AAA & mask));
+		if (probe_timing_exit)
+			programmer_delay(10);
+	}
+	chip_writeb(0xF0, bios + (0x5555 & mask));
+	if (probe_timing_exit)
+		programmer_delay(probe_timing_exit);
+
+	/* Issue JEDEC Product ID Entry command */
+	chip_writeb(0xAA, bios + (0x5555 & mask));
+	if (probe_timing_enter)
+		programmer_delay(10);
+	chip_writeb(0x55, bios + (0x2AAA & mask));
+	if (probe_timing_enter)
+		programmer_delay(10);
+	chip_writeb(0x90, bios + (0x5555 & mask));
+	if (probe_timing_enter)
+		programmer_delay(probe_timing_enter);
+
+	/* Read product ID */
+	id1 = chip_readb(bios);
+	id2 = chip_readb(bios + 0x01);
+	largeid1 = id1;
+	largeid2 = id2;
+
+	/* Check if it is a continuation ID, this should be a while loop. */
+	if (id1 == 0x7F) {
+		largeid1 <<= 8;
+		id1 = chip_readb(bios + 0x100);
+		largeid1 |= id1;
+	}
+	if (id2 == 0x7F) {
+		largeid2 <<= 8;
+		id2 = chip_readb(bios + 0x101);
+		largeid2 |= id2;
+	}
+
+	/* Issue JEDEC Product ID Exit command */
+	if ((flash->feature_bits & FEATURE_RESET_MASK) == FEATURE_LONG_RESET)
+	{
+		chip_writeb(0xAA, bios + (0x5555 & mask));
+		if (probe_timing_exit)
+			programmer_delay(10);
+		chip_writeb(0x55, bios + (0x2AAA & mask));
+		if (probe_timing_exit)
+			programmer_delay(10);
+	}
+	chip_writeb(0xF0, bios + (0x5555 & mask));
+	if (probe_timing_exit)
+		programmer_delay(probe_timing_exit);
+
+	msg_cdbg("%s: id1 0x%02x, id2 0x%02x", __func__, largeid1, largeid2);
+	if (!oddparity(id1))
+		msg_cdbg(", id1 parity violation");
+
+	/* Read the product ID location again. We should now see normal flash contents. */
+	flashcontent1 = chip_readb(bios);
+	flashcontent2 = chip_readb(bios + 0x01);
+
+	/* Check if it is a continuation ID, this should be a while loop. */
+	if (flashcontent1 == 0x7F) {
+		flashcontent1 <<= 8;
+		flashcontent1 |= chip_readb(bios + 0x100);
+	}
+	if (flashcontent2 == 0x7F) {
+		flashcontent2 <<= 8;
+		flashcontent2 |= chip_readb(bios + 0x101);
+	}
+
+	if (largeid1 == flashcontent1)
+		msg_cdbg(", id1 is normal flash content");
+	if (largeid2 == flashcontent2)
+		msg_cdbg(", id2 is normal flash content");
+
+	msg_cdbg("\n");
+	if (largeid1 != flash->manufacture_id || largeid2 != flash->model_id)
+		return 0;
+
+	if (flash->feature_bits & FEATURE_REGISTERMAP)
+		map_flash_registers(flash);
+
+	return 1;
+}
+
+static int erase_sector_jedec_common(struct flashchip *flash, unsigned int page,
+			      unsigned int pagesize, unsigned int mask)
+{
+	chipaddr bios = flash->virtual_memory;
+	int delay_us = 0;
+	if(flash->probe_timing != TIMING_ZERO)
+	        delay_us = 10;
+
+	/*  Issue the Sector Erase command   */
+	chip_writeb(0xAA, bios + (0x5555 & mask));
+	programmer_delay(delay_us);
+	chip_writeb(0x55, bios + (0x2AAA & mask));
+	programmer_delay(delay_us);
+	chip_writeb(0x80, bios + (0x5555 & mask));
+	programmer_delay(delay_us);
+
+	chip_writeb(0xAA, bios + (0x5555 & mask));
+	programmer_delay(delay_us);
+	chip_writeb(0x55, bios + (0x2AAA & mask));
+	programmer_delay(delay_us);
+	chip_writeb(0x30, bios + page);
+	programmer_delay(delay_us);
+
+	/* wait for Toggle bit ready         */
+	toggle_ready_jedec_slow(bios);
+
+	/* FIXME: Check the status register for errors. */
+	return 0;
+}
+
+static int erase_block_jedec_common(struct flashchip *flash, unsigned int block,
+			     unsigned int blocksize, unsigned int mask)
+{
+	chipaddr bios = flash->virtual_memory;
+	int delay_us = 0;
+	if(flash->probe_timing != TIMING_ZERO)
+	        delay_us = 10;
+
+	/*  Issue the Sector Erase command   */
+	chip_writeb(0xAA, bios + (0x5555 & mask));
+	programmer_delay(delay_us);
+	chip_writeb(0x55, bios + (0x2AAA & mask));
+	programmer_delay(delay_us);
+	chip_writeb(0x80, bios + (0x5555 & mask));
+	programmer_delay(delay_us);
+
+	chip_writeb(0xAA, bios + (0x5555 & mask));
+	programmer_delay(delay_us);
+	chip_writeb(0x55, bios + (0x2AAA & mask));
+	programmer_delay(delay_us);
+	chip_writeb(0x50, bios + block);
+	programmer_delay(delay_us);
+
+	/* wait for Toggle bit ready         */
+	toggle_ready_jedec_slow(bios);
+
+	/* FIXME: Check the status register for errors. */
+	return 0;
+}
+
+static int erase_chip_jedec_common(struct flashchip *flash, unsigned int mask)
+{
+	chipaddr bios = flash->virtual_memory;
+	int delay_us = 0;
+	if(flash->probe_timing != TIMING_ZERO)
+	        delay_us = 10;
+
+	/*  Issue the JEDEC Chip Erase command   */
+	chip_writeb(0xAA, bios + (0x5555 & mask));
+	programmer_delay(delay_us);
+	chip_writeb(0x55, bios + (0x2AAA & mask));
+	programmer_delay(delay_us);
+	chip_writeb(0x80, bios + (0x5555 & mask));
+	programmer_delay(delay_us);
+
+	chip_writeb(0xAA, bios + (0x5555 & mask));
+	programmer_delay(delay_us);
+	chip_writeb(0x55, bios + (0x2AAA & mask));
+	programmer_delay(delay_us);
+	chip_writeb(0x10, bios + (0x5555 & mask));
+	programmer_delay(delay_us);
+
+	toggle_ready_jedec_slow(bios);
+
+	/* FIXME: Check the status register for errors. */
+	return 0;
+}
+
+static int write_byte_program_jedec_common(struct flashchip *flash, uint8_t *src,
+			     chipaddr dst, unsigned int mask)
+{
+	int tried = 0, failed = 0;
+	chipaddr bios = flash->virtual_memory;
+
+	/* If the data is 0xFF, don't program it and don't complain. */
+	if (*src == 0xFF) {
+		return 0;
+	}
+
+retry:
+	/* Issue JEDEC Byte Program command */
+	start_program_jedec_common(flash, mask);
+
+	/* transfer data from source to destination */
+	chip_writeb(*src, dst);
+	toggle_ready_jedec(bios);
+
+	if (chip_readb(dst) != *src && tried++ < MAX_REFLASH_TRIES) {
+		goto retry;
+	}
+
+	if (tried >= MAX_REFLASH_TRIES)
+		failed = 1;
+
+	return failed;
+}
+
+/* chunksize is 1 */
+int write_jedec_1(struct flashchip *flash, uint8_t *src, int start, int len)
+{
+	int i, failed = 0;
+	chipaddr dst = flash->virtual_memory + start;
+	chipaddr olddst;
+	int mask;
+
+	mask = getaddrmask(flash);
+
+	olddst = dst;
+	for (i = 0; i < len; i++) {
+		if (write_byte_program_jedec_common(flash, src, dst, mask))
+			failed = 1;
+		dst++, src++;
+	}
+	if (failed)
+		msg_cerr(" writing sector at 0x%lx failed!\n", olddst);
+
+	return failed;
+}
+
+int write_page_write_jedec_common(struct flashchip *flash, uint8_t *src, int start, int page_size);
+int write_page_write_jedec_common(struct flashchip *flash, uint8_t *src, int start, 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;
+
+	mask = getaddrmask(flash);
+
+retry:
+	/* Issue JEDEC Start Program command */
+	start_program_jedec_common(flash, mask);
+
+	/* transfer data from source to destination */
+	for (i = 0; i < page_size; i++) {
+		/* If the data is 0xFF, don't program it */
+		if (*src != 0xFF)
+			chip_writeb(*src, dst);
+		dst++;
+		src++;
+	}
+
+	toggle_ready_jedec(dst - 1);
+
+	dst = d;
+	src = s;
+	failed = 0;//verify_range(flash, src, start, page_size, NULL);
+
+	if (failed && tried++ < MAX_REFLASH_TRIES) {
+		msg_cerr("retrying.\n");
+		goto retry;
+	}
+	if (failed) {
+		msg_cerr(" page 0x%lx failed!\n",
+			(d - bios) / page_size);
+	}
+	return failed;
+}
+
+/* chunksize is page_size */
+/*
+ * Write a part of the flash chip.
+ * FIXME: Use the chunk code from Michael Karcher instead.
+ * 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 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;
+
+	/* Warning: This loop has a very unusual condition and body.
+	 * The loop needs to go through each page with at least one affected
+	 * byte. The lowest page number is (start / page_size) since that
+	 * division rounds down. The highest page number we want is the page
+	 * where the last byte of the range lives. That last byte has the
+	 * address (start + len - 1), thus the highest page number is
+	 * (start + len - 1) / page_size. Since we want to include that last
+	 * page as well, the loop condition uses <=.
+	 */
+	for (i = start / page_size; i <= (start + len - 1) / page_size; i++) {
+		/* Byte position of the first byte in the range in this page. */
+		/* starthere is an offset to the base address of the chip. */
+		starthere = max(start, i * page_size);
+		/* Length of bytes in the range in this page. */
+		lenhere = min(start + len, (i + 1) * page_size) - starthere;
+
+		if (write_page_write_jedec_common(flash, buf + starthere - start, starthere, lenhere))
+			return 1;
+	}
+
+	return 0;
+}
+
+/* erase chip with block_erase() prototype */
+int erase_chip_block_jedec(struct flashchip *flash, unsigned int addr,
+			   unsigned int blocksize)
+{
+	int mask;
+
+	mask = getaddrmask(flash);
+	if ((addr != 0) || (blocksize != flash->total_size * 1024)) {
+		msg_cerr("%s called with incorrect arguments\n",
+			__func__);
+		return -1;
+	}
+	return erase_chip_jedec_common(flash, mask);
+}
+
+int probe_jedec(struct flashchip *flash)
+{
+	int mask;
+
+	mask = getaddrmask(flash);
+	return probe_jedec_common(flash, mask);
+}
+
+int erase_sector_jedec(struct flashchip *flash, unsigned int page, unsigned int size)
+{
+	int mask;
+
+	mask = getaddrmask(flash);
+	return erase_sector_jedec_common(flash, page, size, mask);
+}
+
+int erase_block_jedec(struct flashchip *flash, unsigned int page, unsigned int size)
+{
+	int mask;
+
+	mask = getaddrmask(flash);
+	return erase_block_jedec_common(flash, page, size, mask);
+}
+int erase_chip_jedec(struct flashchip *flash);
+int erase_chip_jedec(struct flashchip *flash)
+{
+	int mask;
+
+	mask = getaddrmask(flash);
+	return erase_chip_jedec_common(flash, mask);
+}
diff --git a/src/mainboard/msi/ms6119/f/pcidev.c b/src/mainboard/msi/ms6119/f/pcidev.c
new file mode 100644
index 0000000..a40dcef
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/pcidev.c
@@ -0,0 +1,310 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2010, 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "flash.h"
+#include "programmer.h"
+
+#include <device/pci.h>
+uint32_t io_base_addr;
+struct pci_access *pacc;
+struct pci_dev *pcidev_dev = NULL;
+
+enum pci_bartype {
+	TYPE_MEMBAR,
+	TYPE_IOBAR,
+	TYPE_ROMBAR,
+	TYPE_UNKNOWN
+};
+
+uintptr_t pcidev_validate(struct pci_dev *dev, int bar,
+			 const struct pcidev_status *devs)
+{
+	int i;
+	uint64_t addr;
+	uint32_t upperaddr;
+	uint8_t headertype;
+	uint16_t supported_cycles;
+	enum pci_bartype bartype = TYPE_UNKNOWN;
+
+	for (i = 0; devs[i].device_name != NULL; i++) {
+		if (dev->device_id != devs[i].device_id)
+			continue;
+
+		msg_pinfo("Found \"%s %s\" (%04x:%04x, BDF %02x:%02x.%x).\n",
+		       devs[i].vendor_name, devs[i].device_name,
+		       dev->vendor_id, dev->device_id, dev->bus, dev->dev,
+		       dev->func);
+
+		headertype = pci_read_byte(dev, PCI_HEADER_TYPE) & 0x7f;
+		msg_pspew("PCI header type 0x%02x\n", headertype);
+
+		/*
+		 * Don't use dev->base_addr[x] (as value for 'bar'), won't
+		 * work on older libpci.
+		 */
+		addr = pci_read_long(dev, bar);
+
+		/* Sanity checks. */
+		switch (headertype) {
+		case PCI_HEADER_TYPE_NORMAL:
+			switch (bar) {
+			case PCI_BASE_ADDRESS_0:
+			case PCI_BASE_ADDRESS_1:
+			case PCI_BASE_ADDRESS_2:
+			case PCI_BASE_ADDRESS_3:
+			case PCI_BASE_ADDRESS_4:
+			case PCI_BASE_ADDRESS_5:
+				if ((addr & PCI_BASE_ADDRESS_SPACE) ==
+				    PCI_BASE_ADDRESS_SPACE_IO)
+					bartype = TYPE_IOBAR;
+				else
+					bartype = TYPE_MEMBAR;
+				break;
+			case PCI_ROM_ADDRESS:
+				bartype = TYPE_ROMBAR;
+				break;
+			}
+			break;
+		case PCI_HEADER_TYPE_BRIDGE:
+			switch (bar) {
+			case PCI_BASE_ADDRESS_0:
+			case PCI_BASE_ADDRESS_1:
+				if ((addr & PCI_BASE_ADDRESS_SPACE) ==
+				    PCI_BASE_ADDRESS_SPACE_IO)
+					bartype = TYPE_IOBAR;
+				else
+					bartype = TYPE_MEMBAR;
+				break;
+			case PCI_ROM_ADDRESS1:
+				bartype = TYPE_ROMBAR;
+				break;
+			}
+			break;
+		case PCI_HEADER_TYPE_CARDBUS:
+			break;
+		default:
+			msg_perr("Unknown PCI header type 0x%02x, BAR type "
+				 "cannot be determined reliably.\n", headertype);
+			break;
+		}
+
+		supported_cycles = pci_read_word(dev, PCI_COMMAND);
+
+		msg_pdbg("Requested BAR is ");
+		switch (bartype) {
+		case TYPE_MEMBAR:
+			msg_pdbg("MEM");
+			if (!(supported_cycles & PCI_COMMAND_MEMORY)) {
+				msg_perr("MEM BAR access requested, but device "
+					 "has MEM space accesses disabled.\n");
+				/* TODO: Abort here? */
+			}
+			msg_pdbg(", %sbit, %sprefetchable\n",
+				 ((addr & 0x6) == 0x0) ? "32" :
+				 (((addr & 0x6) == 0x4) ? "64" : "reserved"),
+				 (addr & 0x8) ? "" : "not ");
+			if ((addr & 0x6) == 0x4) {
+				/* The spec says that a 64-bit register consumes
+				 * two subsequent dword locations.
+				 */
+				upperaddr = pci_read_long(dev, bar + 4);
+				if (upperaddr != 0x00000000) {
+					/* Fun! A real 64-bit resource. */
+					if (sizeof(uintptr_t) != sizeof(uint64_t)) {
+						msg_perr("BAR unreachable!");
+						/* TODO: Really abort here? If
+						 * multiple PCI devices match,
+						 * we might never tell the user
+						 * about the other devices.
+						 */
+						return 0;
+					}
+					addr |= (uint64_t)upperaddr << 32;
+				}
+			}
+			addr &= PCI_BASE_ADDRESS_MEM_MASK;
+			break;
+		case TYPE_IOBAR:
+			msg_pdbg("I/O\n");
+#if __FLASHROM_HAVE_OUTB__
+			if (!(supported_cycles & PCI_COMMAND_IO)) {
+				msg_perr("I/O BAR access requested, but device "
+					 "has I/O space accesses disabled.\n");
+				/* TODO: Abort here? */
+			}
+#else
+			msg_perr("I/O BAR access requested, but flashrom does "
+				 "not support I/O BAR access on this platform "
+				 "(yet).\n");
+#endif
+			addr &= PCI_BASE_ADDRESS_IO_MASK;
+			break;
+		case TYPE_ROMBAR:
+			msg_pdbg("ROM\n");
+			/* Not sure if this check is needed. */
+			if (!(supported_cycles & PCI_COMMAND_MEMORY)) {
+				msg_perr("MEM BAR access requested, but device "
+					 "has MEM space accesses disabled.\n");
+				/* TODO: Abort here? */
+			}
+			addr &= PCI_ROM_ADDRESS_MASK;
+			break;
+		case TYPE_UNKNOWN:
+			msg_perr("BAR type unknown, please report a bug at "
+				 "flashrom@flashrom.org\n");
+		}
+		
+		if (devs[i].status == NT) {
+			msg_pinfo("===\nThis PCI device is UNTESTED. Please "
+				  "report the 'flashrom -p xxxx' output \n"
+				  "to flashrom@flashrom.org if it works "
+				  "for you. Please add the name of your\n"
+				  "PCI device to the subject. Thank you for "
+				  "your help!\n===\n");
+		}
+
+		return (uintptr_t)addr;
+	}
+
+	return 0;
+}
+
+uintptr_t pcidev_init(int bar, const struct pcidev_status *devs)
+{
+	struct pci_dev *dev;
+	struct pci_filter filter;
+	char *pcidev_bdf;
+	char *msg = NULL;
+	int found = 0;
+	uintptr_t addr = 0, curaddr = 0;
+
+	pacc = pci_alloc();     /* Get the pci_access structure */
+	pci_init(pacc);         /* Initialize the PCI library */
+	pci_scan_bus(pacc);     /* We want to get the list of devices */
+	pci_filter_init(pacc, &filter);
+
+	/* Filter by bb:dd.f (if supplied by the user). */
+	pcidev_bdf = extract_programmer_param("pci");
+	if (pcidev_bdf != NULL) {
+		if ((msg = pci_filter_parse_slot(&filter, pcidev_bdf))) {
+			msg_perr("Error: %s\n", msg);
+			return -1;
+		}
+	}
+//	free(pcidev_bdf);
+
+	for (dev = pacc->devices; dev; dev = dev->next) {
+		if (pci_filter_match(&filter, dev)) {
+			/* FIXME: We should count all matching devices, not
+			 * just those with a valid BAR.
+			 */
+			if ((addr = pcidev_validate(dev, bar, devs)) != 0) {
+				curaddr = addr;
+				pcidev_dev = dev;
+				found++;
+			}
+		}
+	}
+
+	/* Only continue if exactly one supported PCI dev has been found. */
+	if (found == 0) {
+		msg_perr("Error: No supported PCI device found.\n");
+		return -1;
+	} else if (found > 1) {
+		msg_perr("Error: Multiple supported PCI devices found. "
+			"Use 'flashrom -p xxxx:pci=bb:dd.f' \n"
+			"to explicitly select the card with the given BDF "
+			"(PCI bus, device, function).\n");
+		return -1;
+	}
+
+	return curaddr;
+}
+
+void print_supported_pcidevs(const struct pcidev_status *devs)
+{
+	int i;
+
+	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)" : "");
+	}
+}
+
+enum pci_write_type {
+	pci_write_type_byte,
+	pci_write_type_word,
+	pci_write_type_long,
+};
+
+struct undo_pci_write_data {
+	struct pci_dev dev;
+	int reg;
+	enum pci_write_type type;
+	union {
+		uint8_t bytedata;
+		uint16_t worddata;
+		uint32_t longdata;
+	};
+};
+
+int undo_pci_write(void *p)
+{
+	struct undo_pci_write_data *data = p;
+	msg_pdbg("Restoring PCI config space for %02x:%02x:%01x reg 0x%02x\n",
+		 data->dev.bus, data->dev.dev, data->dev.func, data->reg);
+	switch (data->type) {
+	case pci_write_type_byte:
+		pci_write_byte(&data->dev, data->reg, data->bytedata);
+		break;
+	case pci_write_type_word:
+		pci_write_word(&data->dev, data->reg, data->worddata);
+		break;
+	case pci_write_type_long:
+		pci_write_long(&data->dev, data->reg, data->longdata);
+		break;
+	}
+	/* p was allocated in register_undo_pci_write. */
+//	free(p);
+	return 0;
+}
+
+
+
+int rpci_write_byte(struct pci_dev *dev, int reg, uint8_t data)
+{
+	return pci_write_byte(dev, reg, data);
+}
+ 
+int rpci_write_word(struct pci_dev *dev, int reg, uint16_t data)
+{
+	return pci_write_word(dev, reg, data);
+}
+ 
+int rpci_write_long(struct pci_dev *dev, int reg, uint32_t data)
+{
+	return pci_write_long(dev, reg, data);
+}
diff --git a/src/mainboard/msi/ms6119/f/physmap.c b/src/mainboard/msi/ms6119/f/physmap.c
new file mode 100644
index 0000000..62fc6c9
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/physmap.c
@@ -0,0 +1,525 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Peter Stuge <peter@stuge.se>
+ * Copyright (C) 2009 coresystems GmbH
+ * Copyright (C) 2010 Carl-Daniel Hailfinger
+ * Copyright (C) 2010 Rudolf Marek <r.marek@assembler.cz>
+ *
+ * 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 <unistd.h>
+//#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "flash.h"
+
+
+int getpagesize(void);
+int getpagesize(void)
+{
+	return 4096;
+}
+#define virt_to_phys(virt) ((unsigned long) (virt))
+#define phys_to_virt(phys) ((void *) ((unsigned long) (phys)))
+#define virt_to_bus(addr) virt_to_phys(addr)
+#define bus_to_virt(addr) phys_to_virt(addr)
+
+/* Do we need any file access or ioctl for physmap or MSR? */
+#if !defined(__DJGPP__) && !defined(__LIBPAYLOAD__)
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#endif
+
+#ifdef __DJGPP__
+#include <dpmi.h>
+#include <sys/nearptr.h>
+
+#define MEM_DEV "dpmi"
+
+static void *realmem_map;
+
+static void *map_first_meg(unsigned long phys_addr, size_t len)
+{
+
+	if (realmem_map) {
+		return realmem_map + phys_addr;
+	}
+
+	realmem_map = valloc(1024 * 1024);
+
+	if (!realmem_map) {
+		return ERROR_PTR;
+	}
+
+	if (__djgpp_map_physical_memory(realmem_map, (1024 * 1024), 0)) {
+		free(realmem_map);
+		realmem_map = NULL;
+		return ERROR_PTR;
+	}
+
+	return realmem_map + phys_addr;
+}
+
+static void *sys_physmap(unsigned long phys_addr, size_t len)
+{
+	int ret;
+	__dpmi_meminfo mi;
+
+	/* 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 */
+		return map_first_meg(phys_addr, len);
+	}
+
+	mi.address = phys_addr;
+	mi.size = len;
+	ret =  __dpmi_physical_address_mapping (&mi);
+
+	if (ret != 0) {
+		return ERROR_PTR;
+	}
+
+	return (void *) mi.address + __djgpp_conventional_base;
+}
+
+#define sys_physmap_rw_uncached	sys_physmap
+#define sys_physmap_ro_cached	sys_physmap
+
+void physunmap(void *virt_addr, size_t len)
+{
+	__dpmi_meminfo mi;
+
+	/* 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)))) {
+		return;
+	}
+
+	mi.address = (unsigned long) virt_addr;
+	__dpmi_free_physical_address_mapping(&mi);
+}
+
+#elif defined(__LIBPAYLOAD__)
+//#include <arch/virtual.h>
+
+#define MEM_DEV ""
+
+void *sys_physmap(unsigned long phys_addr, size_t len)
+{
+	return (void*)phys_to_virt(phys_addr);
+}
+
+#define sys_physmap_rw_uncached	sys_physmap
+#define sys_physmap_ro_cached	sys_physmap
+
+void physunmap(void *virt_addr, size_t len)
+{
+}
+
+int setup_cpu_msr(int cpu)
+{
+	return 0;
+}
+
+void cleanup_cpu_msr(void)
+{
+}
+#elif defined(__DARWIN__)
+
+#define MEM_DEV "DirectHW"
+
+static void *sys_physmap(unsigned long phys_addr, size_t len)
+{
+	/* The short form of ?: is a GNU extension.
+	 * FIXME: map_physical returns NULL both for errors and for success
+	 * if the region is mapped at virtual address zero. If in doubt, report
+	 * an error until a better interface exists.
+	 */
+	return map_physical(phys_addr, len) ? : ERROR_PTR;
+}
+
+/* The OS X driver does not differentiate between mapping types. */
+#define sys_physmap_rw_uncached	sys_physmap
+#define sys_physmap_ro_cached	sys_physmap
+
+void physunmap(void *virt_addr, size_t len)
+{
+	unmap_physical(virt_addr, len);
+}
+
+#else
+#include <sys/mman.h>
+
+#if defined (__sun) && (defined(__i386) || defined(__amd64))
+#  define MEM_DEV "/dev/xsvc"
+#else
+#  define MEM_DEV "/dev/mem"
+#endif
+
+static int fd_mem = -1;
+static int fd_mem_cached = -1;
+
+/* For MMIO access. Must be uncached, doesn't make sense to restrict to ro. */
+static void *sys_physmap_rw_uncached(unsigned long phys_addr, size_t len)
+{
+	void *virt_addr;
+
+	if (-1 == fd_mem) {
+		/* Open the memory device UNCACHED. Important for MMIO. */
+		if (-1 == (fd_mem = open(MEM_DEV, O_RDWR | O_SYNC))) {
+			perror("Critical error: open(" MEM_DEV ")");
+			exit(2);
+		}
+	}
+
+	virt_addr = mmap(NULL, len, PROT_WRITE | PROT_READ, MAP_SHARED,
+			 fd_mem, (off_t)phys_addr);
+	return MAP_FAILED == virt_addr ? ERROR_PTR : virt_addr;
+}
+
+/* For reading DMI/coreboot/whatever tables. We should never write, and we
+ * do not care about caching.
+ */
+static void *sys_physmap_ro_cached(unsigned long phys_addr, size_t len)
+{
+	void *virt_addr;
+
+	if (-1 == fd_mem_cached) {
+		/* Open the memory device CACHED. */
+		if (-1 == (fd_mem_cached = open(MEM_DEV, O_RDWR))) {
+			msg_perr("Critical error: open(" MEM_DEV "): %s", strerror(errno));
+			exit(2);
+		}
+	}
+
+	virt_addr = mmap(NULL, len, PROT_READ, MAP_SHARED,
+			 fd_mem_cached, (off_t)phys_addr);
+	return MAP_FAILED == virt_addr ? ERROR_PTR : virt_addr;
+}
+
+void physunmap(void *virt_addr, size_t len)
+{
+	if (len == 0) {
+		msg_pspew("Not unmapping zero size at %p\n", virt_addr);
+		return;
+	}
+		
+	munmap(virt_addr, len);
+}
+#endif
+
+#define PHYSMAP_NOFAIL	0
+#define PHYSMAP_MAYFAIL	1
+#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)
+{
+	void *virt_addr;
+
+	if (len == 0) {
+		msg_pspew("Not mapping %s, zero size at 0x%08lx.\n",
+			     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);
+	}
+
+	if ((getpagesize() - 1) & phys_addr) {
+		msg_perr("Mapping %s, 0x%lx bytes at unaligned 0x%08lx.\n",
+			descr, (unsigned long)len, phys_addr);
+	}
+
+	if (readonly) {
+		virt_addr = sys_physmap_ro_cached(phys_addr, len);
+	} 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(MEM_DEV " mmap failed");
+#ifdef __linux__
+		if (EINVAL == errno) {
+			msg_perr("In Linux this error can be caused by the CONFIG_NONPROMISC_DEVMEM (<2.6.27),\n");
+			msg_perr("CONFIG_STRICT_DEVMEM (>=2.6.27) and CONFIG_X86_PAT kernel options.\n");
+			msg_perr("Please check if either is enabled in your kernel before reporting a failure.\n");
+			msg_perr("You can override CONFIG_X86_PAT at boot with the nopat kernel parameter but\n");
+			msg_perr("disabling the other option unfortunately requires a kernel recompile. Sorry!\n");
+		}
+#elif defined (__OpenBSD__)
+		msg_perr("Please set securelevel=-1 in /etc/rc.securelevel "
+			   "and reboot, or reboot into \n");
+		msg_perr("single user mode.\n");
+#endif
+		if (!mayfail)
+			return (void *)-1;
+	}
+
+	return virt_addr;
+}
+
+void *physmap(const char *descr, unsigned long phys_addr, size_t len)
+{
+	return physmap_common(descr, phys_addr, len, PHYSMAP_NOFAIL, PHYSMAP_RW);
+}
+
+void *physmap_try_ro(const char *descr, unsigned long phys_addr, size_t len)
+{
+	return physmap_common(descr, phys_addr, len, PHYSMAP_MAYFAIL, PHYSMAP_RO);
+}
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#ifdef __linux__
+/*
+ * 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
+ * be loaded to access these instructions from user space using device
+ * /dev/cpu/0/msr.
+ */
+
+static int fd_msr = -1;
+
+msr_t rdmsr(int addr)
+{
+	uint32_t buf[2];
+	msr_t msr = { 0xffffffff, 0xffffffff };
+
+	if (lseek(fd_msr, (off_t) addr, SEEK_SET) == -1) {
+		perror("Could not lseek() to MSR");
+		close(fd_msr);
+		exit(1);
+	}
+
+	if (read(fd_msr, buf, 8) == 8) {
+		msr.lo = buf[0];
+		msr.hi = buf[1];
+
+		return msr;
+	}
+
+	if (errno != EIO) {
+		// A severe error.
+		perror("Could not read() MSR");
+		close(fd_msr);
+		exit(1);
+	}
+
+	return msr;
+}
+
+int wrmsr(int addr, msr_t msr)
+{
+	uint32_t buf[2];
+	buf[0] = msr.lo;
+	buf[1] = msr.hi;
+
+	if (lseek(fd_msr, (off_t) addr, SEEK_SET) == -1) {
+		perror("Could not lseek() to MSR");
+		close(fd_msr);
+		exit(1);
+	}
+
+	if (write(fd_msr, buf, 8) != 8 && errno != EIO) {
+		perror("Could not write() MSR");
+		close(fd_msr);
+		exit(1);
+	}
+
+	/* some MSRs must not be written */
+	if (errno == EIO)
+		return -1;
+
+	return 0;
+}
+
+int setup_cpu_msr(int cpu)
+{
+	char msrfilename[64];
+	memset(msrfilename, 0, sizeof(msrfilename));
+	snprintf(msrfilename, sizeof(msrfilename), "/dev/cpu/%d/msr", cpu);
+
+	if (fd_msr != -1) {
+		msg_pinfo("MSR was already initialized\n");
+		return -1;
+	}
+
+	fd_msr = open(msrfilename, O_RDWR);
+
+	if (fd_msr < 0) {
+		perror("Error while opening /dev/cpu/0/msr");
+		msg_pinfo("Did you run 'modprobe msr'?\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+void cleanup_cpu_msr(void)
+{
+	if (fd_msr == -1) {
+		msg_pinfo("No MSR initialized.\n");
+		return;
+	}
+
+	close(fd_msr);
+
+	/* Clear MSR file descriptor */
+	fd_msr = -1;
+}
+#else
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#include <sys/ioctl.h>
+
+typedef struct {
+	int msr;
+	uint64_t data;
+} cpu_msr_args_t;
+#define CPU_RDMSR _IOWR('c', 1, cpu_msr_args_t)
+#define CPU_WRMSR _IOWR('c', 2, cpu_msr_args_t)
+
+static int fd_msr = -1;
+
+msr_t rdmsr(int addr)
+{
+	cpu_msr_args_t args;
+
+	msr_t msr = { 0xffffffff, 0xffffffff };
+
+	args.msr = addr;
+
+	if (ioctl(fd_msr, CPU_RDMSR, &args) < 0) {
+		perror("CPU_RDMSR");
+		close(fd_msr);
+		exit(1);
+	}
+
+	msr.lo = args.data & 0xffffffff;
+	msr.hi = args.data >> 32;
+
+	return msr;
+}
+
+int wrmsr(int addr, msr_t msr)
+{
+	return 0;
+}
+
+int setup_cpu_msr(int cpu)
+{
+	char msrfilename[64];
+	memset(msrfilename, 0, sizeof(msrfilename));
+	snprintf(msrfilename, sizeof(msrfilename), "/dev/cpu%d", cpu);
+
+	if (fd_msr != -1) {
+		msg_pinfo("MSR was already initialized\n");
+		return -1;
+	}
+
+	fd_msr = open(msrfilename, O_RDWR);
+
+	if (fd_msr < 0) {
+		perror("Error while opening /dev/cpu0");
+		msg_pinfo("Did you install ports/sysutils/devcpu?\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+void cleanup_cpu_msr(void)
+{
+	if (fd_msr == -1) {
+		msg_pinfo("No MSR initialized.\n");
+		return;
+	}
+
+	close(fd_msr);
+
+	/* Clear MSR file descriptor */
+	fd_msr = -1;
+}
+
+#else
+
+#ifdef __DARWIN__
+int setup_cpu_msr(int cpu)
+{
+	// Always succeed for now
+	return 0;
+}
+
+void cleanup_cpu_msr(void)
+{
+	// Nothing, yet.
+}
+#elif defined(__LIBPAYLOAD__)
+msr_t libpayload_rdmsr(int addr)
+{
+	msr_t msr;
+	unsigned long long val = 0;
+	msr.lo = val & 0xffffffff;
+	msr.hi = val >> 32;
+	return msr;
+}
+
+int libpayload_wrmsr(int addr, msr_t msr)
+{
+	//_wrmsr(addr, msr.lo | ((unsigned long long)msr.hi << 32));
+	return 0;
+}
+#else
+msr_t rdmsr(int addr)
+{
+	msr_t ret = { 0xffffffff, 0xffffffff };
+
+	return ret;
+}
+
+int wrmsr(int addr, msr_t msr)
+{
+	return -1;
+}
+
+int setup_cpu_msr(int cpu)
+{
+	msg_pinfo("No MSR support for your OS yet.\n");
+	return -1;
+}
+
+void cleanup_cpu_msr(void)
+{
+	// Nothing, yet.
+}
+#endif
+#endif
+#endif
+#else
+/* Does MSR exist on non-x86 architectures? */
+#endif
diff --git a/src/mainboard/msi/ms6119/f/programmer.c b/src/mainboard/msi/ms6119/f/programmer.c
new file mode 100644
index 0000000..a9b88d0
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/programmer.c
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include "flash.h"
+
+/* No-op shutdown() for programmers which don't need special handling */
+int noop_shutdown(void);
+int noop_shutdown(void)
+{
+	return 0;
+}
+
+/* Fallback map() for programmers which don't need special handling */
+void *fallback_map(const char *descr, unsigned long phys_addr, size_t len);
+void *fallback_map(const char *descr, unsigned long phys_addr, size_t len)
+{
+	/* FIXME: Should return phys_addr. */
+	return NULL;
+}
+
+/* No-op/fallback unmap() for programmers which don't need special handling */
+void fallback_unmap(void *virt_addr, size_t len);
+void fallback_unmap(void *virt_addr, size_t len)
+{
+}
+
+/* No-op chip_writeb() for drivers not supporting addr/data pair accesses */
+uint8_t noop_chip_readb(const chipaddr addr);
+uint8_t noop_chip_readb(const chipaddr addr)
+{
+	return 0xff;
+}
+
+/* No-op chip_writeb() for drivers not supporting addr/data pair accesses */
+void noop_chip_writeb(uint8_t val, chipaddr addr);
+void noop_chip_writeb(uint8_t val, chipaddr addr)
+{
+}
+
+/* Little-endian fallback for drivers not supporting 16 bit accesses */
+void fallback_chip_writew(uint16_t val, chipaddr addr);
+void fallback_chip_writew(uint16_t val, chipaddr addr)
+{
+	chip_writeb(val & 0xff, addr);
+	chip_writeb((val >> 8) & 0xff, addr + 1);
+}
+
+/* Little-endian fallback for drivers not supporting 16 bit accesses */
+uint16_t fallback_chip_readw(const chipaddr addr);
+uint16_t fallback_chip_readw(const chipaddr addr)
+{
+	uint16_t val;
+	val = chip_readb(addr);
+	val |= chip_readb(addr + 1) << 8;
+	return val;
+}
+
+/* Little-endian fallback for drivers not supporting 32 bit accesses */
+void fallback_chip_writel(uint32_t val, chipaddr addr);
+void fallback_chip_writel(uint32_t val, chipaddr addr)
+{
+	chip_writew(val & 0xffff, addr);
+	chip_writew((val >> 16) & 0xffff, addr + 2);
+}
+
+/* Little-endian fallback for drivers not supporting 32 bit accesses */
+uint32_t fallback_chip_readl(const chipaddr addr);
+uint32_t fallback_chip_readl(const chipaddr addr)
+{
+	uint32_t val;
+	val = chip_readw(addr);
+	val |= chip_readw(addr + 2) << 16;
+	return val;
+}
+void fallback_chip_writen(uint8_t *buf, chipaddr addr, size_t len);
+void fallback_chip_writen(uint8_t *buf, chipaddr addr, size_t len)
+{
+	size_t i;
+	for (i = 0; i < len; i++)
+		chip_writeb(buf[i], addr + i);
+	return;
+}
+void fallback_chip_readn(uint8_t *buf, chipaddr addr, size_t len);
+void fallback_chip_readn(uint8_t *buf, chipaddr addr, size_t len)
+{
+	size_t i;
+	for (i = 0; i < len; i++)
+		buf[i] = chip_readb(addr + i);
+	return;
+}
diff --git a/src/mainboard/msi/ms6119/f/programmer.h b/src/mainboard/msi/ms6119/f/programmer.h
new file mode 100644
index 0000000..bdf1da6
--- /dev/null
+++ b/src/mainboard/msi/ms6119/f/programmer.h
@@ -0,0 +1,608 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2000 Ronald G. Minnich <rminnich@gmail.com>
+ * Copyright (C) 2005-2009 coresystems GmbH
+ * Copyright (C) 2006-2009 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#ifndef __PROGRAMMER_H__
+#define __PROGRAMMER_H__ 1
+
+#define NEED_PCI	1
+
+enum programmer {
+#if CONFIG_INTERNAL == 1
+	PROGRAMMER_INTERNAL,
+#endif
+#if CONFIG_DUMMY == 1
+	PROGRAMMER_DUMMY,
+#endif
+#if CONFIG_NIC3COM == 1
+	PROGRAMMER_NIC3COM,
+#endif
+#if CONFIG_NICREALTEK == 1
+	PROGRAMMER_NICREALTEK,
+#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
+	PROGRAMMER_INVALID /* This must always be the last entry. */
+};
+
+extern enum programmer programmer;
+
+struct programmer_entry {
+	const char *vendor;
+	const char *name;
+
+	int (*init) (void);
+
+	void * (*map_flash_region) (const char *descr, unsigned long phys_addr,
+				    size_t len);
+	void (*unmap_flash_region) (void *virt_addr, size_t len);
+
+	void (*chip_writeb) (uint8_t val, chipaddr addr);
+	void (*chip_writew) (uint16_t val, chipaddr addr);
+	void (*chip_writel) (uint32_t val, chipaddr addr);
+	void (*chip_writen) (uint8_t *buf, chipaddr addr, size_t len);
+	uint8_t (*chip_readb) (const chipaddr addr);
+	uint16_t (*chip_readw) (const chipaddr addr);
+	uint32_t (*chip_readl) (const chipaddr addr);
+	void (*chip_readn) (uint8_t *buf, const chipaddr addr, size_t len);
+	void (*delay) (int usecs);
+};
+
+extern const struct programmer_entry programmer_table[];
+
+int programmer_init(char *param);
+int programmer_shutdown(void);
+
+enum bitbang_spi_master_type {
+	BITBANG_SPI_INVALID	= 0, /* This must always be the first entry. */
+#if CONFIG_RAYER_SPI == 1
+	BITBANG_SPI_MASTER_RAYER,
+#endif
+#if CONFIG_NICINTEL_SPI == 1
+	BITBANG_SPI_MASTER_NICINTEL,
+#endif
+#if CONFIG_INTERNAL == 1
+#if defined(__i386__) || defined(__x86_64__)
+	BITBANG_SPI_MASTER_MCP,
+#endif
+#endif
+#if CONFIG_OGP_SPI == 1
+	BITBANG_SPI_MASTER_OGP,
+#endif
+};
+
+struct bitbang_spi_master {
+	enum bitbang_spi_master_type type;
+
+	/* Note that CS# is active low, so val=0 means the chip is active. */
+	void (*set_cs) (int val);
+	void (*set_sck) (int val);
+	void (*set_mosi) (int val);
+	int (*get_miso) (void);
+	void (*request_bus) (void);
+	void (*release_bus) (void);
+};
+
+#if CONFIG_INTERNAL == 1
+struct penable {
+	uint16_t vendor_id;
+	uint16_t device_id;
+	int status; /* OK=0 and NT=1 are defines only. Beware! */
+	const char *vendor_name;
+	const char *device_name;
+	int (*doit) (struct pci_dev *dev, const char *name);
+};
+
+extern const struct penable chipset_enables[];
+
+enum board_match_phase {
+	P1,
+	P2,
+	P3
+};
+
+struct board_pciid_enable {
+	/* Any device, but make it sensible, like the ISA bridge. */
+	uint16_t first_vendor;
+	uint16_t first_device;
+	uint16_t first_card_vendor;
+	uint16_t first_card_device;
+
+	/* Any device, but make it sensible, like
+	 * the host bridge. May be NULL.
+	 */
+	uint16_t second_vendor;
+	uint16_t second_device;
+	uint16_t second_card_vendor;
+	uint16_t second_card_device;
+
+	/* Pattern to match DMI entries. May be NULL. */
+	const char *dmi_pattern;
+
+	/* The vendor / part name from the coreboot table. May be NULL. */
+	const char *lb_vendor;
+	const char *lb_part;
+
+	enum board_match_phase phase;
+
+	const char *vendor_name;
+	const char *board_name;
+
+	int max_rom_decode_parallel;
+	int status;
+	int (*enable) (void); /* May be NULL. */
+};
+
+extern const struct board_pciid_enable board_pciid_enables[];
+
+struct board_info {
+	const char *vendor;
+	const char *name;
+	const int working;
+#ifdef CONFIG_PRINT_WIKI
+	const char *url;
+	const char *note;
+#endif
+};
+
+extern const struct board_info boards_known[];
+extern const struct board_info laptops_known[];
+#endif
+
+/* udelay.c */
+void myusec_delay(int usecs);
+void myusec_calibrate_delay(void);
+void internal_delay(int usecs);
+
+#if NEED_PCI == 1
+/* pcidev.c */
+extern uint32_t io_base_addr;
+extern struct pci_access *pacc;
+extern struct pci_dev *pcidev_dev;
+struct pcidev_status {
+	uint16_t vendor_id;
+	uint16_t device_id;
+	int status;
+	const char *vendor_name;
+	const char *device_name;
+};
+uintptr_t pcidev_validate(struct pci_dev *dev, int bar, const struct pcidev_status *devs);
+uintptr_t pcidev_init(int bar, const struct pcidev_status *devs);
+/* rpci_write_* are reversible writes. The original PCI config space register
+ * contents will be restored on shutdown.
+ */
+int rpci_write_byte(struct pci_dev *dev, int reg, uint8_t data);
+int rpci_write_word(struct pci_dev *dev, int reg, uint16_t data);
+int rpci_write_long(struct pci_dev *dev, int reg, uint32_t data);
+#endif
+
+/* print.c */
+#if CONFIG_NIC3COM+CONFIG_NICREALTEK+CONFIG_NICNATSEMI+CONFIG_GFXNVIDIA+CONFIG_DRKAISER+CONFIG_SATASII+CONFIG_ATAHPT+CONFIG_NICINTEL+CONFIG_NICINTEL_SPI+CONFIG_OGP_SPI+CONFIG_SATAMV >= 1
+void print_supported_pcidevs(const struct pcidev_status *devs);
+#endif
+
+#if CONFIG_INTERNAL == 1
+/* board_enable.c */
+void w836xx_ext_enter(uint16_t port);
+void w836xx_ext_leave(uint16_t port);
+int it8705f_write_enable(uint8_t port);
+uint8_t sio_read(uint16_t port, uint8_t reg);
+void sio_write(uint16_t port, uint8_t reg, uint8_t data);
+void sio_mask(uint16_t port, uint8_t reg, uint8_t data, uint8_t mask);
+void board_handle_before_superio(void);
+void board_handle_before_laptop(void);
+int board_flash_enable(const char *vendor, const char *part);
+
+/* chipset_enable.c */
+int chipset_flash_enable(void);
+
+/* processor_enable.c */
+int processor_flash_enable(void);
+#endif
+
+/* physmap.c */
+void *physmap(const char *descr, unsigned long phys_addr, size_t len);
+void *physmap_try_ro(const char *descr, unsigned long phys_addr, size_t len);
+void physunmap(void *virt_addr, size_t len);
+#if CONFIG_INTERNAL == 1
+int setup_cpu_msr(int cpu);
+void cleanup_cpu_msr(void);
+
+/* cbtable.c */
+void lb_vendor_dev_from_string(char *boardstring);
+int coreboot_init(void);
+extern char *lb_part, *lb_vendor;
+extern int partvendor_from_cbtable;
+
+/* dmi.c */
+extern int has_dmi_support;
+void dmi_init(void);
+int dmi_match(const char *pattern);
+
+/* internal.c */
+struct superio {
+	uint16_t vendor;
+	uint16_t port;
+	uint16_t model;
+};
+extern struct superio superios[];
+extern int superio_count;
+#define SUPERIO_VENDOR_NONE	0x0
+#define SUPERIO_VENDOR_ITE	0x1
+#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(uint16_t vendor, uint16_t device);
+struct pci_dev *pci_card_find(uint16_t vendor, uint16_t device,
+			      uint16_t card_vendor, uint16_t card_device);
+#endif
+void get_io_perms(void);
+void release_io_perms(void);
+#if CONFIG_INTERNAL == 1
+extern int is_laptop;
+extern int laptop_ok;
+extern int force_boardenable;
+extern int force_boardmismatch;
+void probe_superio(void);
+int register_superio(struct superio s);
+int internal_init(void);
+void internal_chip_writeb(uint8_t val, chipaddr addr);
+void internal_chip_writew(uint16_t val, chipaddr addr);
+void internal_chip_writel(uint32_t val, chipaddr addr);
+uint8_t internal_chip_readb(const chipaddr addr);
+uint16_t internal_chip_readw(const chipaddr addr);
+uint32_t internal_chip_readl(const chipaddr addr);
+void internal_chip_readn(uint8_t *buf, const chipaddr addr, size_t len);
+#endif
+
+/* hwaccess.c */
+void mmio_writeb(uint8_t val, void *addr);
+void mmio_writew(uint16_t val, void *addr);
+void mmio_writel(uint32_t val, void *addr);
+uint8_t mmio_readb(void *addr);
+uint16_t mmio_readw(void *addr);
+uint32_t mmio_readl(void *addr);
+void mmio_le_writeb(uint8_t val, void *addr);
+void mmio_le_writew(uint16_t val, void *addr);
+void mmio_le_writel(uint32_t val, void *addr);
+uint8_t mmio_le_readb(void *addr);
+uint16_t mmio_le_readw(void *addr);
+uint32_t mmio_le_readl(void *addr);
+#define pci_mmio_writeb mmio_le_writeb
+#define pci_mmio_writew mmio_le_writew
+#define pci_mmio_writel mmio_le_writel
+#define pci_mmio_readb mmio_le_readb
+#define pci_mmio_readw mmio_le_readw
+#define pci_mmio_readl mmio_le_readl
+void rmmio_writeb(uint8_t val, void *addr);
+void rmmio_writew(uint16_t val, void *addr);
+void rmmio_writel(uint32_t val, void *addr);
+void rmmio_le_writeb(uint8_t val, void *addr);
+void rmmio_le_writew(uint16_t val, void *addr);
+void rmmio_le_writel(uint32_t val, void *addr);
+#define pci_rmmio_writeb rmmio_le_writeb
+#define pci_rmmio_writew rmmio_le_writew
+#define pci_rmmio_writel rmmio_le_writel
+void rmmio_valb(void *addr);
+void rmmio_valw(void *addr);
+void rmmio_vall(void *addr);
+
+/* programmer.c */
+int noop_shutdown(void);
+void *fallback_map(const char *descr, unsigned long phys_addr, size_t len);
+void fallback_unmap(void *virt_addr, size_t len);
+uint8_t noop_chip_readb(const chipaddr addr);
+void noop_chip_writeb(uint8_t val, chipaddr addr);
+void fallback_chip_writew(uint16_t val, chipaddr addr);
+void fallback_chip_writel(uint32_t val, chipaddr addr);
+void fallback_chip_writen(uint8_t *buf, chipaddr addr, size_t len);
+uint16_t fallback_chip_readw(const chipaddr addr);
+uint32_t fallback_chip_readl(const chipaddr addr);
+void fallback_chip_readn(uint8_t *buf, const chipaddr addr, size_t len);
+
+/* dummyflasher.c */
+#if CONFIG_DUMMY == 1
+int dummy_init(void);
+void *dummy_map(const char *descr, unsigned long phys_addr, size_t len);
+void dummy_unmap(void *virt_addr, size_t len);
+void dummy_chip_writeb(uint8_t val, chipaddr addr);
+void dummy_chip_writew(uint16_t val, chipaddr addr);
+void dummy_chip_writel(uint32_t val, chipaddr addr);
+void dummy_chip_writen(uint8_t *buf, chipaddr addr, size_t len);
+uint8_t dummy_chip_readb(const chipaddr addr);
+uint16_t dummy_chip_readw(const chipaddr addr);
+uint32_t dummy_chip_readl(const chipaddr addr);
+void dummy_chip_readn(uint8_t *buf, const chipaddr addr, size_t len);
+#endif
+
+/* nic3com.c */
+#if CONFIG_NIC3COM == 1
+int nic3com_init(void);
+void nic3com_chip_writeb(uint8_t val, chipaddr addr);
+uint8_t nic3com_chip_readb(const chipaddr addr);
+extern const struct pcidev_status nics_3com[];
+#endif
+
+/* gfxnvidia.c */
+#if CONFIG_GFXNVIDIA == 1
+int gfxnvidia_init(void);
+void gfxnvidia_chip_writeb(uint8_t val, chipaddr addr);
+uint8_t gfxnvidia_chip_readb(const chipaddr addr);
+extern const struct pcidev_status gfx_nvidia[];
+#endif
+
+/* drkaiser.c */
+#if CONFIG_DRKAISER == 1
+int drkaiser_init(void);
+void drkaiser_chip_writeb(uint8_t val, chipaddr addr);
+uint8_t drkaiser_chip_readb(const chipaddr addr);
+extern const struct pcidev_status drkaiser_pcidev[];
+#endif
+
+/* nicrealtek.c */
+#if CONFIG_NICREALTEK == 1
+int nicrealtek_init(void);
+void nicrealtek_chip_writeb(uint8_t val, chipaddr addr);
+uint8_t nicrealtek_chip_readb(const chipaddr addr);
+extern const struct pcidev_status nics_realtek[];
+#endif
+
+/* nicnatsemi.c */
+#if CONFIG_NICNATSEMI == 1
+int nicnatsemi_init(void);
+void nicnatsemi_chip_writeb(uint8_t val, chipaddr addr);
+uint8_t nicnatsemi_chip_readb(const chipaddr addr);
+extern const struct pcidev_status nics_natsemi[];
+#endif
+
+/* nicintel.c */
+#if CONFIG_NICINTEL == 1
+int nicintel_init(void);
+void nicintel_chip_writeb(uint8_t val, chipaddr addr);
+uint8_t nicintel_chip_readb(const chipaddr addr);
+extern const struct pcidev_status nics_intel[];
+#endif
+
+/* 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
+
+/* ogp_spi.c */
+#if CONFIG_OGP_SPI == 1
+int ogp_spi_init(void);
+extern const struct pcidev_status ogp_spi[];
+#endif
+
+/* satamv.c */
+#if CONFIG_SATAMV == 1
+int satamv_init(void);
+void satamv_chip_writeb(uint8_t val, chipaddr addr);
+uint8_t satamv_chip_readb(const chipaddr addr);
+extern const struct pcidev_status satas_mv[];
+#endif
+
+/* satasii.c */
+#if CONFIG_SATASII == 1
+int satasii_init(void);
+void satasii_chip_writeb(uint8_t val, chipaddr addr);
+uint8_t satasii_chip_readb(const chipaddr addr);
+extern const struct pcidev_status satas_sii[];
+#endif
+
+/* atahpt.c */
+#if CONFIG_ATAHPT == 1
+int atahpt_init(void);
+void atahpt_chip_writeb(uint8_t val, chipaddr addr);
+uint8_t atahpt_chip_readb(const chipaddr addr);
+extern const struct pcidev_status ata_hpt[];
+#endif
+
+/* ft2232_spi.c */
+#if CONFIG_FT2232_SPI == 1
+struct usbdev_status {
+	uint16_t vendor_id;
+	uint16_t device_id;
+	int status;
+	const char *vendor_name;
+	const char *device_name;
+};
+int ft2232_spi_init(void);
+extern const struct usbdev_status devs_ft2232spi[];
+void print_supported_usbdevs(const struct usbdev_status *devs);
+#endif
+
+/* rayer_spi.c */
+#if CONFIG_RAYER_SPI == 1
+int rayer_spi_init(void);
+#endif
+
+/* bitbang_spi.c */
+int bitbang_spi_init(const struct bitbang_spi_master *master, int halfperiod);
+int bitbang_spi_shutdown(const struct bitbang_spi_master *master);
+
+/* buspirate_spi.c */
+#if CONFIG_BUSPIRATE_SPI == 1
+int buspirate_spi_init(void);
+#endif
+
+/* dediprog.c */
+#if CONFIG_DEDIPROG == 1
+int dediprog_init(void);
+#endif
+
+/* flashrom.c */
+struct decode_sizes {
+	uint32_t parallel;
+	uint32_t lpc;
+	uint32_t fwh;
+	uint32_t spi;
+};
+extern struct decode_sizes max_rom_decode;
+extern int programmer_may_write;
+extern unsigned long flashbase;
+void check_chip_supported(const struct flashchip *flash);
+int check_max_decode(enum chipbustype buses, uint32_t size);
+char *extract_programmer_param(const char *param_name);
+
+/* layout.c */
+int show_id(uint8_t *bios, int size, int force);
+
+/* spi.c */
+enum spi_controller {
+	SPI_CONTROLLER_NONE,
+#if CONFIG_INTERNAL == 1
+#if defined(__i386__) || defined(__x86_64__)
+	SPI_CONTROLLER_ICH7,
+	SPI_CONTROLLER_ICH9,
+	SPI_CONTROLLER_IT85XX,
+	SPI_CONTROLLER_IT87XX,
+	SPI_CONTROLLER_SB600,
+	SPI_CONTROLLER_VIA,
+	SPI_CONTROLLER_WBSIO,
+#endif
+#endif
+#if CONFIG_FT2232_SPI == 1
+	SPI_CONTROLLER_FT2232,
+#endif
+#if CONFIG_DUMMY == 1
+	SPI_CONTROLLER_DUMMY,
+#endif
+#if CONFIG_BUSPIRATE_SPI == 1
+	SPI_CONTROLLER_BUSPIRATE,
+#endif
+#if CONFIG_DEDIPROG == 1
+	SPI_CONTROLLER_DEDIPROG,
+#endif
+#if CONFIG_OGP_SPI == 1 || CONFIG_NICINTEL_SPI == 1 || CONFIG_RAYER_SPI == 1 || (CONFIG_INTERNAL == 1 && (defined(__i386__) || defined(__x86_64__)))
+	SPI_CONTROLLER_BITBANG,
+#endif
+};
+extern const int spi_programmer_count;
+
+#define MAX_DATA_UNSPECIFIED 0
+#define MAX_DATA_READ_UNLIMITED 64 * 1024
+#define MAX_DATA_WRITE_UNLIMITED 256
+struct spi_programmer {
+	enum spi_controller type;
+	int max_data_read;
+	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);
+};
+
+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);
+void register_spi_programmer(const struct spi_programmer *programmer);
+
+/* ichspi.c */
+#if CONFIG_INTERNAL == 1
+extern uint32_t ichspi_bbar;
+int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
+		    int ich_generation);
+int via_init_spi(struct pci_dev *dev);
+
+/* it85spi.c */
+int it85xx_spi_init(struct superio s);
+
+/* it87spi.c */
+void enter_conf_mode_ite(uint16_t port);
+void exit_conf_mode_ite(uint16_t port);
+void probe_superio_ite(void);
+int init_superio_ite(void);
+
+/* mcp6x_spi.c */
+int mcp6x_spi_init(int want_spi);
+
+/* sb600spi.c */
+int sb600_probe_spi(struct pci_dev *dev);
+
+/* wbsio_spi.c */
+int wbsio_check_for_spi(void);
+#endif
+
+/* 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);
+#endif
+
+/* serial.c */
+
+#endif				/* !__PROGRAMMER_H__ */
diff --git a/src/mainboard/msi/ms6119/romstage.c b/src/mainboard/msi/ms6119/romstage.c
index fba2593..a5a9844 100644
--- a/src/mainboard/msi/ms6119/romstage.c
+++ b/src/mainboard/msi/ms6119/romstage.c
@@ -33,6 +33,8 @@
 #include "cpu/x86/bist.h"
 #include "superio/winbond/w83977tf/early_serial.c"
 #include <lib.h>
+#include "f/f.h"
+
 
 #define SERIAL_DEV PNP_DEV(0x3f0, W83977TF_SP1)
 
@@ -41,12 +43,17 @@ int spd_read_byte(unsigned int device, unsigned int address)
 	return smbus_read_byte(device, address);
 }
 
+int main_flashrom(int argc);
+
+
 void main(unsigned long bist)
 {
 	w83977tf_enable_serial(SERIAL_DEV, CONFIG_TTYS0_BASE);
 	console_init();
 	report_bist_failure(bist);
 
+	//main_fl();
+
 	enable_smbus();
 	dump_spd_registers();
 	sdram_set_registers();
-- 
1.7.6

