summaryrefslogtreecommitdiffstats
path: root/ft2232_spi.c
diff options
context:
space:
mode:
authorSimon Buhrow <simon.buhrow@sieb-meyer.de>2021-04-09 14:48:39 +0200
committerNico Huber <nico.h@gmx.de>2021-06-25 12:47:47 +0000
commit4d5a3b5eb03ad73d7eab16cf158513c1ed013c14 (patch)
tree25e53c4c2baee8b61703628a748fc20c49c45bc5 /ft2232_spi.c
parentd3e778f8ab1694d9c93c2c290fdcd8929c7151f9 (diff)
downloadflashrom-4d5a3b5eb03ad73d7eab16cf158513c1ed013c14.tar.gz
flashrom-4d5a3b5eb03ad73d7eab16cf158513c1ed013c14.tar.bz2
flashrom-4d5a3b5eb03ad73d7eab16cf158513c1ed013c14.zip
ft2232_spi.c: Implement spi_send_multicommand()
Every ftdi_write_data() call is quite time consuming as the ftdi-chips seems to take always 2-3ms to respond. This leads to what the comment already says: Minimize USB transfers by packing as many commands as possible together. So I packed the WREN command together with the following operation which can be program or erase operation. This saves about 1 minute when programming a 128MBit Flash within a virtualized setup. Signed-off-by: Simon Buhrow <simon.buhrow@posteo.de> Change-Id: Ie4a07499ec5ef0af23818593f45dc427285a9e8a Reviewed-on: https://review.coreboot.org/c/flashrom/+/40477 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Nico Huber <nico.h@gmx.de> Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Diffstat (limited to 'ft2232_spi.c')
-rw-r--r--ft2232_spi.c91
1 files changed, 90 insertions, 1 deletions
diff --git a/ft2232_spi.c b/ft2232_spi.c
index 082f5ffe4..3fe7ea4dd 100644
--- a/ft2232_spi.c
+++ b/ft2232_spi.c
@@ -79,6 +79,8 @@ static const struct dev_entry devs_ft2232spi[] = {
{0},
};
+#define FTDI_HW_BUFFER_SIZE 4096 /* in bytes */
+
#define DEFAULT_DIVISOR 2
#define BITMODE_BITBANG_NORMAL 1
@@ -276,12 +278,99 @@ static int ft2232_spi_send_command(const struct flashctx *flash,
return failed ? -1 : 0;
}
+static bool ft2232_spi_command_fits(const struct spi_command *cmd, size_t buffer_size)
+{
+ const size_t cmd_len = 3; /* same length for any ft2232 command */
+ return
+ /* commands for CS# assertion and de-assertion: */
+ cmd_len + cmd_len
+ /* commands for either a write, a read or both: */
+ + (cmd->writecnt && cmd->readcnt ? cmd_len + cmd_len : cmd_len)
+ /* payload (only writecnt; readcnt concerns another buffer): */
+ + cmd->writecnt
+ <= buffer_size;
+}
+
+/* Returns 0 upon success, a negative number upon errors. */
+static int ft2232_spi_send_multicommand(const struct flashctx *flash, struct spi_command *cmds)
+{
+ struct ft2232_data *spi_data = flash->mst->spi.data;
+ struct ftdi_context *ftdic = &spi_data->ftdic_context;
+ static unsigned char buf[FTDI_HW_BUFFER_SIZE];
+ size_t i = 0;
+ int ret = 0;
+
+ /*
+ * Minimize FTDI-calls by packing as many commands as possible together.
+ */
+ for (; cmds->writecnt || cmds->readcnt; cmds++) {
+
+ if (cmds->writecnt > 65536 || cmds->readcnt > 65536)
+ return SPI_INVALID_LENGTH;
+
+ if (!ft2232_spi_command_fits(cmds, FTDI_HW_BUFFER_SIZE - i)) {
+ msg_perr("Command does not fit\n");
+ return SPI_GENERIC_ERROR;
+ }
+
+ msg_pspew("Assert CS#\n");
+ buf[i++] = SET_BITS_LOW;
+ buf[i++] = ~ 0x08 & spi_data->pinlvl; /* assert CS (3rd) bit only */
+ buf[i++] = spi_data->pindir;
+
+ /* WREN, OP(PROGRAM, ERASE), ADDR, DATA */
+ if (cmds->writecnt) {
+ buf[i++] = MPSSE_DO_WRITE | MPSSE_WRITE_NEG;
+ buf[i++] = (cmds->writecnt - 1) & 0xff;
+ buf[i++] = ((cmds->writecnt - 1) >> 8) & 0xff;
+ memcpy(buf + i, cmds->writearr, cmds->writecnt);
+ i += cmds->writecnt;
+ }
+
+ /* An optional read command */
+ if (cmds->readcnt) {
+ buf[i++] = MPSSE_DO_READ;
+ buf[i++] = (cmds->readcnt - 1) & 0xff;
+ buf[i++] = ((cmds->readcnt - 1) >> 8) & 0xff;
+ }
+
+ /* Add final de-assert CS# */
+ msg_pspew("De-assert CS#\n");
+ buf[i++] = SET_BITS_LOW;
+ buf[i++] = spi_data->pinlvl;
+ buf[i++] = spi_data->pindir;
+
+ /* continue if there is no read-cmd and further cmds exist */
+ if (!cmds->readcnt &&
+ ((cmds + 1)->writecnt || (cmds + 1)->readcnt) &&
+ ft2232_spi_command_fits((cmds + 1), FTDI_HW_BUFFER_SIZE - i)) {
+ continue;
+ }
+
+ ret = send_buf(ftdic, buf, i);
+ i = 0;
+ if (ret) {
+ msg_perr("send_buf failed: %i\n", ret);
+ break;
+ }
+
+ if (cmds->readcnt) {
+ ret = get_buf(ftdic, cmds->readarr, cmds->readcnt);
+ if (ret) {
+ msg_perr("get_buf failed: %i\n", ret);
+ break;
+ }
+ }
+ }
+ return ret ? -1 : 0;
+}
+
static const struct spi_master spi_master_ft2232 = {
.features = SPI_MASTER_4BA,
.max_data_read = 64 * 1024,
.max_data_write = 256,
.command = ft2232_spi_send_command,
- .multicommand = default_spi_send_multicommand,
+ .multicommand = ft2232_spi_send_multicommand,
.read = default_spi_read,
.write_256 = default_spi_write_256,
.write_aai = default_spi_write_aai,