dediprog: implement command spec for firmware >= 7.2.30

This adds support for the latest command spec for Dediprog SF100/SF600
programmers. Since we now have more than two protocols to
deal with the is_new_prot() function is replaced with protocol() which
returns an enum specifying which protocol is supported.

The latest spec (FW >= 7.2.30) updates read and write packets. It's
been tested on an SF600 using firmware 7.2.21 and SF600Plus using FW
7.2.30.

The latest command protocol has a few small but important changes:
- Read packets have two more bytes:
  11: B4Addr: address len (3 or 4)
  12: Dummy cycle /2

- Write packets have four more bytes:
  11, 12: 16 HSBs of page size
  13, 14: 16 LSBs of page size

(The spec seems to be mistaken, though, as 11 and 12 are actually
 LSBs instead of HSBs)

Change-Id: I1a53c143948ec40d40433621891a2871d8815f2f
Signed-off-by: David Hendricks <dhendricks@fb.com>
Reviewed-on: https://review.coreboot.org/23836
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Nico Huber <nico.h@gmx.de>
diff --git a/dediprog.c b/dediprog.c
index 2f5b441..146b3e7 100644
--- a/dediprog.c
+++ b/dediprog.c
@@ -144,6 +144,17 @@
 	LEAVE_STANDALONE_MODE = 1,
 };
 
+/*
+ * These are not official designations; they are for use in flashrom only.
+ * Order must be preserved so that comparison operators work.
+ */
+enum protocol {
+	PROTOCOL_UNKNOWN,
+	PROTOCOL_V1,
+	PROTOCOL_V2,
+	PROTOCOL_V3,
+};
+
 const struct dev_entry devs_dediprog[] = {
 	{0x0483, 0xDADA, OK, "Dediprog", "SF100/SF600"},
 
@@ -169,16 +180,24 @@
 }
 #endif
 
-/* Returns true if firmware (and thus hardware) supports the "new" protocol */
-static bool is_new_prot(void)
+static enum protocol protocol(void)
 {
+	/* Firmware version < 5.0.0 is handled explicitly in some cases. */
 	switch (dediprog_devicetype) {
 	case DEV_SF100:
-		return dediprog_firmwareversion >= FIRMWARE_VERSION(5, 5, 0);
+		if (dediprog_firmwareversion < FIRMWARE_VERSION(5, 5, 0))
+			return PROTOCOL_V1;
+		else
+			return PROTOCOL_V2;
 	case DEV_SF600:
-		return dediprog_firmwareversion >= FIRMWARE_VERSION(6, 9, 0);
+		if (dediprog_firmwareversion < FIRMWARE_VERSION(6, 9, 0))
+			return PROTOCOL_V1;
+		else if (dediprog_firmwareversion <= FIRMWARE_VERSION(7, 2, 21))
+			return PROTOCOL_V2;
+		else
+			return PROTOCOL_V3;
 	default:
-		return 0;
+		return PROTOCOL_UNKNOWN;
 	}
 }
 
@@ -287,7 +306,7 @@
 	 * FIXME: take IO pins into account
 	 */
 	int target_leds, ret;
-	if (is_new_prot()) {
+	if (protocol() >= PROTOCOL_V2) {
 		target_leds = (leds ^ 7) << 8;
 		ret = dediprog_write(CMD_SET_IO_LED, target_leds, 0, NULL, 0);
 	} else {
@@ -387,7 +406,8 @@
 	return 0;
 }
 
-static void fill_rw_cmd_payload(uint8_t *data_packet, unsigned int count, uint8_t dedi_spi_cmd, unsigned int *value, unsigned int *idx, unsigned int start) {
+static void fill_rw_cmd_payload(uint8_t *data_packet, unsigned int count, uint8_t dedi_spi_cmd,
+		unsigned int *value, unsigned int *idx, unsigned int start, int is_read) {
 	/* First 5 bytes are common in both generations. */
 	data_packet[0] = count & 0xff;
 	data_packet[1] = (count >> 8) & 0xff;
@@ -395,13 +415,26 @@
 	data_packet[3] = dedi_spi_cmd; /* Read/Write Mode (currently READ_MODE_STD, WRITE_MODE_PAGE_PGM or WRITE_MODE_2B_AAI) */
 	data_packet[4] = 0; /* "Opcode". Specs imply necessity only for READ_MODE_4B_ADDR_FAST and WRITE_MODE_4B_ADDR_256B_PAGE_PGM */
 
-	if (is_new_prot()) {
+	if (protocol() >= PROTOCOL_V2) {
 		*value = *idx = 0;
 		data_packet[5] = 0; /* RFU */
 		data_packet[6] = (start >>  0) & 0xff;
 		data_packet[7] = (start >>  8) & 0xff;
 		data_packet[8] = (start >> 16) & 0xff;
 		data_packet[9] = (start >> 24) & 0xff;
+		if (protocol() >= PROTOCOL_V3) {
+			if (is_read) {
+				data_packet[10] = 0x00;	/* address length (3 or 4) */
+				data_packet[11] = 0x00;	/* dummy cycle / 2 */
+			} else {
+				/* 16 LSBs and 16 HSBs of page size */
+				/* FIXME: This assumes page size of 256. */
+				data_packet[10] = 0x00;
+				data_packet[11] = 0x01;
+				data_packet[12] = 0x00;
+				data_packet[13] = 0x00;
+			}
+		}
 	} else {
 		*value = start % 0x10000;
 		*idx = start / 0x10000;
@@ -434,10 +467,24 @@
 	if (len == 0)
 		return 0;
 
-	/* Command packet size of protocols: new 10 B, old 5 B. */
-	uint8_t data_packet[is_new_prot() ? 10 : 5];
+	int command_packet_size;
+	switch (protocol()) {
+	case PROTOCOL_V1:
+		command_packet_size = 5;
+		break;
+	case PROTOCOL_V2:
+		command_packet_size = 10;
+		break;
+	case PROTOCOL_V3:
+		command_packet_size = 12;
+		break;
+	default:
+		return 1;
+	}
+
+	uint8_t data_packet[command_packet_size];
 	unsigned int value, idx;
-	fill_rw_cmd_payload(data_packet, count, READ_MODE_STD, &value, &idx, start);
+	fill_rw_cmd_payload(data_packet, count, READ_MODE_STD, &value, &idx, start, 1);
 
 	int ret = dediprog_write(CMD_READ, value, idx, data_packet, sizeof(data_packet));
 	if (ret != sizeof(data_packet)) {
@@ -576,10 +623,24 @@
 	if (len == 0)
 		return 0;
 
-	/* Command packet size of protocols: new 10 B, old 5 B. */
-	uint8_t data_packet[is_new_prot() ? 10 : 5];
+	int command_packet_size;
+	switch (protocol()) {
+	case PROTOCOL_V1:
+		command_packet_size = 5;
+		break;
+	case PROTOCOL_V2:
+		command_packet_size = 10;
+		break;
+	case PROTOCOL_V3:
+		command_packet_size = 14;
+		break;
+	default:
+		return 1;
+	}
+
+	uint8_t data_packet[command_packet_size];
 	unsigned int value, idx;
-	fill_rw_cmd_payload(data_packet, count, dedi_spi_cmd, &value, &idx, start);
+	fill_rw_cmd_payload(data_packet, count, dedi_spi_cmd, &value, &idx, start, 0);
 	int ret = dediprog_write(CMD_WRITE, value, idx, data_packet, sizeof(data_packet));
 	if (ret != sizeof(data_packet)) {
 		msg_perr("Command Write SPI Bulk failed, %s!\n", libusb_error_name(ret));
@@ -686,7 +747,7 @@
 	unsigned int idx, value;
 	/* New protocol has options and timeout combined as value while the old one used the value field for
 	 * timeout and the index field for options. */
-	if (is_new_prot()) {
+	if (protocol() >= PROTOCOL_V2) {
 		idx = 0;
 		value = readcnt ? 0x1 : 0x0; // Indicate if we require a read
 	} else {
@@ -711,7 +772,7 @@
 	 * The specification also uses only 0 in its examples, so the lesson to learn here:
 	 * "Never trust the description of an interface in the documentation but use the example code and pray."
 	const uint8_t read_timeout = 10 + readcnt/512;
-	if (is_new_prot()) {
+	if (protocol() >= PROTOCOL_V2) {
 		idx = 0;
 		value = min(read_timeout, 0xFF) | (0 << 8) ; // Timeout in lower byte, option in upper byte
 	} else {
@@ -762,7 +823,12 @@
 		msg_perr("Unexpected firmware version %d.%d.%d!\n", fw[0], fw[1], fw[2]);
 		return 1;
 	}
+
 	dediprog_firmwareversion = FIRMWARE_VERSION(fw[0], fw[1], fw[2]);
+	if (protocol() == PROTOCOL_UNKNOWN) {
+		msg_perr("Internal error: Unable to determine protocol version.\n");
+		return 1;
+	}
 
 	return 0;
 }
@@ -922,7 +988,6 @@
 
 static int dediprog_shutdown(void *data)
 {
-	dediprog_firmwareversion = FIRMWARE_VERSION(0, 0, 0);
 	dediprog_devicetype = DEV_UNKNOWN;
 
 	/* URB 28. Command Set SPI Voltage to 0. */
@@ -1083,7 +1148,7 @@
 
 	/* Set all possible LEDs as soon as possible to indicate activity.
 	 * Because knowing the firmware version is required to set the LEDs correctly we need to this after
-	 * dediprog_check_devicestring() has queried the device and set dediprog_firmwareversion. */
+	 * dediprog_check_devicestring() has queried the device. */
 	dediprog_set_leds(LED_ALL);
 
 	/* Select target/socket, frequency and VCC. */