Thanks to Ian Lesnet for adding a new SPI mode to the Bus Pirate which is specifically designed for flashrom. It has the potential to speed up reads and writes a lot. This patch implements flashrom support for the new SPI mode in a hopefully backward compatible way.
The new Bus Pirate interface is present since Bus Pirate firmware version 4.6 and this patch should help testing if the interface works as designed. No significant speedups expected yet because the code still uses the old small block sizes.
I would love to see timing comparisons for read and write, though. Once with <4.6 firmware and once with >=4.6 firmware.
Side note: The buffer management (search for realloc) is horrible, and should probably be changed to avoid the realloc in most (all cases). OTOH, a static huge buffer is also not optimal. A grow-only buffer like in other drivers may be the best choice.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-buspirate_newcommands/buspirate_spi.c =================================================================== --- flashrom-buspirate_newcommands/buspirate_spi.c (Revision 1178) +++ flashrom-buspirate_newcommands/buspirate_spi.c (Arbeitskopie) @@ -46,6 +46,8 @@ #define sp_flush_incoming(...) 0 #endif
+static int buspirate_interface_version; + static int buspirate_sendrecv(unsigned char *buf, unsigned int writecnt, unsigned int readcnt) { int i, ret = 0; @@ -131,7 +133,13 @@ return ret; free(dev);
- /* This is the brute force version, but it should work. */ + /* This is the brute force version, but it should work. + * It is guaranteed to fail if a previous flashrom run was aborted + * during a write with the new SPI commands in firmware v4.6 because + * that firmware may wait for up to 4096 bytes of input before + * responding to 0x00 again. The obvious workaround may cause startup + * penalties of more than one second. + */ for (i = 0; i < 19; i++) { /* Enter raw bitbang mode */ buf[0] = 0x00; @@ -231,6 +239,34 @@ return 1; }
+ /* Test combined SPI write/read, length 0. */ + buf[0] = 0x04; + buf[1] = 0; + buf[2] = 0; + buf[3] = 0; + buf[4] = 0; + ret = buspirate_sendrecv(buf, 5, 1); + if (ret) + return 1; + if (buf[0] != 0x01) { + msg_pdbg("SPI command set v2 not available, using old commands " + "present in firmware vX.Y or later\n"); + + /* FIXME: Check the error code? */ + /* We sent 4 bytes of 0x00, so we expect 4 BBIO1 responses. */ + buspirate_sendrecv(buf, 0, 4 * 5); + + /* Enter raw SPI mode again. */ + buf[0] = 0x01; + /* FIXME: Check the error code? */ + buspirate_sendrecv(buf, 1, 4); + + buspirate_interface_version = 1; + } else { + msg_pdbg("Using SPI command set v2.\n"); + buspirate_interface_version = 2; + } + buses_supported = CHIP_BUSTYPE_SPI; spi_controller = SPI_CONTROLLER_BUSPIRATE;
@@ -272,12 +308,56 @@ return 0; }
-int buspirate_spi_send_command(unsigned int writecnt, unsigned int readcnt, +int buspirate_spi_send_command_v2(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr) { static unsigned char *buf = NULL; int i = 0, ret = 0;
+ if (writecnt > 4096 || readcnt > 4096 || (readcnt + writecnt) > 4096) + return SPI_INVALID_LENGTH; + + /* 5 bytes extra for command, writelen, readlen. + * 1 byte extra for Ack/Nack. + */ + buf = realloc(buf, max(writecnt + 5, readcnt + 1)); + if (!buf) { + msg_perr("Out of memory!\n"); + exit(1); // -1 + } + + /* Combined SPI write/read. */ + buf[i++] = 0x04; + buf[i++] = (writecnt >> 8) & 0xff; + buf[i++] = writecnt & 0xff; + buf[i++] = (readcnt >> 8) & 0xff; + buf[i++] = readcnt & 0xff; + memcpy(buf + i, writearr, writecnt); + + ret = buspirate_sendrecv(buf, i + writecnt, 1 + readcnt); + + if (ret) { + msg_perr("Bus Pirate communication error!\n"); + return SPI_GENERIC_ERROR; + } + + if (buf[0] != 0x01) { + msg_perr("Protocol error while sending SPI write/read!\n"); + return SPI_GENERIC_ERROR; + } + + /* Skip Ack. */ + memcpy(readarr, buf + 1, readcnt); + + return ret; +} + +int buspirate_spi_send_command_v1(unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr) +{ + static unsigned char *buf = NULL; + int i = 0, ret = 0; + if (writecnt > 16 || readcnt > 16 || (readcnt + writecnt) > 16) return SPI_INVALID_LENGTH;
@@ -328,6 +408,17 @@ return ret; }
+int buspirate_spi_send_command(unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr) +{ + switch (buspirate_interface_version) { + case 2: + return buspirate_spi_send_command_v2(writecnt, readcnt, writearr, readarr); + default: + return buspirate_spi_send_command_v1(writecnt, readcnt, writearr, readarr); + } +} + int buspirate_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len) { return spi_read_chunked(flash, buf, start, len, 12);