Speed up dediprog SPI page writes

All chips which use spi_chip_write_256 should be written at native
speed. Chips using spi_chip_write_1 or spi_chip_write_aai will
still be slow.

Thanks to Steven A. Falco for testing with a ST/Numonyx M25P16.
Thanks to David Hendricks for testing with a Winbond W25Q64.

Corresponding to flashrom svn r1477.

Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>
Acked-by: Steven A. Falco <sfalco@coincident.com>
diff --git a/dediprog.c b/dediprog.c
index db29c13..f5d08ff 100644
--- a/dediprog.c
+++ b/dediprog.c
@@ -299,22 +299,109 @@
 	return 0;
 }
 
+/* Bulk write interface, will read multiple page_size byte chunks aligned to page_size bytes.
+ * @start	start address
+ * @len		length
+ * @return	0 on success, 1 on failure
+ */
+static int dediprog_spi_bulk_write(struct flashctx *flash, uint8_t *buf,
+				   unsigned int start, unsigned int len)
+{
+	int ret;
+	unsigned int i;
+	/* USB transfer size must be 512, other sizes will NOT work at all.
+	 * chunksize is the real data size per USB bulk transfer. The remaining
+	 * space in a USB bulk transfer must be filled with 0xff padding.
+	 */
+	const unsigned int chunksize = flash->page_size;
+	const unsigned int count = len / chunksize;
+	const char count_and_chunk[] = {count & 0xff,
+					(count >> 8) & 0xff,
+					chunksize & 0xff,
+					(chunksize >> 8) & 0xff};
+	char usbbuf[512];
+
+	if ((start % chunksize) || (len % chunksize)) {
+		msg_perr("%s: Unaligned start=%i, len=%i! Please report a bug "
+			 "at flashrom@flashrom.org\n", __func__, start, len);
+		return 1;
+	}
+
+	/* No idea if the hardware can handle empty writes, so chicken out. */
+	if (!len)
+		return 0;
+	/* Command Write SPI Bulk. No idea which write command is used on the
+	 * SPI side.
+	 */
+	ret = usb_control_msg(dediprog_handle, 0x42, 0x30, start % 0x10000,
+			      start / 0x10000, (char *)count_and_chunk,
+			      sizeof(count_and_chunk), DEFAULT_TIMEOUT);
+	if (ret != sizeof(count_and_chunk)) {
+		msg_perr("Command Write SPI Bulk failed, %i %s!\n", ret,
+			 usb_strerror());
+		return 1;
+	}
+
+	for (i = 0; i < count; i++) {
+		memset(usbbuf, 0xff, sizeof(usbbuf));
+		memcpy(usbbuf, buf + i * chunksize, chunksize);
+		ret = usb_bulk_write(dediprog_handle, dediprog_endpoint,
+				    usbbuf, 512,
+				    DEFAULT_TIMEOUT);
+		if (ret != 512) {
+			msg_perr("SPI bulk write failed, expected %i, got %i "
+				 "%s!\n", 512, ret, usb_strerror());
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
 static int dediprog_spi_write_256(struct flashctx *flash, uint8_t *buf,
 				  unsigned int start, unsigned int len)
 {
 	int ret;
+	const unsigned int chunksize = flash->page_size;
+	unsigned int residue = start % chunksize ? chunksize - start % chunksize : 0;
+	unsigned int bulklen;
 
 	dediprog_set_leds(PASS_OFF|BUSY_ON|ERROR_OFF);
 
-	/* No idea about the real limit. Maybe 12, maybe more, maybe less. */
-	ret = spi_write_chunked(flash, buf, start, len, 12);
+	if (residue) {
+		msg_pdbg("Slow write for partial block from 0x%x, length 0x%x\n",
+			 start, residue);
+		/* No idea about the real limit. Maybe 12, maybe more. */
+		ret = spi_write_chunked(flash, buf, start, residue, 12);
+		if (ret) {
+			dediprog_set_leds(PASS_OFF|BUSY_OFF|ERROR_ON);
+			return ret;
+		}
+	}
 
-	if (ret)
+	/* Round down. */
+	bulklen = (len - residue) / chunksize * chunksize;
+	ret = dediprog_spi_bulk_write(flash, buf + residue, start + residue,
+				     bulklen);
+	if (ret) {
 		dediprog_set_leds(PASS_OFF|BUSY_OFF|ERROR_ON);
-	else
-		dediprog_set_leds(PASS_ON|BUSY_OFF|ERROR_OFF);
+		return ret;
+	}
 
-	return ret;
+	len -= residue + bulklen;
+	if (len) {
+		msg_pdbg("Slow write for partial block from 0x%x, length 0x%x\n",
+			 start, len);
+		ret = spi_write_chunked(flash, buf + residue + bulklen,
+				        start + residue + bulklen, len, 12);
+		if (ret) {
+			dediprog_set_leds(PASS_OFF|BUSY_OFF|ERROR_ON);
+			return ret;
+		}
+	}
+
+	dediprog_set_leds(PASS_ON|BUSY_OFF|ERROR_OFF);
+	return 0;
 }
 
 static int dediprog_spi_send_command(struct flashctx *flash,
@@ -494,6 +581,76 @@
 	}
 	return 0;
 }
+
+/* Start/stop blinking?
+ * Present in eng_detect_blink.log with firmware 3.1.8
+ * Preceded by Command J
+ */
+static int dediprog_command_g(void)
+{
+	int ret;
+
+	ret = usb_control_msg(dediprog_handle, 0x42, 0x07, 0x09, 0x03, NULL, 0x0, DEFAULT_TIMEOUT);
+	if (ret != 0x0) {
+		msg_perr("Command G failed (%s)!\n", usb_strerror());
+		return 1;
+	}
+	return 0;
+}
+
+/* Something.
+ * Present in all logs with firmware 5.1.5
+ * Always preceded by Command Receive Device String
+ * Always followed by Command Set SPI Voltage nonzero
+ */
+static int dediprog_command_h(void)
+{
+	int ret;
+
+	ret = usb_control_msg(dediprog_handle, 0x42, 0x07, 0x09, 0x05, NULL, 0x0, DEFAULT_TIMEOUT);
+	if (ret != 0x0) {
+		msg_perr("Command H failed (%s)!\n", usb_strerror());
+		return 1;
+	}
+	return 0;
+}
+
+/* Shutdown for firmware 5.x?
+ * Present in all logs with firmware 5.1.5
+ * Often preceded by a SPI operation (Command Read SPI Bulk or Receive SPI)
+ * Always followed by Command Set SPI Voltage 0x0000
+ */
+static int dediprog_command_i(void)
+{
+	int ret;
+
+	ret = usb_control_msg(dediprog_handle, 0x42, 0x07, 0x09, 0x06, NULL, 0x0, DEFAULT_TIMEOUT);
+	if (ret != 0x0) {
+		msg_perr("Command I failed (%s)!\n", usb_strerror());
+		return 1;
+	}
+	return 0;
+}
+
+/* Start/stop blinking?
+ * Present in all logs with firmware 5.1.5
+ * Always preceded by Command Receive Device String on 5.1.5
+ * Always followed by Command Set SPI Voltage nonzero on 5.1.5
+ * Present in eng_detect_blink.log with firmware 3.1.8
+ * Preceded by Command B in eng_detect_blink.log
+ * Followed by Command G in eng_detect_blink.log
+ */
+static int dediprog_command_j(void)
+{
+	int ret;
+
+	ret = usb_control_msg(dediprog_handle, 0x42, 0x07, 0x09, 0x07, NULL, 0x0, DEFAULT_TIMEOUT);
+	if (ret != 0x0) {
+		msg_perr("Command J failed (%s)!\n", usb_strerror());
+		return 1;
+	}
+	return 0;
+}
 #endif
 
 static int parse_voltage(char *voltage)
@@ -558,6 +715,13 @@
 {
 	msg_pspew("%s\n", __func__);
 
+#if 0
+	/* Shutdown on firmware 5.x */
+	if (dediprog_firmwareversion == 5)
+		if (dediprog_command_i())
+			return 1;
+#endif
+
 	/* URB 28. Command Set SPI Voltage to 0. */
 	if (dediprog_set_spi_voltage(0x0))
 		return 1;