dummyflasher.c: add support for SFDP by adding a new emulator chip: MX25L6436

The chip features a complete 1.0 SFDP JEDEC flash parameter table and also a
vendor-specific extension table (defining voltages, lock bits etc).
NB: the MX25L6436 uses the same RDID as the MX25L6405.

Corresponding to flashrom svn r1534.

Signed-off-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
Acked-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>
diff --git a/dummyflasher.c b/dummyflasher.c
index 50e8bf3..29256c2 100644
--- a/dummyflasher.c
+++ b/dummyflasher.c
@@ -46,6 +46,7 @@
 	EMULATE_ST_M25P10_RES,
 	EMULATE_SST_SST25VF040_REMS,
 	EMULATE_SST_SST25VF032B,
+	EMULATE_MACRONIX_MX25L6436,
 };
 static enum emu_chip emu_chip = EMULATE_NONE;
 static char *emu_persistent_image = NULL;
@@ -63,6 +64,33 @@
 int spi_blacklist_size = 0;
 int spi_ignorelist_size = 0;
 static uint8_t emu_status = 0;
+
+/* A legit complete SFDP table based on the MX25L6436E (rev. 1.8) datasheet. */
+static const uint8_t const sfdp_table[] = {
+	0x53, 0x46, 0x44, 0x50, // @0x00: SFDP signature
+	0x00, 0x01, 0x01, 0xFF, // @0x04: revision 1.0, 2 headers
+	0x00, 0x00, 0x01, 0x09, // @0x08: JEDEC SFDP header rev. 1.0, 9 DW long
+	0x1C, 0x00, 0x00, 0xFF, // @0x0C: PTP0 = 0x1C (instead of 0x30)
+	0xC2, 0x00, 0x01, 0x04, // @0x10: Macronix header rev. 1.0, 4 DW long
+	0x48, 0x00, 0x00, 0xFF, // @0x14: PTP1 = 0x48 (instead of 0x60)
+	0xFF, 0xFF, 0xFF, 0xFF, // @0x18: hole.
+	0xE5, 0x20, 0xC9, 0xFF, // @0x1C: SFDP parameter table start
+	0xFF, 0xFF, 0xFF, 0x03, // @0x20
+	0x00, 0xFF, 0x08, 0x6B, // @0x24
+	0x08, 0x3B, 0x00, 0xFF, // @0x28
+	0xEE, 0xFF, 0xFF, 0xFF, // @0x2C
+	0xFF, 0xFF, 0x00, 0x00, // @0x30
+	0xFF, 0xFF, 0x00, 0xFF, // @0x34
+	0x0C, 0x20, 0x0F, 0x52, // @0x38
+	0x10, 0xD8, 0x00, 0xFF, // @0x3C: SFDP parameter table end
+	0xFF, 0xFF, 0xFF, 0xFF, // @0x40: hole.
+	0xFF, 0xFF, 0xFF, 0xFF, // @0x44: hole.
+	0x00, 0x36, 0x00, 0x27, // @0x48: Macronix parameter table start
+	0xF4, 0x4F, 0xFF, 0xFF, // @0x4C
+	0xD9, 0xC8, 0xFF, 0xFF, // @0x50
+	0xFF, 0xFF, 0xFF, 0xFF, // @0x54: Macronix parameter table end
+};
+
 #endif
 #endif
 
@@ -301,6 +329,19 @@
 		msg_pdbg("Emulating SST SST25VF032B SPI flash chip (RDID, AAI "
 			 "write)\n");
 	}
+	if (!strcmp(tmp, "MX25L6436")) {
+		emu_chip = EMULATE_MACRONIX_MX25L6436;
+		emu_chip_size = 8 * 1024 * 1024;
+		emu_max_byteprogram_size = 256;
+		emu_max_aai_size = 0;
+		emu_jedec_se_size = 4 * 1024;
+		emu_jedec_be_52_size = 32 * 1024;
+		emu_jedec_be_d8_size = 64 * 1024;
+		emu_jedec_ce_60_size = emu_chip_size;
+		emu_jedec_ce_c7_size = emu_chip_size;
+		msg_pdbg("Emulating Macronix MX25L6436 SPI flash chip (RDID, "
+			 "SFDP)\n");
+	}
 #endif
 	if (emu_chip == EMULATE_NONE) {
 		msg_perr("Invalid chip specified for emulation: %s\n", tmp);
@@ -449,7 +490,7 @@
 				     const unsigned char *writearr,
 				     unsigned char *readarr)
 {
-	unsigned int offs, i;
+	unsigned int offs, i, toread;
 	static int unsigned aai_offs;
 
 	if (writecnt == 0) {
@@ -503,20 +544,30 @@
 			readarr[1] = 0x44;
 		break;
 	case JEDEC_RDID:
-		if (emu_chip != EMULATE_SST_SST25VF032B)
+		switch (emu_chip) {
+		case EMULATE_SST_SST25VF032B:
+			if (readcnt > 0)
+				readarr[0] = 0xbf;
+			if (readcnt > 1)
+				readarr[1] = 0x25;
+			if (readcnt > 2)
+				readarr[2] = 0x4a;
 			break;
-		/* Respond with SST_SST25VF032B. */
-		if (readcnt > 0)
-			readarr[0] = 0xbf;
-		if (readcnt > 1)
-			readarr[1] = 0x25;
-		if (readcnt > 2)
-			readarr[2] = 0x4a;
+		case EMULATE_MACRONIX_MX25L6436:
+			if (readcnt > 0)
+				readarr[0] = 0xc2;
+			if (readcnt > 1)
+				readarr[1] = 0x20;
+			if (readcnt > 2)
+				readarr[2] = 0x17;
+			break;
+		default: /* ignore */
+			break;
+		}
 		break;
-	case JEDEC_RDSR: {
+	case JEDEC_RDSR:
 		memset(readarr, emu_status, readcnt);
 		break;
-	}
 	/* FIXME: this should be chip-specific. */
 	case JEDEC_EWSR:
 	case JEDEC_WREN:
@@ -673,6 +724,41 @@
 		/* emu_jedec_ce_c7_size is emu_chip_size. */
 		memset(flashchip_contents, 0xff, emu_jedec_ce_c7_size);
 		break;
+	case JEDEC_SFDP:
+		if (emu_chip != EMULATE_MACRONIX_MX25L6436)
+			break;
+		if (writecnt < 4)
+			break;
+		offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+
+		/* SFDP expects one dummy byte after the address. */
+		if (writecnt == 4) {
+			/* The dummy byte was not written, make sure it is read instead.
+			 * Shifting and shortening the read array does achieve this goal.
+			 */
+			readarr++;
+			readcnt--;
+		} else {
+			/* The response is shifted if more than 5 bytes are written, because SFDP data is
+			 * already shifted out by the chip while those superfluous bytes are written. */
+			offs += writecnt - 5;
+		}
+
+		/* The SFDP spec implies that the start address of an SFDP read may be truncated to fit in the
+		 * SFDP table address space, i.e. the start address may be wrapped around at SFDP table size.
+		 * This is a reasonable implementation choice in hardware because it saves a few gates. */
+		if (offs >= sizeof(sfdp_table)) {
+			msg_pdbg("Wrapping the start address around the SFDP table boundary (using 0x%x "
+				 "instead of 0x%x).\n", (unsigned int)(offs % sizeof(sfdp_table)), offs);
+			offs %= sizeof(sfdp_table);
+		}
+		toread = min(sizeof(sfdp_table) - offs, readcnt);
+		memcpy(readarr, sfdp_table + offs, toread);
+		if (toread < readcnt)
+			msg_pdbg("Crossing the SFDP table boundary in a single "
+				 "continuous chunk produces undefined results "
+				 "after that point.\n");
+		break;
 	default:
 		/* No special response. */
 		break;
@@ -703,6 +789,7 @@
 	case EMULATE_ST_M25P10_RES:
 	case EMULATE_SST_SST25VF040_REMS:
 	case EMULATE_SST_SST25VF032B:
+	case EMULATE_MACRONIX_MX25L6436:
 		if (emulate_spi_chip_response(writecnt, readcnt, writearr,
 					      readarr)) {
 			msg_pdbg("Invalid command sent to flash chip!\n");