summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2013-11-05 07:41:49 -0800
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2013-11-07 03:43:57 +0000
commita5d2fa9fb4fde97bced002c1ab886612a5b8f84a (patch)
tree63d448a9bf4a132f8140451c842284f6a1fd226b
parent6c126f559a30c5ebe65e868cc0dafa5d5cfb7c97 (diff)
downloadchrome-ec-a5d2fa9fb4fde97bced002c1ab886612a5b8f84a.tar.gz
ite: Add IT8380 In-system Programming tool
use the IT8380 SMB0 interface connected to the Servo FTDI chip to access the internal flash. The write-protect is not implemented. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> Signed-off-by: Alec Berg <alecaberg@chromium.org> BRANCH=none BUG=chrome-os-partner:23576 TEST=check waveforms on the Logic analyzer. Change-Id: Ic3402e4e8def731fe4f2fe93be254f5fd0982abf Reviewed-on: https://chromium-review.googlesource.com/175677 Reviewed-by: Vincent Palatin <vpalatin@chromium.org> Commit-Queue: Vincent Palatin <vpalatin@chromium.org> Tested-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--util/build.mk2
-rw-r--r--util/iteflash.c773
2 files changed, 774 insertions, 1 deletions
diff --git a/util/build.mk b/util/build.mk
index d4563caa08..e9922b4f24 100644
--- a/util/build.mk
+++ b/util/build.mk
@@ -13,4 +13,4 @@ host-util-common+=comm-lpc
else
host-util-common+=comm-i2c
endif
-build-util-bin=ec_uartd stm32mon
+build-util-bin=ec_uartd stm32mon iteflash
diff --git a/util/iteflash.c b/util/iteflash.c
new file mode 100644
index 0000000000..9cbfa1498c
--- /dev/null
+++ b/util/iteflash.c
@@ -0,0 +1,773 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * ITE83xx SoC in-system programming tool
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#include <ftdi.h>
+#pragma GCC diagnostic pop
+
+/* default USB device : Servo v2 */
+#define SERVO_USB_VID 0x18d1
+#define SERVO_USB_PID 0x5002
+#define SERVO_INTERFACE INTERFACE_B
+
+/* DBGR I2C addresses */
+#define I2C_CMD_ADDR 0x5A
+#define I2C_DATA_ADDR 0x35
+#define I2C_BLOCK_ADDR 0x79
+
+#define I2C_FREQ 200000
+
+/* I2C pins on the FTDI interface */
+#define SCL_BIT (1 << 0)
+#define SDA_BIT (1 << 1)
+
+/* Chip ID register value */
+#define CHIP_ID 0x8380
+
+/* Embedded flash page size */
+#define PAGE_SIZE 256
+
+/* JEDEC SPI Flash commands */
+#define SPI_CMD_PAGE_PROGRAM 0x02
+#define SPI_CMD_WRITE_DISABLE 0x04
+#define SPI_CMD_READ_STATUS 0x05
+#define SPI_CMD_WRITE_ENABLE 0x06
+#define SPI_CMD_FAST_READ 0x0B
+#define SPI_CMD_CHIP_ERASE 0xC7
+
+/* Size for FTDI outgoing buffer */
+#define FTDI_CMD_BUF_SIZE (1<<12)
+
+/* store custom parameters */
+const char *input_filename;
+const char *output_filename;
+static int usb_vid = SERVO_USB_VID;
+static int usb_pid = SERVO_USB_PID;
+static int usb_interface = SERVO_INTERFACE;
+static char *usb_serial;
+static int flash_size;
+
+/* debug traces : default OFF*/
+static int debug;
+
+/* optional command flags */
+enum {
+ FLAG_UNPROTECT = 0x01,
+ FLAG_ERASE = 0x02,
+};
+
+static int i2c_add_send_byte(struct ftdi_context *ftdi, uint8_t *buf,
+ uint8_t *ptr, uint8_t byte)
+{
+ int ret;
+ uint8_t ack;
+ uint8_t *b = ptr;
+
+ *b++ = MPSSE_DO_WRITE | MPSSE_BITMODE | MPSSE_WRITE_NEG;
+ *b++ = 0x07; *b++ = byte;
+ /* prepare for ACK */
+ *b++ = SET_BITS_LOW; *b++ = 0; *b++ = SCL_BIT;
+ /* read ACK */
+ *b++ = MPSSE_DO_READ | MPSSE_BITMODE | MPSSE_LSB;
+ *b++ = 0;
+ *b++ = SEND_IMMEDIATE;
+ ret = ftdi_write_data(ftdi, buf, b - buf);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write byte\n");
+ return ret;
+ }
+ ret = ftdi_read_data(ftdi, &ack, 1);
+ if (ret < 0 || (ack & 0x80) != 0) {
+ if (debug)
+ fprintf(stderr, "write ACK failed: %d - 0x%02x\n",
+ ret, ack);
+ return -ENXIO;
+ }
+ return 0;
+}
+
+static int i2c_add_recv_bytes(struct ftdi_context *ftdi, uint8_t *buf,
+ uint8_t *ptr, uint8_t *rbuf, int rcnt)
+{
+ int ret, i;
+ uint8_t *b = ptr;
+
+ for (i = 0; i < rcnt; i++) {
+ /* set SCL low */
+ *b++ = SET_BITS_LOW; *b++ = 0; *b++ = SCL_BIT;
+ /* read the byte on the wire */
+ *b++ = MPSSE_DO_READ; *b++ = 0; *b++ = 0;
+
+ if (i == rcnt - 1) {
+ /* NACK last byte */
+ *b++ = SET_BITS_LOW; *b++ = 0; *b++ = SCL_BIT;
+ *b++ = MPSSE_DO_WRITE | MPSSE_BITMODE | MPSSE_WRITE_NEG;
+ *b++ = 0; *b++ = 0xff; *b++ = SEND_IMMEDIATE;
+ } else {
+ /* ACK all other bytes */
+ *b++ = SET_BITS_LOW; *b++ = 0; *b++ = SCL_BIT | SDA_BIT;
+ *b++ = MPSSE_DO_WRITE | MPSSE_BITMODE | MPSSE_WRITE_NEG;
+ *b++ = 0; *b++ = 0; *b++ = SEND_IMMEDIATE;
+ }
+ }
+
+ ret = ftdi_write_data(ftdi, buf, b - buf);
+ if (ret < 0) {
+ fprintf(stderr, "failed to prepare read\n");
+ return ret;
+ }
+ ret = ftdi_read_data(ftdi, rbuf, rcnt);
+ if (ret < 0)
+ fprintf(stderr, "read byte failed\n");
+ return ret;
+}
+
+static int i2c_byte_transfer(struct ftdi_context *ftdi, uint8_t addr,
+ uint8_t *data, int write, int numbytes)
+{
+ int ret = 0, rets;
+ static uint8_t buf[FTDI_CMD_BUF_SIZE];
+ uint8_t *b = buf;
+
+ /* START condition */
+ /* SCL & SDA high */
+ *b++ = SET_BITS_LOW; *b++ = 0; *b++ = 0;
+ *b++ = SET_BITS_LOW; *b++ = 0; *b++ = 0;
+ /* SCL high, SDA low */
+ *b++ = SET_BITS_LOW; *b++ = 0; *b++ = SDA_BIT;
+ *b++ = SET_BITS_LOW; *b++ = 0; *b++ = SDA_BIT;
+ /* SCL low, SDA low */
+ *b++ = SET_BITS_LOW; *b++ = 0; *b++ = SCL_BIT | SDA_BIT;
+ *b++ = SET_BITS_LOW; *b++ = 0; *b++ = SCL_BIT | SDA_BIT;
+
+ /* send address */
+ ret = i2c_add_send_byte(ftdi, buf, b, (addr << 1) | (write ? 0 : 1));
+ if (ret < 0) {
+ if (debug)
+ fprintf(stderr, "address %02x failed\n", addr);
+ ret = -ENXIO;
+ goto exit_xfer;
+ }
+
+ b = buf;
+ /* WORKAROUND: force SDA before sending the 2nd byte */
+ *b++ = SET_BITS_LOW; *b++ = SDA_BIT; *b++ = SCL_BIT | SDA_BIT;
+ if (write) /* write data */
+ ret = i2c_add_send_byte(ftdi, buf, b, *data);
+ else /* read data */
+ ret = i2c_add_recv_bytes(ftdi, buf, b, data, numbytes);
+
+exit_xfer:
+ b = buf;
+ /* STOP condition */
+ /* SCL high, SDA low */
+ *b++ = SET_BITS_LOW; *b++ = 0; *b++ = SDA_BIT;
+ *b++ = SET_BITS_LOW; *b++ = 0; *b++ = SDA_BIT;
+ /* SCL high, SDA high */
+ *b++ = SET_BITS_LOW; *b++ = 0; *b++ = 0;
+ *b++ = SET_BITS_LOW; *b++ = 0; *b++ = 0;
+
+ rets = ftdi_write_data(ftdi, buf, b - buf);
+ if (rets < 0)
+ fprintf(stderr, "failed to send STOP\n");
+ return ret;
+}
+
+static int i2c_write_byte(struct ftdi_context *ftdi, uint8_t cmd, uint8_t data)
+{
+ int ret;
+
+ ret = i2c_byte_transfer(ftdi, I2C_CMD_ADDR, &cmd, 1, 1);
+ if (ret < 0)
+ return -EIO;
+ ret = i2c_byte_transfer(ftdi, I2C_DATA_ADDR, &data, 1, 1);
+ if (ret < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int i2c_read_byte(struct ftdi_context *ftdi, uint8_t cmd, uint8_t *data)
+{
+ int ret;
+
+ ret = i2c_byte_transfer(ftdi, I2C_CMD_ADDR, &cmd, 1, 1);
+ if (ret < 0)
+ return -EIO;
+ ret = i2c_byte_transfer(ftdi, I2C_DATA_ADDR, data, 0, 1);
+ if (ret < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int check_chipid(struct ftdi_context *ftdi)
+{
+ int ret;
+ uint8_t ver = 0xff;
+ uint16_t id = 0xffff;
+
+ ret = i2c_read_byte(ftdi, 0x00, (uint8_t *)&id + 1);
+ if (ret < 0)
+ return ret;
+ ret = i2c_read_byte(ftdi, 0x01, (uint8_t *)&id);
+ if (ret < 0)
+ return ret;
+ ret = i2c_read_byte(ftdi, 0x01, &ver);
+ if (ret < 0)
+ return ret;
+ if (id != CHIP_ID) {
+ fprintf(stderr, "Invalid chip id: %04x\n", id);
+ return -EINVAL;
+ }
+ /* compute embedded flash size from CHIPVER field */
+ flash_size = (128 + (ver & 0xF0)) * 1024;
+
+ printf("CHIPID %04x Flash size %d kB\n", id, flash_size / 1024);
+
+ return 0;
+}
+
+/* SPI Flash generic command */
+static int spi_flash_command(struct ftdi_context *ftdi, uint8_t cmd)
+{
+ int ret = 0;
+
+ ret |= i2c_write_byte(ftdi, 0x07, 0x7f);
+ ret |= i2c_write_byte(ftdi, 0x06, 0xff);
+ ret |= i2c_write_byte(ftdi, 0x05, 0xfe);
+ ret |= i2c_write_byte(ftdi, 0x04, 0x00);
+ ret |= i2c_write_byte(ftdi, 0x08, 0x00);
+ ret |= i2c_write_byte(ftdi, 0x05, 0xfd);
+ ret |= i2c_write_byte(ftdi, 0x08, cmd);
+
+ return ret ? -EIO : 0;
+}
+
+/* Poll SPI Flash Read Status register until BUSY is reset */
+static int spi_poll_busy(struct ftdi_context *ftdi)
+{
+ uint8_t reg = 0xff;
+
+ while (1) {
+ int ret = spi_flash_command(ftdi, SPI_CMD_READ_STATUS);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_read_byte(ftdi, 0x08, &reg);
+ if (ret < 0)
+ return ret;
+ if ((ret & 0x01) == 0)
+ break;
+ }
+ return 0;
+}
+
+static int config_i2c(struct ftdi_context *ftdi)
+{
+ int ret;
+ uint8_t buf[5];
+ uint16_t divisor;
+
+ ret = ftdi_set_latency_timer(ftdi, 16 /* ms */);
+ if (ret < 0)
+ fprintf(stderr, "Cannot set latency\n");
+
+ ret = ftdi_set_bitmode(ftdi, 0, BITMODE_RESET);
+ if (ret < 0) {
+ fprintf(stderr, "Cannot reset MPSSE\n");
+ return -EIO;
+ }
+ ret = ftdi_set_bitmode(ftdi, 0, BITMODE_MPSSE);
+ if (ret < 0) {
+ fprintf(stderr, "Cannot enable MPSSE\n");
+ return -EIO;
+ }
+
+ ret = ftdi_usb_purge_buffers(ftdi);
+ if (ret < 0)
+ fprintf(stderr, "Cannot purge buffers\n");
+
+ /* configure the clock */
+ divisor = (60000000 / (2 * I2C_FREQ * 3 / 2 /* 3-phase CLK */) - 1);
+ buf[0] = EN_3_PHASE;
+ buf[1] = DIS_DIV_5;
+ buf[2] = TCK_DIVISOR;
+ buf[3] = divisor & 0xff;
+ buf[4] = divisor >> 8;
+ ret = ftdi_write_data(ftdi, buf, sizeof(buf));
+ return ret;
+}
+
+/* Special waveform definition */
+#define SPECIAL_LEN_USEC 50000ULL /* us */
+#define SPECIAL_FREQ 400000ULL
+
+#define SPECIAL_PATTERN 0x0000020301010302ULL
+
+#define MSEC 1000
+#define USEC 1000000
+
+#define SPECIAL_BUFFER_SIZE \
+ (((SPECIAL_LEN_USEC * SPECIAL_FREQ * 2 / USEC) + 7) & ~7)
+
+static int send_special_waveform(struct ftdi_context *ftdi)
+{
+ int ret;
+ int i;
+ uint64_t *wave;
+ uint8_t release_lines[] = {SET_BITS_LOW, 0, 0};
+
+ wave = malloc(SPECIAL_BUFFER_SIZE);
+
+ printf("Waiting for the EC power-on sequence ...");
+ fflush(stdout);
+
+retry:
+ /* Reset the FTDI into a known state */
+ ret = ftdi_set_bitmode(ftdi, 0xFF, BITMODE_RESET);
+ if (ret != 0) {
+ fprintf(stderr, "failed to reset FTDI\n");
+ goto special_failed;
+ }
+
+ /*
+ * set the clock divider,
+ * so we output a new bitbang value every 2.5us.
+ */
+ ret = ftdi_set_baudrate(ftdi, 160000);
+ if (ret != 0) {
+ fprintf(stderr, "failed to set bitbang clock\n");
+ goto special_failed;
+ }
+
+ /* Enable asynchronous bit-bang mode */
+ ret = ftdi_set_bitmode(ftdi, 0xFF, BITMODE_BITBANG);
+ if (ret != 0) {
+ fprintf(stderr, "failed to set bitbang mode\n");
+ goto special_failed;
+ }
+
+ /* fill the buffer with the waveform pattern */
+ for (i = 0; i < SPECIAL_BUFFER_SIZE / sizeof(uint64_t); i++)
+ wave[i] = SPECIAL_PATTERN;
+
+ ret = ftdi_write_data(ftdi, (uint8_t *)wave, SPECIAL_BUFFER_SIZE);
+ if (ret < 0)
+ fprintf(stderr, "Cannot output special waveform\n");
+
+ /* clean everything to go back to regular I2C communication */
+ ftdi_usb_purge_buffers(ftdi);
+ ftdi_set_bitmode(ftdi, 0xff, BITMODE_RESET);
+ config_i2c(ftdi);
+ ftdi_write_data(ftdi, release_lines, sizeof(release_lines));
+
+ /* wait for PLL stable for 5ms (plus remaining USB transfers) */
+ usleep(10 * MSEC);
+
+ /* if we cannot communicate, retry the sequence */
+ if (check_chipid(ftdi) < 0)
+ goto retry;
+
+special_failed:
+ printf("Done.\n");
+ free(wave);
+ return ret;
+}
+
+static int windex;
+static const char wheel[] = {'|', '/', '-', '\\' };
+static void draw_spinner(uint32_t remaining, uint32_t size)
+{
+ int percent = (size - remaining)*100/size;
+ printf("\r%c%3d%%", wheel[windex++], percent);
+ windex %= sizeof(wheel);
+}
+
+int command_read_pages(struct ftdi_context *ftdi, uint32_t address,
+ uint32_t size, uint8_t *buffer)
+{
+ int res;
+ uint32_t remaining = size;
+ int cnt;
+ uint16_t page;
+
+ while (remaining) {
+ uint8_t cmd = 0x9;
+
+ cnt = (remaining > PAGE_SIZE) ? PAGE_SIZE : remaining;
+ page = address / PAGE_SIZE;
+
+ draw_spinner(remaining, size);
+ /* Fast Read command */
+ res = spi_flash_command(ftdi, SPI_CMD_FAST_READ);
+ if (res < 0)
+ goto failed_read;
+ res = i2c_write_byte(ftdi, 0x08, page >> 8);
+ res += i2c_write_byte(ftdi, 0x08, page & 0xff);
+ res += i2c_write_byte(ftdi, 0x08, 0x00);
+ res += i2c_write_byte(ftdi, 0x08, 0x00);
+ if (res < 0) {
+ fprintf(stderr, "page address set failed\n");
+ goto failed_read;
+ }
+
+ /* read page data */
+ res = i2c_byte_transfer(ftdi, I2C_CMD_ADDR, &cmd, 1, 1);
+ res = i2c_byte_transfer(ftdi, I2C_BLOCK_ADDR, buffer, 0, cnt);
+ if (res < 0) {
+ fprintf(stderr, "page data read failed\n");
+ goto failed_read;
+ }
+
+ address += cnt;
+ remaining -= cnt;
+ buffer += cnt;
+ }
+ res = size;
+
+failed_read:
+
+ return res;
+}
+
+int command_write_pages(struct ftdi_context *ftdi, uint32_t address,
+ uint32_t size, uint8_t *buffer)
+{
+ int res;
+ uint32_t remaining = size;
+ int cnt;
+ uint16_t page;
+
+ res = spi_flash_command(ftdi, SPI_CMD_WRITE_ENABLE);
+ if (res < 0) {
+ fprintf(stderr, "Flash write enable FAILED (%d)\n", res);
+ goto failed_write;
+ }
+
+ while (remaining) {
+ uint8_t cmd = 0xA;
+ int i;
+
+ cnt = (remaining > PAGE_SIZE) ? PAGE_SIZE : remaining;
+ page = address / PAGE_SIZE;
+
+ draw_spinner(remaining, size);
+
+ /* Program Page command */
+ res = spi_flash_command(ftdi, SPI_CMD_PAGE_PROGRAM);
+ if (res < 0)
+ goto failed_write;
+ /* send page address */
+ res = i2c_write_byte(ftdi, 0x08, page >> 8);
+ res = i2c_write_byte(ftdi, 0x08, page & 0xff);
+ res = i2c_write_byte(ftdi, 0x08, 0x00);
+ if (res < 0) {
+ fprintf(stderr, "page address set failed\n");
+ goto failed_write;
+ }
+ /* write page data */
+ res = i2c_byte_transfer(ftdi, I2C_CMD_ADDR, &cmd, 1, 1);
+ for (i = 0; i < cnt; i++, buffer++) {
+ res = i2c_byte_transfer(ftdi, I2C_DATA_ADDR, buffer,
+ 1, 1);
+ if (res < 0) {
+ fprintf(stderr, "page data write failed\n");
+ goto failed_write;
+ }
+ }
+
+ address += cnt;
+ remaining -= cnt;
+ }
+ res = size;
+
+failed_write:
+ if (spi_flash_command(ftdi, SPI_CMD_WRITE_DISABLE) < 0)
+ fprintf(stderr, "Flash write disable FAILED\n");
+
+ return res;
+}
+
+int command_write_unprotect(struct ftdi_context *ftdi)
+{
+ /* TODO(http://crosbug.com/p/23576): implement me */
+ return 0;
+}
+
+int command_erase(struct ftdi_context *ftdi, uint32_t len, uint32_t off)
+{
+ int res;
+
+ if (off != 0 || len != flash_size) {
+ fprintf(stderr, "Only full chip erase is supported\n");
+ return -EINVAL;
+ }
+
+ res = spi_flash_command(ftdi, SPI_CMD_WRITE_ENABLE);
+ if (res < 0) {
+ fprintf(stderr, "Flash write enable FAILED (%d)\n", res);
+ goto failed_erase;
+ }
+
+ res = spi_flash_command(ftdi, SPI_CMD_CHIP_ERASE);
+ if (res < 0)
+ fprintf(stderr, "Flash chip erase FAILED (%d)\n", res);
+ res = spi_poll_busy(ftdi);
+ if (res < 0)
+ fprintf(stderr, "Flash BUSY polling FAILED (%d)\n", res);
+
+failed_erase:
+ if (spi_flash_command(ftdi, SPI_CMD_WRITE_DISABLE) < 0)
+ fprintf(stderr, "Flash write disable FAILED\n");
+
+ return res;
+}
+
+/* Return zero on success, a negative error value on failures. */
+int read_flash(struct ftdi_context *ftdi, const char *filename,
+ uint32_t offset, uint32_t size)
+{
+ int res;
+ FILE *hnd;
+ uint8_t *buffer = malloc(size);
+
+ if (!buffer) {
+ fprintf(stderr, "Cannot allocate %d bytes\n", size);
+ return -ENOMEM;
+ }
+
+ hnd = fopen(filename, "w");
+ if (!hnd) {
+ fprintf(stderr, "Cannot open file %s for writing\n", filename);
+ free(buffer);
+ return -EIO;
+ }
+
+ if (!size)
+ size = flash_size;
+ printf("Reading %d bytes at 0x%08x\n", size, offset);
+ res = command_read_pages(ftdi, offset, size, buffer);
+ if (res > 0) {
+ if (fwrite(buffer, res, 1, hnd) != 1)
+ fprintf(stderr, "Cannot write %s\n", filename);
+ }
+ printf("\r %d bytes read.\n", res);
+
+ fclose(hnd);
+ free(buffer);
+ return (res < 0) ? res : 0;
+}
+
+/* Return zero on success, a negative error value on failures. */
+int write_flash(struct ftdi_context *ftdi, const char *filename,
+ uint32_t offset)
+{
+ int res, written;
+ FILE *hnd;
+ int size = flash_size;
+ uint8_t *buffer = malloc(size);
+
+ if (!buffer) {
+ fprintf(stderr, "Cannot allocate %d bytes\n", size);
+ return -ENOMEM;
+ }
+
+ hnd = fopen(filename, "r");
+ if (!hnd) {
+ fprintf(stderr, "Cannot open file %s for reading\n", filename);
+ free(buffer);
+ return -EIO;
+ }
+ res = fread(buffer, 1, size, hnd);
+ if (res <= 0) {
+ fprintf(stderr, "Cannot read %s\n", filename);
+ free(buffer);
+ return -EIO;
+ }
+ fclose(hnd);
+
+ printf("Writing %d bytes at 0x%08x\n", res, offset);
+ written = command_write_pages(ftdi, offset, res, buffer);
+ if (written != res) {
+ fprintf(stderr, "Error writing to flash\n");
+ free(buffer);
+ return -EIO;
+ }
+ printf("\rDone.\n");
+
+ free(buffer);
+ return 0;
+}
+
+static struct ftdi_context *open_ftdi_device(int vid, int pid,
+ int interface, char *serial)
+{
+ struct ftdi_context *ftdi;
+ int ret;
+
+ ftdi = ftdi_new();
+ if (!ftdi) {
+ fprintf(stderr, "Cannot allocate context memory\n");
+ return NULL;
+ }
+
+ ret = ftdi_set_interface(ftdi, interface);
+ if (ret < 0) {
+ fprintf(stderr, "cannot set ftdi interface %d: %s(%d)\n",
+ interface, ftdi_get_error_string(ftdi), ret);
+ goto open_failed;
+ }
+ ret = ftdi_usb_open_desc(ftdi, vid, pid, NULL, serial);
+ if (ret < 0) {
+ fprintf(stderr, "unable to open ftdi device: %s(%d)\n",
+ ftdi_get_error_string(ftdi), ret);
+ goto open_failed;
+ }
+ return ftdi;
+
+open_failed:
+ ftdi_free(ftdi);
+ return NULL;
+}
+
+static const struct option longopts[] = {
+ {"debug", 0, 0, 'd'},
+ {"product", 1, 0, 'p'},
+ {"vendor", 1, 0, 'v'},
+ {"interface", 1, 0, 'i'},
+ {"serial", 1, 0, 's'},
+ {"read", 1, 0, 'r'},
+ {"write", 1, 0, 'w'},
+ {"erase", 0, 0, 'e'},
+ {"help", 0, 0, 'h'},
+ {"unprotect", 0, 0, 'u'},
+ {NULL, 0, 0, 0}
+};
+
+void display_usage(char *program)
+{
+ fprintf(stderr, "Usage: %s [-d] [-v <VID>] [-p <PID>] [-i <1|2>] "
+ "[-s <serial>] [-u] [-e] [-r <file>] [-w <file>]\n", program);
+ fprintf(stderr, "--d[ebug] : output debug traces\n");
+ fprintf(stderr, "--v[endor] <0x1234> : USB vendor ID\n");
+ fprintf(stderr, "--p[roduct] <0x1234> : USB product ID\n");
+ fprintf(stderr, "--s[erial] <serialname> : USB serial string\n");
+ fprintf(stderr, "--i[interface] <1> : FTDI interface: A=1, B=2, ...\n");
+ fprintf(stderr, "--u[nprotect] : remove flash write protect\n");
+ fprintf(stderr, "--e[rase] : erase all the flash content\n");
+ fprintf(stderr, "--r[ead] <file> : read the flash content and "
+ "write it into <file>\n");
+ fprintf(stderr, "--w[rite] <file> : read <file> and "
+ "write it to flash\n");
+
+ exit(2);
+}
+
+int parse_parameters(int argc, char **argv)
+{
+ int opt, idx;
+ int flags = 0;
+
+ while ((opt = getopt_long(argc, argv, "dv:p:i:s:ehr:w:u?",
+ longopts, &idx)) != -1) {
+ switch (opt) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'v':
+ usb_vid = strtol(optarg, NULL, 16);
+ break;
+ case 'p':
+ usb_pid = strtol(optarg, NULL, 16);
+ break;
+ case 'i':
+ usb_interface = atoi(optarg);
+ break;
+ case 's':
+ usb_serial = optarg;
+ break;
+ case 'e':
+ flags |= FLAG_ERASE;
+ break;
+ case 'h':
+ case '?':
+ display_usage(argv[0]);
+ break;
+ case 'r':
+ input_filename = optarg;
+ break;
+ case 'w':
+ output_filename = optarg;
+ break;
+ case 'u':
+ flags |= FLAG_UNPROTECT;
+ break;
+ }
+ }
+ return flags;
+}
+
+int main(int argc, char **argv)
+{
+ void *hnd;
+ int ret = 1;
+ int flags;
+
+ /* Parse command line options */
+ flags = parse_parameters(argc, argv);
+
+ /* Open the USB device */
+ hnd = open_ftdi_device(usb_vid, usb_pid, usb_interface, usb_serial);
+ if (hnd == NULL)
+ return 1;
+
+ /* Trigger embedded monitor detection */
+ if (send_special_waveform(hnd) < 0)
+ goto terminate;
+
+ if (config_i2c(hnd) < 0)
+ goto terminate;
+
+ if (check_chipid(hnd) < 0)
+ goto terminate;
+
+ if (flags & FLAG_UNPROTECT)
+ command_write_unprotect(hnd);
+
+ if (flags & FLAG_ERASE || output_filename)
+ command_erase(hnd, flash_size, 0);
+
+ if (input_filename) {
+ ret = read_flash(hnd, input_filename, 0, flash_size);
+ if (ret)
+ goto terminate;
+ }
+
+ if (output_filename) {
+ ret = write_flash(hnd, output_filename, 0);
+ if (ret)
+ goto terminate;
+ }
+
+ /* Normal exit */
+ ret = 0;
+terminate:
+ /* Close the FTDI USB handle */
+ ftdi_usb_close(hnd);
+ ftdi_free(hnd);
+ return ret;
+}