summaryrefslogtreecommitdiffstats
path: root/dediprog.c
diff options
context:
space:
mode:
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