Add proper workaround for 3COM 3C90xB cards, which need special fixups (the 3C90xC ones don't)

This is tested on hardware.

Also, add initial support for the Atmel AT29C010A chip (which I inserted
in a 3COM 3C90xB card for testing). It can be detected, read works,
erase works, but write will need some additional code (will post in
another patch later).

Corresponding to flashrom svn r520.

Signed-off-by: Uwe Hermann <uwe@hermann-uwe.de>
Acked-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>
diff --git a/flash.h b/flash.h
index b980cba..2a9b9bb 100644
--- a/flash.h
+++ b/flash.h
@@ -193,6 +193,7 @@
 #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_PREW	(TEST_OK_PROBE|TEST_OK_READ|TEST_OK_ERASE|TEST_OK_WRITE)
 #define TEST_OK_MASK	0x0f
 
@@ -258,6 +259,7 @@
 #define AT_26DF321		0x4700	/* also 25DF321 */
 #define AT_26F004		0x0400
 #define AT_29C040A		0xA4
+#define AT_29C010A		0xD5	
 #define AT_29C020		0xDA
 #define AT_45BR3214B		/* No ID available */
 #define AT_45CS1282		0x2920
@@ -559,6 +561,7 @@
 extern uint32_t io_base_addr;
 extern struct pci_access *pacc;
 extern struct pci_filter filter;
+extern struct pci_dev *pcidev_dev;
 struct pcidev_status {
 	uint16_t vendor_id;
 	uint16_t device_id;
diff --git a/flashchips.c b/flashchips.c
index eb241a9..36307c1 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -376,6 +376,20 @@
 
 	{
 		.vendor		= "Atmel",
+		.name		= "AT29C010A",
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= AT_29C010A,
+		.total_size	= 128,
+		.page_size	= 128,
+		.tested		= TEST_OK_PRE,
+		.probe		= probe_jedec,
+		.erase		= erase_chip_jedec,
+		.write		= write_jedec,	/* FIXME */
+		.read		= read_memmapped,
+	},
+
+	{
+		.vendor		= "Atmel",
 		.name		= "AT29C020",
 		.manufacture_id	= ATMEL_ID,
 		.model_id	= AT_29C020,
diff --git a/nic3com.c b/nic3com.c
index f5d0aa1..4e3e475 100644
--- a/nic3com.c
+++ b/nic3com.c
@@ -29,13 +29,17 @@
 #define BIOS_ROM_ADDR		0x04
 #define BIOS_ROM_DATA		0x08
 #define INT_STATUS		0x0e
+#define INTERNAL_CONFIG		0x00
 #define SELECT_REG_WINDOW	0x800
 
 #define PCI_VENDOR_ID_3COM	0x10b7
 
+uint32_t internal_conf;
+uint16_t id;
+
 struct pcidev_status nics_3com[] = {
 	/* 3C90xB */
-	{0x10b7, 0x9055, PCI_NT, "3COM", "3C90xB: PCI 10/100 Mbps; shared 10BASE-T/100BASE-TX"},
+	{0x10b7, 0x9055, PCI_OK, "3COM", "3C90xB: PCI 10/100 Mbps; shared 10BASE-T/100BASE-TX"},
 	{0x10b7, 0x9001, PCI_NT, "3COM", "3C90xB: PCI 10/100 Mbps; shared 10BASE-T/100BASE-T4" },
 	{0x10b7, 0x9004, PCI_NT, "3COM", "3C90xB: PCI 10BASE-T (TPO)" },
 	{0x10b7, 0x9005, PCI_NT, "3COM", "3C90xB: PCI 10BASE-T/10BASE2/AUI (COMBO)" },
@@ -57,6 +61,18 @@
 	get_io_perms();
 
 	io_base_addr = pcidev_init(PCI_VENDOR_ID_3COM, nics_3com);
+	id = pcidev_dev->device_id;
+
+	/* 3COM 3C90xB cards need a special fixup. */
+	if (id == 0x9055 || id == 0x9001 || id == 0x9004 || id == 0x9005
+	    || id == 0x9006 || id == 0x900a || id == 0x905a) {
+		/* Select register window 3 and save the receiver status. */
+		OUTW(SELECT_REG_WINDOW + 3, io_base_addr + INT_STATUS);
+		internal_conf = INL(io_base_addr + INTERNAL_CONFIG);
+
+		/* Set receiver type to MII for full BIOS ROM access. */
+		OUTL((internal_conf & 0xf00fffff) | 0x00600000, io_base_addr);
+	}
 
 	/*
 	 * The lowest 16 bytes of the I/O mapped register space of (most) 3COM
@@ -70,6 +86,14 @@
 
 int nic3com_shutdown(void)
 {
+	/* 3COM 3C90xB cards need a special fixup. */
+	if (id == 0x9055 || id == 0x9001 || id == 0x9004 || id == 0x9005
+	    || id == 0x9006 || id == 0x900a || id == 0x905a) {
+		/* Select register window 3 and restore the receiver status. */
+		OUTW(SELECT_REG_WINDOW + 3, io_base_addr + INT_STATUS);
+		OUTL(internal_conf, io_base_addr + INTERNAL_CONFIG);
+	}
+
 	free(pcidev_bdf);
 	pci_cleanup(pacc);
 #if defined(__FreeBSD__) || defined(__DragonFly__)
diff --git a/pcidev.c b/pcidev.c
index f07fe80..41b5f25 100644
--- a/pcidev.c
+++ b/pcidev.c
@@ -32,6 +32,7 @@
 struct pci_access *pacc;
 struct pci_filter filter;
 char *pcidev_bdf = NULL;
+struct pci_dev *pcidev_dev = NULL;
 
 uint32_t pcidev_validate(struct pci_dev *dev, struct pcidev_status *devs)
 {
@@ -67,7 +68,7 @@
 	struct pci_dev *dev;
 	char *msg = NULL;
 	int found = 0;
-	uint32_t addr = 0;
+	uint32_t addr = 0, curaddr = 0;
 
 	pacc = pci_alloc();     /* Get the pci_access structure */
 	pci_init(pacc);         /* Initialize the PCI library */
@@ -85,8 +86,11 @@
 
 	for (dev = pacc->devices; dev; dev = dev->next) {
 		if (pci_filter_match(&filter, dev)) {
-			if ((addr = pcidev_validate(dev, devs)) != 0)
+			if ((addr = pcidev_validate(dev, devs)) != 0) {
+				curaddr = addr;
+				pcidev_dev = dev;
 				found++;
+			}
 		}
 	}
 
@@ -102,7 +106,7 @@
 		exit(1);
 	}
 
-	return addr;
+	return curaddr;
 }
 
 void print_supported_pcidevs(struct pcidev_status *devs)