dediprog: Add protocol variant support for newer SF600 fw revs
This is a backport of the upstream `commit f73f8a732`
dediprog: implement command spec for firmware >= 7.2.30
However this also contains elements of `commit ae61651bfa`
dediprog: support new communication protocol, cleanup and enable by
default
that was only partially applied to our tree. Although
the backport here is not a straight forward one were we have
cherry-picked hulks where it makes sense to do so to get a
viable itermediate patch working.
BUG=chromium:478356
BRANCH=none
TEST=still builds
Change-Id: Idf960bc7c57129e9f63e4aaeef6c9b82b0339555
Signed-off-by: Edward O'Callaghan <quasisec@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1492816
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
diff --git a/dediprog.c b/dediprog.c
index 51c9263..b34338d 100644
--- a/dediprog.c
+++ b/dediprog.c
@@ -142,6 +142,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/SF200/SF600"},
@@ -160,16 +171,21 @@
}
#endif
-/* Returns true if firmware (and thus hardware) supports the "new" protocol */
-static int 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);
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;
}
}
@@ -239,7 +255,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 {
@@ -296,6 +312,41 @@
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, int is_read) {
+ /* First 5 bytes are common in both generations. */
+ data_packet[0] = count & 0xff;
+ data_packet[1] = (count >> 8) & 0xff;
+ data_packet[2] = 0; /* RFU */
+ 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 (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;
+ }
+}
+
/* Bulk read interface, will read multiple 512 byte chunks aligned to 512 bytes.
* @start start address
* @len length
@@ -304,52 +355,46 @@
static int dediprog_spi_bulk_read(struct flashctx *flash, uint8_t *buf,
unsigned int start, unsigned int len)
{
- int ret, err = 1;
- unsigned int i;
+ int err = 1;
+
/* chunksize must be 512, other sizes will NOT work at all. */
const unsigned int chunksize = 0x200;
const unsigned int count = len / chunksize;
- unsigned int cmd_len;
+
struct dediprog_transfer_status status = { 0, 0, 0 };
struct libusb_transfer *transfers[DEDIPROG_ASYNC_TRANSFERS] = { NULL, };
struct libusb_transfer *transfer;
+ if (len == 0)
+ return 0;
+
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 reads, so chicken out. */
- if (!len)
- return 0;
- /* Command Read SPI Bulk. */
- if (is_new_prot()) {
- const uint8_t read_cmd[] = {
- count & 0xff,
- (count >> 8) & 0xff,
- 0,
- READ_MODE_FAST,
- 0,
- 0,
- start & 0xff,
- (start >> 8) & 0xff,
- (start >> 16) & 0xff,
- (start >> 24) & 0xff,
- };
-
- cmd_len = sizeof(read_cmd);
- ret = dediprog_write(CMD_READ, 0, 0, read_cmd, cmd_len);
- } else {
- const uint8_t read_cmd[] = {count & 0xff,
- (count >> 8) & 0xff,
- chunksize & 0xff,
- (chunksize >> 8) & 0xff};
-
- cmd_len = sizeof(read_cmd);
- ret = dediprog_write(CMD_READ, start % 0x10000, start / 0x10000, read_cmd, cmd_len);
+ 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;
}
- if (ret != cmd_len) {
+
+ uint8_t data_packet[command_packet_size];
+ unsigned int value, idx;
+ 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)) {
msg_perr("Command Read SPI Bulk failed, %i %s!\n", ret, libusb_error_name(ret));
return 1;
}
@@ -361,6 +406,7 @@
*/
/* Allocate bulk transfers. */
+ unsigned int i;
for (i = 0; i < min(DEDIPROG_ASYNC_TRANSFERS, count); ++i) {
transfers[i] = libusb_alloc_transfer(0);
if (!transfers[i]) {
@@ -371,7 +417,9 @@
/* Now transfer requested chunks using libusb's asynchronous interface. */
while (!status.error && (status.queued_idx < count)) {
- while ((status.queued_idx - status.finished_idx) < DEDIPROG_ASYNC_TRANSFERS) {
+ while ((status.queued_idx < count) &&
+ (status.queued_idx - status.finished_idx) < DEDIPROG_ASYNC_TRANSFERS)
+ {
transfer = transfers[status.queued_idx % DEDIPROG_ASYNC_TRANSFERS];
libusb_fill_bulk_transfer(transfer, dediprog_handle, 0x80 | dediprog_in_endpoint,
(unsigned char *)buf + status.queued_idx * chunksize, chunksize,
@@ -410,7 +458,7 @@
int ret;
/* chunksize must be 512, other sizes will NOT work at all. */
const unsigned int chunksize = 0x200;
- unsigned int residue = start % chunksize ? chunksize - start % chunksize : 0;
+ unsigned int residue = start % chunksize ? min(len, chunksize - start % chunksize) : 0;
unsigned int bulklen;
dediprog_set_leds(LED_BUSY);
@@ -431,7 +479,7 @@
goto err;
len -= residue + bulklen;
- if (len) {
+ if (len != 0) {
msg_pdbg("Slow read for partial block from 0x%x, length 0x%x\n",
start, len);
ret = spi_read_chunked(flash, buf + residue + bulklen,
@@ -457,27 +505,11 @@
static int dediprog_spi_bulk_write(struct flashctx *flash, const uint8_t *buf, unsigned int chunksize,
unsigned int start, unsigned int len, uint8_t dedi_spi_cmd)
{
- int ret, transferred;
- 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 count = len / chunksize;
- const unsigned char count_and_cmd_old[] = {count & 0xff, (count >> 8) & 0xff, 0x00, dedi_spi_cmd};
- const unsigned char count_and_cmd_new[] = {
- count & 0xff,
- (count >> 8) & 0xff,
- 0, /* used for 24-bit address support? */
- dedi_spi_cmd,
- JEDEC_BYTE_PROGRAM, /* FIXME: needs to be determined based on byte 3? */
- 0,
- start & 0xff,
- (start >> 8) & 0xff,
- (start >> 16) & 0xff,
- (start >> 24) & 0xff,
- };
- unsigned char usbbuf[512];
/*
* We should change this check to
@@ -497,37 +529,41 @@
}
/* No idea if the hardware can handle empty writes, so chicken out. */
- if (!len)
+ if (len == 0)
return 0;
- if (!is_new_prot()) {
- /* Command Write SPI Bulk. No idea which write command is used on the
- * SPI side.
- */
- ret = dediprog_write(CMD_WRITE, start % 0x10000, start / 0x10000,
- count_and_cmd_old, sizeof(count_and_cmd_old));
- if (ret != sizeof(count_and_cmd_old)) {
- msg_perr("Command Write SPI Bulk failed, %i %s!\n", ret,
- libusb_error_name(ret));
- return 1;
- }
- } else {
- /* Command Write SPI Bulk. No idea which write command is used on the
- * SPI side.
- */
- ret = dediprog_write(CMD_WRITE, 0, 0,
- count_and_cmd_new, sizeof(count_and_cmd_new));
- if (ret != sizeof(count_and_cmd_new)) {
- msg_perr("Command Write SPI Bulk failed, %i %s!\n", ret,
- libusb_error_name(ret));
- return 1;
- }
+
+ 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, 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));
+ return 1;
+ }
+
+ unsigned int i;
for (i = 0; i < count; i++) {
- memset(usbbuf, 0xff, sizeof(usbbuf));
+ unsigned char usbbuf[512];
memcpy(usbbuf, buf + i * chunksize, chunksize);
- ret = libusb_bulk_transfer(dediprog_handle, dediprog_out_endpoint,
- usbbuf, 512, &transferred, DEFAULT_TIMEOUT);
+ memset(usbbuf + chunksize, 0xff, sizeof(usbbuf) - chunksize); // fill up with 0xFF
+ int transferred;
+ ret = libusb_bulk_transfer(dediprog_handle, dediprog_out_endpoint, usbbuf, 512, &transferred,
+ DEFAULT_TIMEOUT);
if ((ret < 0) || (transferred != 512)) {
msg_perr("SPI bulk write failed, expected %i, got %i %s!\n",
512, ret, libusb_error_name(ret));
@@ -620,12 +656,17 @@
return 1;
}
- /* New protocol has the read flag as value while the old protocol had it in the index field. */
- if (is_new_prot()) {
- ret = dediprog_write(CMD_TRANSCEIVE, readcnt ? 1 : 0, 0, writearr, writecnt);
+ 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 (protocol() >= PROTOCOL_V2) {
+ idx = 0;
+ value = readcnt ? 0x1 : 0x0; // Indicate if we require a read
} else {
- ret = dediprog_write(CMD_TRANSCEIVE, 0, readcnt ? 1 : 0, writearr, writecnt);
+ idx = readcnt ? 0x1 : 0x0; // Indicate if we require a read
+ value = 0;
}
+ ret = dediprog_write(CMD_TRANSCEIVE, value, idx, writearr, writecnt);
if (ret != writecnt) {
msg_perr("Send SPI failed, expected %i, got %i %s!\n",
writecnt, ret, libusb_error_name(ret));
@@ -634,6 +675,24 @@
if (readcnt == 0)
return 0;
+ /* The specifications do state the possibility to set a timeout for transceive transactions.
+ * Apparently the "timeout" is a delay, and you can use long delays to accelerate writing - in case you
+ * can predict the time needed by the previous command or so (untested). In any case, using this
+ * "feature" to set sane-looking timouts for the read below will completely trash performance with
+ * SF600 and/or firmwares >= 6.0 while they seem to be benign on SF100 with firmwares <= 5.5.2. *shrug*
+ *
+ * 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 (protocol() >= PROTOCOL_V2) {
+ idx = 0;
+ value = min(read_timeout, 0xFF) | (0 << 8) ; // Timeout in lower byte, option in upper byte
+ } else {
+ idx = (0 & 0xFF); // Lower byte is option (0x01 = require SR, 0x02 keep CS low)
+ value = min(read_timeout, 0xFF); // Possibly two bytes but we play safe here
+ }
+ ret = dediprog_read(CMD_TRANSCEIVE, value, idx, readarr, readcnt);
+ */
ret = dediprog_read(CMD_TRANSCEIVE, 0, 0, readarr, readcnt);
if (ret != readcnt) {
msg_perr("Receive SPI failed, expected %i, got %i %s!\n",
@@ -646,8 +705,6 @@
static int dediprog_check_devicestring(void)
{
int ret;
- int fw[3];
- int sfnum;
char buf[0x11];
/* Command Receive Device String. */
@@ -666,8 +723,11 @@
msg_perr("Device not a SF100 or SF600!\n");
return 1;
}
- if (sscanf(buf, "SF%d V:%d.%d.%d ", &sfnum, &fw[0], &fw[1], &fw[2])
- != 4 || sfnum != dediprog_devicetype) {
+
+ int sfnum;
+ int fw[3];
+ if (sscanf(buf, "SF%d V:%d.%d.%d ", &sfnum, &fw[0], &fw[1], &fw[2]) != 4 ||
+ sfnum != dediprog_devicetype) {
msg_perr("Unexpected firmware version string '%s'\n", buf);
return 1;
}
@@ -676,7 +736,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;
}
@@ -920,7 +985,6 @@
{
msg_pspew("%s\n", __func__);
- dediprog_firmwareversion = FIRMWARE_VERSION(0, 0, 0);
dediprog_devicetype = DEV_UNKNOWN;
/* URB 28. Command Set SPI Voltage to 0. */
@@ -1075,7 +1139,7 @@
/* Set some 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_setup() has queried the device and set dediprog_firmwareversion. */
+ * dediprog_setup() has queried the device. */
dediprog_set_leds(LED_PASS | LED_BUSY);
/* FIXME: need to do this so buses_supported gets SPI */