summaryrefslogtreecommitdiffstats
path: root/dediprog.c
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2016-01-23 23:27:58 +0000
committerStefan Tauner <stefan.tauner@alumni.tuwien.ac.at>2016-01-23 23:27:58 +0000
commitae61651bfab63139493f5ac18348125e8d5baeb0 (patch)
tree4fcafc84ea8bd579b4cc21f8783cd8271ea00bb0 /dediprog.c
parent23e10b87801c22f34642895de8b0b726265eb016 (diff)
downloadast2050-flashrom-ae61651bfab63139493f5ac18348125e8d5baeb0.zip
ast2050-flashrom-ae61651bfab63139493f5ac18348125e8d5baeb0.tar.gz
dediprog: support new communication protocol, cleanup and enable by default
The new protocol changes some commands, so adjust the code to support these. Use helper functions to reduce duplication in libusb calls. Testing with real hardware showed that the maximum read size via the control endpoint is about 16 bytes although specification does not mention that. Dediprog SF600 is not supported yet. Based on the following chromiumos changes: Change-Id: Ibd1e27d9e8273ba879b9b5d95675b99596255c89 Change-Id: Ifc33e7b1eed5b0cb80f83458fa24741a577fa46a Additionally, some ideas from Alex for cleaner code were incorporated as well. Tested on an SF100 V4 with both firmware generations PREWing a M25PX80 and AT45DB041D (the latter is using dediprog_spi_send_command() instead of optimized functions which make it a good test vehicle). Corresponding to flashrom svn r1918. Signed-off-by: Simon Glass <sjg@chromium.org> Signed-off-by: David Hendricks <dhendrix@chromium.org> Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com> Signed-off-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at> Acked-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
Diffstat (limited to 'dediprog.c')
-rw-r--r--dediprog.c241
1 files changed, 130 insertions, 111 deletions
diff --git a/dediprog.c b/dediprog.c
index 996d2d7..d18ce36 100644
--- a/dediprog.c
+++ b/dediprog.c
@@ -40,7 +40,7 @@
#define FIRMWARE_VERSION(x,y,z) ((x << 16) | (y << 8) | z)
#define DEFAULT_TIMEOUT 3000
-#define REQTYPE_OTHER_OUT (USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER) /* 0x43 */
+#define REQTYPE_OTHER_OUT (USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER) /* 0x43 */
#define REQTYPE_OTHER_IN (USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER) /* 0xC3 */
#define REQTYPE_EP_OUT (USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT) /* 0x42 */
#define REQTYPE_EP_IN (USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT) /* 0xC2 */
@@ -120,9 +120,9 @@ enum dediprog_writemode {
WRITE_MODE_128B_PAGE = 5,
WRITE_MODE_PAGE_AT26DF041 = 6,
WRITE_MODE_SILICON_BLUE_FPGA = 7,
- WRITE_MODE_64B_PAGE_NUMONYX_PCM = 8, /* unit of length 512 bytes */
+ WRITE_MODE_64B_PAGE_NUMONYX_PCM = 8, /* unit of 512 bytes */
WRITE_MODE_4B_ADDR_256B_PAGE_PGM = 9,
- WRITE_MODE_32B_PAGE_PGM_MXIC_512K = 10, /* unit of length 512 bytes */
+ WRITE_MODE_32B_PAGE_PGM_MXIC_512K = 10, /* unit of 512 bytes */
WRITE_MODE_4B_ADDR_256B_PAGE_PGM_0x12 = 11,
WRITE_MODE_4B_ADDR_256B_PAGE_PGM_FLAGS = 12,
};
@@ -130,16 +130,26 @@ enum dediprog_writemode {
static int dediprog_firmwareversion = FIRMWARE_VERSION(0, 0, 0);
-#if 0
-/* Might be useful for other pieces of code as well. */
-static void print_hex(void *buf, size_t len)
+/* Returns true if firmware (and thus hardware) supports the "new" protocol */
+static bool is_new_prot(void)
{
- size_t i;
+ /* if (SF100) */
+ return dediprog_firmwareversion >= FIRMWARE_VERSION(5, 5, 0);
+ /* else if (SF600)
+ return dediprog_firmwareversion >= FIRMWARE_VERSION(6, 9, 0); */
+}
- for (i = 0; i < len; i++)
- msg_pdbg(" %02x", ((uint8_t *)buf)[i]);
+static int dediprog_read(enum dediprog_cmds cmd, unsigned int value, unsigned int idx, uint8_t *bytes, size_t size)
+{
+ return usb_control_msg(dediprog_handle, REQTYPE_EP_IN, cmd, value, idx,
+ (char *)bytes, size, DEFAULT_TIMEOUT);
+}
+
+static int dediprog_write(enum dediprog_cmds cmd, unsigned int value, unsigned int idx, const uint8_t *bytes, size_t size)
+{
+ return usb_control_msg(dediprog_handle, REQTYPE_EP_OUT, cmd, value, idx,
+ (char *)bytes, size, DEFAULT_TIMEOUT);
}
-#endif
/* Might be useful for other USB devices as well. static for now. */
/* device parameter allows user to specify one device of multiple installed */
@@ -166,24 +176,30 @@ static int dediprog_set_leds(int leds)
if (leds < LED_NONE || leds > LED_ALL)
leds = LED_ALL;
- /* Older Dediprogs with 2.x.x and 3.x.x firmware only had
- * two LEDs, and they were reversed. So map them around if
- * we have an old device. On those devices the LEDs map as
- * follows:
+ /* Older Dediprogs with 2.x.x and 3.x.x firmware only had two LEDs, assigned to different bits. So map
+ * them around if we have an old device. On those devices the LEDs map as follows:
* bit 2 == 0: green light is on.
- * bit 0 == 0: red light is on.
+ * bit 0 == 0: red light is on.
+ *
+ * Additionally, the command structure has changed with the "new" protocol.
+ *
+ * FIXME: take IO pins into account
*/
- int target_leds;
- if (dediprog_firmwareversion < FIRMWARE_VERSION(5,0,0)) {
- target_leds = ((leds & LED_ERROR) >> 2) |
- ((leds & LED_PASS) << 2);
+ int target_leds, ret;
+ if (is_new_prot()) {
+ target_leds = (leds ^ 7) << 8;
+ ret = dediprog_write(CMD_SET_IO_LED, target_leds, 0, NULL, 0);
} else {
- target_leds = leds;
+ if (dediprog_firmwareversion < FIRMWARE_VERSION(5, 0, 0)) {
+ target_leds = ((leds & LED_ERROR) >> 2) | ((leds & LED_PASS) << 2);
+ } else {
+ target_leds = leds;
+ }
+ target_leds ^= 7;
+
+ ret = dediprog_write(CMD_SET_IO_LED, 0x9, target_leds, NULL, 0);
}
- target_leds ^= 7;
- int ret = usb_control_msg(dediprog_handle, REQTYPE_EP_OUT, CMD_SET_IO_LED, 0x09, target_leds,
- NULL, 0x0, DEFAULT_TIMEOUT);
if (ret != 0x0) {
msg_perr("Command Set LED 0x%x failed (%s)!\n", leds, usb_strerror());
return 1;
@@ -222,8 +238,7 @@ static int dediprog_set_spi_voltage(int millivolt)
/* Wait some time as the original driver does. */
programmer_delay(200 * 1000);
}
- ret = usb_control_msg(dediprog_handle, REQTYPE_EP_OUT, CMD_SET_VCC, voltage_selector, 0,
- NULL, 0x0, DEFAULT_TIMEOUT);
+ ret = dediprog_write(CMD_SET_VCC, voltage_selector, 0, NULL, 0);
if (ret != 0x0) {
msg_perr("Command Set SPI Voltage 0x%x failed!\n",
voltage_selector);
@@ -263,8 +278,7 @@ static int dediprog_set_spi_speed(unsigned int spispeed_idx)
const struct dediprog_spispeeds *spispeed = &spispeeds[spispeed_idx];
msg_pdbg("SPI speed is %sHz\n", spispeed->name);
- int ret = usb_control_msg(dediprog_handle, REQTYPE_EP_OUT, CMD_SET_SPI_CLK, spispeed->speed, 0xff,
- NULL, 0x0, DEFAULT_TIMEOUT);
+ int ret = dediprog_write(CMD_SET_SPI_CLK, spispeed->speed, 0, NULL, 0);
if (ret != 0x0) {
msg_perr("Command Set SPI Speed 0x%x failed!\n", spispeed->speed);
return 1;
@@ -272,52 +286,66 @@ static int dediprog_set_spi_speed(unsigned int spispeed_idx)
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) {
+ /* 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 (is_new_prot()) {
+ *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;
+ } 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
* @return 0 on success, 1 on failure
*/
-static int dediprog_spi_bulk_read(struct flashctx *flash, uint8_t *buf,
- unsigned int start, unsigned int len)
+static int dediprog_spi_bulk_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len)
{
- int ret;
- unsigned int i;
/* chunksize must be 512, other sizes will NOT work at all. */
- const unsigned int chunksize = 0x200;
+ const unsigned int chunksize = 512;
const unsigned int count = len / chunksize;
- const char count_and_chunk[] = {count & 0xff,
- (count >> 8) & 0xff,
- chunksize & 0xff,
- (chunksize >> 8) & 0xff};
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);
+ 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)
+ if (len == 0)
return 0;
- /* Command Read SPI Bulk. No idea which read command is used on the
- * SPI side.
- */
- ret = usb_control_msg(dediprog_handle, REQTYPE_EP_OUT, CMD_READ, start % 0x10000,
- start / 0x10000, (char *)count_and_chunk,
- sizeof(count_and_chunk), DEFAULT_TIMEOUT);
- if (ret != sizeof(count_and_chunk)) {
- msg_perr("Command Read SPI Bulk failed, %i %s!\n", ret,
- usb_strerror());
+
+ /* Command packet size of protocols: new 10 B, old 5 B. */
+ uint8_t data_packet[is_new_prot() ? 10 : 5];
+ unsigned int value, idx;
+ fill_rw_cmd_payload(data_packet, count, READ_MODE_STD, &value, &idx, start);
+
+ 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, usb_strerror());
return 1;
}
+ unsigned int i;
for (i = 0; i < count; i++) {
ret = usb_bulk_read(dediprog_handle, 0x80 | dediprog_endpoint,
(char *)buf + i * chunksize, chunksize,
DEFAULT_TIMEOUT);
if (ret != chunksize) {
- msg_perr("SPI bulk read %i failed, expected %i, got %i "
- "%s!\n", i, chunksize, ret, usb_strerror());
+ msg_perr("SPI bulk read %i failed, expected %i, got %i %s!\n",
+ i, chunksize, ret, usb_strerror());
return 1;
}
}
@@ -325,8 +353,7 @@ static int dediprog_spi_bulk_read(struct flashctx *flash, uint8_t *buf,
return 0;
}
-static int dediprog_spi_read(struct flashctx *flash, uint8_t *buf,
- unsigned int start, unsigned int len)
+static int dediprog_spi_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len)
{
int ret;
/* chunksize must be 512, other sizes will NOT work at all. */
@@ -346,13 +373,12 @@ static int dediprog_spi_read(struct flashctx *flash, uint8_t *buf,
/* Round down. */
bulklen = (len - residue) / chunksize * chunksize;
- ret = dediprog_spi_bulk_read(flash, buf + residue, start + residue,
- bulklen);
+ ret = dediprog_spi_bulk_read(flash, buf + residue, start + residue, bulklen);
if (ret)
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,
@@ -378,15 +404,11 @@ err:
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;
- 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 char count_and_cmd[] = {count & 0xff, (count >> 8) & 0xff, 0x00, dedi_spi_cmd};
- char usbbuf[512];
/*
* We should change this check to
@@ -406,28 +428,28 @@ static int dediprog_spi_bulk_write(struct flashctx *flash, const uint8_t *buf, u
}
/* No idea if the hardware can handle empty writes, so chicken out. */
- if (!len)
+ if (len == 0)
return 0;
- /* Command Write SPI Bulk. No idea which write command is used on the
- * SPI side.
- */
- ret = usb_control_msg(dediprog_handle, REQTYPE_EP_OUT, CMD_WRITE, start % 0x10000, start / 0x10000,
- (char *)count_and_cmd, sizeof(count_and_cmd), DEFAULT_TIMEOUT);
- if (ret != sizeof(count_and_cmd)) {
+
+ /* Command packet size of protocols: new 10 B, old 5 B. */
+ uint8_t data_packet[is_new_prot() ? 10 : 5];
+ unsigned int value, idx;
+ fill_rw_cmd_payload(data_packet, count, dedi_spi_cmd, &value, &idx, start);
+ 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, %i %s!\n", ret,
usb_strerror());
return 1;
}
+ unsigned int i;
for (i = 0; i < count; i++) {
- memset(usbbuf, 0xff, sizeof(usbbuf));
+ char usbbuf[512];
memcpy(usbbuf, buf + i * chunksize, chunksize);
- ret = usb_bulk_write(dediprog_handle, dediprog_endpoint,
- usbbuf, 512,
- DEFAULT_TIMEOUT);
+ memset(usbbuf + chunksize, 0xff, sizeof(usbbuf) - chunksize); // fill up with 0xFF
+ 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());
+ msg_perr("SPI bulk write failed, expected %i, got %i %s!\n", 512, ret, usb_strerror());
return 1;
}
}
@@ -506,30 +528,45 @@ static int dediprog_spi_send_command(struct flashctx *flash,
int ret;
msg_pspew("%s, writecnt=%i, readcnt=%i\n", __func__, writecnt, readcnt);
- if (writecnt > UINT16_MAX) {
+ if (writecnt > flash->mst->spi.max_data_write) {
msg_perr("Invalid writecnt=%i, aborting.\n", writecnt);
return 1;
}
- if (readcnt > UINT16_MAX) {
+ if (readcnt > flash->mst->spi.max_data_read) {
msg_perr("Invalid readcnt=%i, aborting.\n", readcnt);
return 1;
}
- ret = usb_control_msg(dediprog_handle, REQTYPE_EP_OUT, CMD_TRANSCEIVE, 0, readcnt ? 0x1 : 0x0,
- (char *)writearr, writecnt, DEFAULT_TIMEOUT);
+ 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()) {
+ idx = 0;
+ value = readcnt ? 0x1 : 0x0; // Indicate if we require a read
+ } else {
+ 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, usb_strerror());
return 1;
}
- if (readcnt == 0)
+ if (readcnt == 0) // If we don't require a response, we are done here
return 0;
- ret = usb_control_msg(dediprog_handle, REQTYPE_EP_IN, CMD_TRANSCEIVE, 0, 0,
- (char *)readarr, readcnt, DEFAULT_TIMEOUT);
+ const uint8_t read_timeout = 10 + readcnt/512;
+ if (is_new_prot()) {
+ 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);
if (ret != readcnt) {
- msg_perr("Receive SPI failed, expected %i, got %i %s!\n",
- readcnt, ret, usb_strerror());
+ msg_perr("Receive SPI failed, expected %i, got %i %s!\n", readcnt, ret, usb_strerror());
return 1;
}
return 0;
@@ -541,20 +578,8 @@ static int dediprog_check_devicestring(void)
int fw[3];
char buf[0x11];
-#if 0
- /* Command Prepare Receive Device String. */
- ret = usb_control_msg(dediprog_handle, REQTYPE_OTHER_IN, 0x7, 0x0, 0xef03,
- buf, 0x1, DEFAULT_TIMEOUT);
- /* The char casting is needed to stop gcc complaining about an always true comparison. */
- if ((ret != 0x1) || (buf[0] != (char)0xff)) {
- msg_perr("Unexpected response to Command Prepare Receive Device"
- " String!\n");
- return 1;
- }
-#endif
/* Command Receive Device String. */
- ret = usb_control_msg(dediprog_handle, REQTYPE_EP_IN, CMD_READ_PROG_INFO, 0, 0,
- buf, 0x10, DEFAULT_TIMEOUT);
+ ret = dediprog_read(CMD_READ_PROG_INFO, 0, 0, (uint8_t *)buf, 0x10);
if (ret != 0x10) {
msg_perr("Incomplete/failed Command Receive Device String!\n");
return 1;
@@ -566,16 +591,16 @@ static int dediprog_check_devicestring(void)
return 1;
}
if (sscanf(buf, "SF100 V:%d.%d.%d ", &fw[0], &fw[1], &fw[2]) != 3) {
- msg_perr("Unexpected firmware version string!\n");
+ msg_perr("Unexpected firmware version string '%s'\n", buf);
return 1;
}
- /* Only these versions were tested. */
- if (fw[0] < 2 || fw[0] > 5) {
- msg_perr("Unexpected firmware version %d.%d.%d!\n", fw[0],
- fw[1], fw[2]);
+ /* Only these major versions were tested. */
+ if (fw[0] < 2 || fw[0] > 6) {
+ 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]);
+
return 0;
}
@@ -700,8 +725,8 @@ static int parse_voltage(char *voltage)
static const struct spi_master spi_master_dediprog = {
.type = SPI_CONTROLLER_DEDIPROG,
- .max_data_read = MAX_DATA_UNSPECIFIED,
- .max_data_write = MAX_DATA_UNSPECIFIED,
+ .max_data_read = 16, /* 18 seems to work fine as well, but 19 times out sometimes with FW 5.15. */
+ .max_data_write = 16,
.command = dediprog_spi_send_command,
.multicommand = default_spi_send_multicommand,
.read = dediprog_spi_read,
@@ -711,8 +736,6 @@ static const struct spi_master spi_master_dediprog = {
static int dediprog_shutdown(void *data)
{
- msg_pspew("%s\n", __func__);
-
dediprog_firmwareversion = FIRMWARE_VERSION(0, 0, 0);
/* URB 28. Command Set SPI Voltage to 0. */
@@ -730,7 +753,6 @@ static int dediprog_shutdown(void *data)
return 0;
}
-/* URB numbers refer to the first log ever captured. */
int dediprog_init(void)
{
struct usb_device *dev;
@@ -741,8 +763,6 @@ int dediprog_init(void)
long target = 1;
int i, ret;
- msg_pspew("%s\n", __func__);
-
spispeed = extract_programmer_param("spispeed");
if (spispeed) {
for (i = 0; spispeeds[i].name; ++i) {
@@ -861,7 +881,7 @@ int dediprog_init(void)
/* 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_setup() has queried the device and set dediprog_firmwareversion. */
+ * dediprog_check_devicestring() has queried the device and set dediprog_firmwareversion. */
dediprog_set_leds(LED_ALL);
/* Select target/socket, frequency and VCC. */
@@ -872,9 +892,8 @@ int dediprog_init(void)
return 1;
}
- register_spi_master(&spi_master_dediprog);
-
- dediprog_set_leds(LED_NONE);
+ if (register_spi_master(&spi_master_dediprog) || dediprog_set_leds(LED_NONE))
+ return 1;
return 0;
}
OpenPOWER on IntegriCloud