This patch aims to restructure SPI flash support in a more reasonable way

It introduces a generic SPI host driver for the IT8716F Super I/O
which will enable easy SPI programming without having to care for the
peculiarities of the SPI host.

To activate probing for the IT8716F, you have to use the gigabyte:m57sli
mainboard override. SPI support will then use the gathered SPI host data
to access the SPI flash.

This has been tested sucessfully by Ward Vandewege <ward@gnu.org> on the
GA-M57SLI v2.0, which has a MX25L4005 SPI flash part.

Corresponding to flashrom svn r140 and coreboot v2 svn r2817.

Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>
Acked-by: Ward Vandewege <ward@gnu.org>
diff --git a/board_enable.c b/board_enable.c
index 43ca02c..0e72c2c 100644
--- a/board_enable.c
+++ b/board_enable.c
@@ -30,6 +30,15 @@
 #include <string.h>
 #include "flash.h"
 
+#define ITE_SUPERIO_PORT1	0x2e
+#define ITE_SUPERIO_PORT2	0x4e
+
+#define JEDEC_RDID	{0x9f}
+#define JEDEC_RDID_OUTSIZE	0x01
+#define JEDEC_RDID_INSIZE	0x03
+
+static uint16_t it8716f_flashport = 0;
+
 /* Generic Super I/O helper functions */
 uint8_t regval(uint16_t port, uint8_t reg)
 {
@@ -51,7 +60,7 @@
 	outb(0x87, port);
 	outb(0x01, port);
 	outb(0x55, port);
-	if (port == 0x2e)
+	if (port == ITE_SUPERIO_PORT1)
 		outb(0x55, port);
 	else
 		outb(0xaa, port);
@@ -96,35 +105,97 @@
 	return flashport;
 }
 
-static void it8716_serial_rdid(uint16_t port)
+/* The IT8716F only supports commands with length 1,2,4,5 bytes including
+   command byte and can not read more than 3 bytes from the device.
+   This function expects writearr[0] to be the first byte sent to the device,
+   whereas the IT8716F splits commands internally into address and non-address
+   commands with the address in inverse wire order. That's why the register
+   ordering in case 4 and 5 may seem strange. */
+static int it8716f_spi_command(uint16_t port, unsigned char writecnt, unsigned char readcnt, const unsigned char *writearr, unsigned char *readarr)
 {
-	uint8_t busy, data0, data1, data2;
+	uint8_t busy, writeenc;
 	do {
 		busy = inb(port) & 0x80;
 	} while (busy);
-	/* RDID */
-	outb(0x9f, port + 1);
-	/* Start IO, 33MHz, 3 input bytes, 0 output bytes*/
-	outb((0x5<<4)|(0x3<<2)|(0x0), port);
+	if (readcnt > 3) {
+		printf("%s called with unsupported readcnt %i\n",
+			__FUNCTION__, readcnt);
+		return 1;
+	}
+	switch (writecnt) {
+	case 1:
+		outb(writearr[0], port + 1);
+		writeenc = 0x0;
+		break;
+	case 2:
+		outb(writearr[0], port + 1);
+		outb(writearr[1], port + 7);
+		writeenc = 0x1;
+		break;
+	case 4:
+		outb(writearr[0], port + 1);
+		outb(writearr[1], port + 4);
+		outb(writearr[2], port + 3);
+		outb(writearr[3], port + 2);
+		writeenc = 0x2;
+		break;
+	case 5:
+		outb(writearr[0], port + 1);
+		outb(writearr[1], port + 4);
+		outb(writearr[2], port + 3);
+		outb(writearr[3], port + 2);
+		outb(writearr[4], port + 7);
+		writeenc = 0x3;
+		break;
+	default:
+		printf("%s called with unsupported writecnt %i\n",
+			__FUNCTION__, writecnt);
+		return 1;
+	}
+	/* Start IO, 33MHz, readcnt input bytes, writecnt output bytes. Note:
+	   We can't use writecnt directly, but have to use a strange encoding */
+	outb((0x5 << 4) | ((readcnt & 0x3) << 2) | (writeenc), port);
 	do {
 		busy = inb(port) & 0x80;
 	} while (busy);
-	data0 = inb(port + 5);
-	data1 = inb(port + 6);
-	data2 = inb(port + 7);
-	printf("RDID returned %02x %02x %02x\n", data0, data1, data2);
-	return;
+	readarr[0] = inb(port + 5);
+	readarr[1] = inb(port + 6);
+	readarr[2] = inb(port + 7);
+	return 0;
+}
+
+static int it8716f_serial_rdid(uint16_t port, unsigned char *readarr)
+{
+	const unsigned char cmd[] = JEDEC_RDID;
+
+	if (it8716f_spi_command(port, JEDEC_RDID_OUTSIZE, JEDEC_RDID_INSIZE, cmd, readarr))
+		return 1;
+	printf("RDID returned %02x %02x %02x\n", readarr[0], readarr[1], readarr[2]);
+	return 0;
 }
 
 static int it87xx_probe_serial_flash(const char *name)
 {
-	uint16_t flashport;
-	flashport = find_ite_serial_flash_port(0x2e);
-	if (flashport)
-		it8716_serial_rdid(flashport);
-	flashport = find_ite_serial_flash_port(0x4e);
-	if (flashport)
-		it8716_serial_rdid(flashport);
+	it8716f_flashport = find_ite_serial_flash_port(ITE_SUPERIO_PORT1);
+	if (!it8716f_flashport)
+		it8716f_flashport = find_ite_serial_flash_port(ITE_SUPERIO_PORT2);
+	return (!it8716f_flashport);
+}
+
+int probe_spi(struct flashchip *flash)
+{
+	unsigned char readarr[3];
+	uint8_t manuf_id;
+	uint16_t model_id;
+	if (it8716f_flashport) {
+		it8716f_serial_rdid(it8716f_flashport, readarr);
+		manuf_id = readarr[0];
+		model_id = (readarr[1] << 8) | readarr[2];
+		printf_debug("%s: id1 0x%x, id2 0x%x\n", __FUNCTION__, manuf_id, model_id);
+		if (manuf_id == flash->manufacture_id && model_id == flash->model_id)
+			return 1;
+	}
+
 	return 0;
 }
 
diff --git a/flash.h b/flash.h
index 2a45e46..46f8ad3 100644
--- a/flash.h
+++ b/flash.h
@@ -55,6 +55,8 @@
 /* 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.
+ * All SPI parts have 16-bit device IDs.
  */
 
 #define AMD_ID			0x01	/* AMD */
@@ -68,8 +70,31 @@
 #define AT_29C040A		0xA4
 #define AT_29C020		0xDA
 
+#define EON_ID			0x1C
+/* EN25 chips are SPI, first byte of device id is memory type,
+   second byte of device id is log(bitsize)-9 */
+#define EN_25B05		0x2010	/* 2^19 kbit or 2^16 kByte */
+#define EN_25B10		0x2011
+#define EN_25B20		0x2012
+#define EN_25B40		0x2013
+#define EN_25B80		0x2014
+#define EN_25B16		0x2015
+#define EN_25B32		0x2016
+
 #define MX_ID			0xC2	/* Macronix (MX) */
 #define MX_29F002		0xB0
+/* MX25L chips are SPI, first byte of device id is memory type,
+   second byte of device id is log(bitsize)-9 */
+#define MX_25L512		0x2010	/* 2^19 kbit or 2^16 kByte */
+#define MX_25L1005		0x2011
+#define MX_25L2005		0x2012
+#define MX_25L4005		0x2013	/* MX25L4005{,A} */
+#define MX_25L8005		0x2014
+#define MX_25L1605		0x2015	/* MX25L1605{,A,D} */
+#define MX_25L3205		0x2016	/* MX25L3205{,A} */
+#define MX_25L6405		0x2017	/* MX25L3205{,D} */
+#define MX_25L1635D		0x2415
+#define MX_25L3235D		0x2416
 
 #define SHARP_ID		0xB0	/* Sharp */
 #define SHARP_LHF00L04		0xCF
@@ -182,6 +207,8 @@
 int linuxbios_init(void);
 extern char *lb_part, *lb_vendor;
 
+int probe_spi(struct flashchip *flash);
+
 /* 82802ab.c */
 int probe_82802ab(struct flashchip *flash);
 int erase_82802ab(struct flashchip *flash);
diff --git a/flashchips.c b/flashchips.c
index ec8089a..0c393a9 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -38,6 +38,8 @@
 	 probe_jedec,	erase_chip_jedec, write_jedec},
 	{"Mx29f002",	MX_ID,		MX_29F002,	256, 64 * 1024,
 	 probe_29f002,	erase_29f002, 	write_29f002},
+	{"MX25L4005",	MX_ID,		MX_25L4005,	512, 4 * 1024,
+	 probe_spi,	NULL,		NULL},
 	{"SST29EE020A", SST_ID,		SST_29EE020A,	256, 128,
 	 probe_jedec,	erase_chip_jedec, write_jedec},
 	{"SST28SF040A", SST_ID,		SST_28SF040,	512, 256,