/* 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 #include #include #include #include #include #include #include #pragma GCC diagnostic ignored "-Wstrict-prototypes" #include #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 400000 /* 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 /* Embedded flash block write size */ #define BLOCK_WRITE_SIZE 65536 /* Embedded flash number of pages in a sector erase */ #define SECTOR_ERASE_PAGES 4 /* 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 0x60 #define SPI_CMD_SECTOR_ERASE 0xD7 #define SPI_CMD_WORD_PROGRAM 0xAD /* 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, }; /* number of bytes to send consecutively before checking for ACKs */ #define TX_BUFFER_LIMIT 32 static int i2c_add_send_byte(struct ftdi_context *ftdi, uint8_t *buf, uint8_t *ptr, uint8_t *tbuf, int tcnt) { int ret, i, j; int tx_buffered = 0; static uint8_t ack[TX_BUFFER_LIMIT]; uint8_t *b = ptr; uint8_t failed_ack = 0; for (i = 0; i < tcnt; i++) { /* WORKAROUND: force SDA before sending the next byte */ *b++ = SET_BITS_LOW; *b++ = SDA_BIT; *b++ = SCL_BIT | SDA_BIT; /* write byte */ *b++ = MPSSE_DO_WRITE | MPSSE_BITMODE | MPSSE_WRITE_NEG; *b++ = 0x07; *b++ = *tbuf++; /* 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; tx_buffered++; /* * On the last byte, or every TX_BUFFER_LIMIT bytes, read the * ACK bits. */ if (i == tcnt-1 || (tx_buffered == TX_BUFFER_LIMIT)) { /* write data */ ret = ftdi_write_data(ftdi, buf, b - buf); if (ret < 0) { fprintf(stderr, "failed to write byte\n"); return ret; } /* read ACK bits */ ret = ftdi_read_data(ftdi, &ack[0], tx_buffered); for (j = 0; j < tx_buffered; j++) { if ((ack[j] & 0x80) != 0) failed_ack = ack[j]; } /* check ACK bits */ if (ret < 0 || failed_ack) { if (debug) fprintf(stderr, "write ACK fail: %d, 0x%02x\n", ret, failed_ack); return -ENXIO; } /* reset for next set of transactions */ b = ptr; tx_buffered = 0; } } 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; uint8_t slave_addr; /* 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 */ slave_addr = (addr << 1) | (write ? 0 : 1); ret = i2c_add_send_byte(ftdi, buf, b, &slave_addr, 1); if (ret < 0) { if (debug) fprintf(stderr, "address %02x failed\n", addr); ret = -ENXIO; goto exit_xfer; } b = buf; if (write) /* write data */ ret = i2c_add_send_byte(ftdi, buf, b, data, numbytes); 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, 0x02, &ver); if (ret < 0) return ret; if ((id & 0xff00) != (CHIP_ID & 0xff00)) { 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, CHIPVER %02x, Flash size %d kB\n", id, ver, flash_size / 1024); return 0; } /* DBGR Reset*/ static int dbgr_reset(struct ftdi_context *ftdi) { int ret = 0; /* Reset CPU only, and we keep power state until flashing is done. */ ret |= i2c_write_byte(ftdi, 0x2f, 0x20); ret |= i2c_write_byte(ftdi, 0x2e, 0x06); ret |= i2c_write_byte(ftdi, 0x30, 0x40); ret |= i2c_write_byte(ftdi, 0x27, 0x80); if (ret < 0) printf("DBGR RESET FAILED\n"); return 0; } static int exit_dbgr_mode(struct ftdi_context *ftdi) { uint8_t val; int ret = 0; /* We have to exit dbgr mode so that EC won't hold I2C bus. */ ret |= i2c_write_byte(ftdi, 0x2f, 0x1c); ret |= i2c_write_byte(ftdi, 0x2e, 0x08); ret |= i2c_read_byte(ftdi, 0x30, &val); ret |= i2c_write_byte(ftdi, 0x30, (val | (1 << 4))); /* * NOTE: * We won't be able to send any commands to EC * if we have exit dbgr mode. * We do a cold reset for EC after flashing. */ printf("=== EXIT DBGR MODE %s ===\n", (ret < 0) ? "FAILED" : "DONE"); return 0; } /* Enter follow mode and FSCE# high level */ static int spi_flash_follow_mode(struct ftdi_context *ftdi, char *desc) { 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 = (ret ? -EIO : 0); if (ret < 0) fprintf(stderr, "Flash %s enter follow mode FAILED (%d)\n", desc, ret); return ret; } /* Exit follow mode */ static int spi_flash_follow_mode_exit(struct ftdi_context *ftdi, char *desc) { int ret = 0; ret |= i2c_write_byte(ftdi, 0x07, 0x00); ret |= i2c_write_byte(ftdi, 0x06, 0x00); ret = (ret ? -EIO : 0); if (ret < 0) fprintf(stderr, "Flash %s exit follow mode FAILED (%d)\n", desc, ret); return ret; } /* SPI Flash generic command, short version */ static int spi_flash_command_short(struct ftdi_context *ftdi, uint8_t cmd, char *desc) { int ret = 0; ret |= i2c_write_byte(ftdi, 0x05, 0xfe); ret |= i2c_write_byte(ftdi, 0x08, 0x00); ret |= i2c_write_byte(ftdi, 0x05, 0xfd); ret |= i2c_write_byte(ftdi, 0x08, cmd); ret = (ret ? -EIO : 0); if (ret < 0) fprintf(stderr, "Flash CMD %s FAILED (%d)\n", desc, ret); return ret; } /* SPI Flash set erase page */ static int spi_flash_set_erase_page(struct ftdi_context *ftdi, int page, char *desc) { int ret = 0; ret |= i2c_write_byte(ftdi, 0x08, page >> 8); ret |= i2c_write_byte(ftdi, 0x08, page & 0xff); ret |= i2c_write_byte(ftdi, 0x08, 0); ret = (ret ? -EIO : 0); if (ret < 0) fprintf(stderr, "Flash %s set page FAILED (%d)\n", desc, ret); return ret; } /* Poll SPI Flash Read Status register until BUSY is reset */ static int spi_poll_busy(struct ftdi_context *ftdi, char *desc) { uint8_t reg = 0xff; int ret = -EIO; if (spi_flash_command_short(ftdi, SPI_CMD_READ_STATUS, "read status for busy bit") < 0) { fprintf(stderr, "Flash %s wait busy cleared FAILED\n", desc); goto failed_read_status; } while (1) { if (i2c_byte_transfer(ftdi, I2C_DATA_ADDR, ®, 0, 1) < 0) { fprintf(stderr, "Flash polling busy cleared FAILED\n"); break; } if ((reg & 0x01) == 0) { /* busy bit cleared */ ret = 0; break; } } failed_read_status: return ret; } static int spi_check_write_enable(struct ftdi_context *ftdi, char *desc) { uint8_t reg = 0xff; int ret = -EIO; if (spi_flash_command_short(ftdi, SPI_CMD_READ_STATUS, "read status for write enable bit") < 0) { fprintf(stderr, "Flash %s wait WE FAILED\n", desc); goto failed_read_status; } while (1) { if (i2c_byte_transfer(ftdi, I2C_DATA_ADDR, ®, 0, 1) < 0) { fprintf(stderr, "Flash polling WE FAILED\n"); break; } if ((reg & 0x03) == 2) { /* busy bit cleared and WE bit set */ ret = 0; break; } } failed_read_status: return ret; } 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 SPECIAL_PATTERN_SDA_L_SCL_L 0x0000000000000000ULL #define SPECIAL_PATTERN_SDA_H_SCL_L 0x0202020202020202ULL #define SPECIAL_PATTERN_SDA_L_SCL_H 0x0101010101010101ULL #define SPECIAL_PATTERN_SDA_H_SCL_H 0x0303030303030303ULL #define TICK_COUNT 24 #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; } /* do usb special waveform */ wave[0] = 0x0; ftdi_write_data(ftdi, (uint8_t *)wave, 1); usleep(5000); /* program each special tick */ for (i = 0; i < TICK_COUNT; ) { wave[i++] = SPECIAL_PATTERN_SDA_L_SCL_L; wave[i++] = SPECIAL_PATTERN_SDA_H_SCL_L; wave[i++] = SPECIAL_PATTERN_SDA_L_SCL_L; } wave[19] = SPECIAL_PATTERN_SDA_H_SCL_H; /* fill the buffer with the waveform pattern */ for (i = TICK_COUNT; 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) { sleep(1); 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 = -EIO; uint32_t remaining = size; int cnt; uint16_t page; if (spi_flash_follow_mode(ftdi, "fast read") < 0) goto failed_read; while (remaining) { uint8_t cmd = 0x9; cnt = (remaining > PAGE_SIZE) ? PAGE_SIZE : remaining; page = address / PAGE_SIZE; draw_spinner(remaining, size); /* Fast Read command */ if (spi_flash_command_short(ftdi, SPI_CMD_FAST_READ, "fast read") < 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; } /* No error so far */ res = size; failed_read: if (spi_flash_follow_mode_exit(ftdi, "fast read") < 0) res = -EIO; return res; } int command_write_pages(struct ftdi_context *ftdi, uint32_t address, uint32_t size, uint8_t *buffer) { int res = -EIO; uint32_t remaining = size; int cnt; uint8_t page; uint8_t cmd; if (spi_flash_follow_mode(ftdi, "AAI write") < 0) goto failed_write; while (remaining) { cnt = (remaining > BLOCK_WRITE_SIZE) ? BLOCK_WRITE_SIZE : remaining; page = address / BLOCK_WRITE_SIZE; draw_spinner(remaining, size); /* Write enable */ if (spi_flash_command_short(ftdi, SPI_CMD_WRITE_ENABLE, "write enable for AAI write") < 0) goto failed_write; /* Check write enable bit */ if (spi_check_write_enable(ftdi, "AAI write") < 0) goto failed_write; /* Setup write */ if (spi_flash_command_short(ftdi, SPI_CMD_WORD_PROGRAM, "AAI write") < 0) goto failed_write; /* Set page */ cmd = 0; res = i2c_byte_transfer(ftdi, I2C_DATA_ADDR, &page, 1, 1); res |= i2c_byte_transfer(ftdi, I2C_DATA_ADDR, &cmd, 1, 1); res |= i2c_byte_transfer(ftdi, I2C_DATA_ADDR, &cmd, 1, 1); if (res < 0) { fprintf(stderr, "Flash write set page FAILED (%d)\n", res); goto failed_write; } /* Wait until not busy */ if (spi_poll_busy(ftdi, "AAI write") < 0) goto failed_write; /* Write up to BLOCK_WRITE_SIZE data */ res = i2c_write_byte(ftdi, 0x10, 0x20); res = i2c_byte_transfer(ftdi, I2C_BLOCK_ADDR, buffer, 1, cnt); buffer += cnt; if (res < 0) { fprintf(stderr, "Flash data write failed\n"); goto failed_write; } cmd = 0xff; res = i2c_byte_transfer(ftdi, I2C_DATA_ADDR, &cmd, 1, 1); res |= i2c_write_byte(ftdi, 0x10, 0x00); if (res < 0) { fprintf(stderr, "Flash end data write FAILED (%d)\n", res); goto failed_write; } /* Write disable */ if (spi_flash_command_short(ftdi, SPI_CMD_WRITE_DISABLE, "write disable for AAI write") < 0) goto failed_write; /* Wait until available */ if (spi_poll_busy(ftdi, "write disable for AAI write") < 0) goto failed_write; address += cnt; remaining -= cnt; } /* No error so far */ res = size; failed_write: if (spi_flash_command_short(ftdi, SPI_CMD_WRITE_DISABLE, "write disable exit AAI write") < 0) res = -EIO; if (spi_flash_follow_mode_exit(ftdi, "AAI write") < 0) res = -EIO; 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 = -EIO; int page = 0; uint32_t remaining = len; printf("Erasing chip...\n"); if (off != 0 || len != flash_size) { fprintf(stderr, "Only full chip erase is supported\n"); return -EINVAL; } if (spi_flash_follow_mode(ftdi, "erase") < 0) goto failed_erase; while (remaining) { draw_spinner(remaining, len); if (spi_flash_command_short(ftdi, SPI_CMD_WRITE_ENABLE, "write enable for erase") < 0) goto failed_erase; if (spi_check_write_enable(ftdi, "erase") < 0) goto failed_erase; /* do chip erase */ if (remaining == flash_size) { if (spi_flash_command_short(ftdi, SPI_CMD_CHIP_ERASE, "chip erase") < 0) goto failed_erase; goto wait_busy_cleared; } /* do sector erase */ if (spi_flash_command_short(ftdi, SPI_CMD_SECTOR_ERASE, "sector erase") < 0) goto failed_erase; if (spi_flash_set_erase_page(ftdi, page, "sector erase") < 0) goto failed_erase; wait_busy_cleared: if (spi_poll_busy(ftdi, "erase") < 0) goto failed_erase; if (spi_flash_command_short(ftdi, SPI_CMD_WRITE_DISABLE, "write disable for erase") < 0) goto failed_erase; if (remaining == flash_size) { remaining = 0; draw_spinner(remaining, len); } else { page += SECTOR_ERASE_PAGES; remaining -= SECTOR_ERASE_PAGES * PAGE_SIZE; } } /* No error so far */ res = 0; failed_erase: if (spi_flash_command_short(ftdi, SPI_CMD_WRITE_DISABLE, "write disable exit erase") < 0) res = -EIO; if (spi_flash_follow_mode_exit(ftdi, "erase") < 0) res = -EIO; printf("\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; } /* Return zero on success, a negative error value on failures. */ int verify_flash(struct ftdi_context *ftdi, const char *filename, uint32_t offset) { int res; int file_size; FILE *hnd; uint8_t *buffer = malloc(flash_size); uint8_t *buffer2 = malloc(flash_size); if (!buffer || !buffer2) { fprintf(stderr, "Cannot allocate %d bytes\n", flash_size); free(buffer); free(buffer2); return -ENOMEM; } hnd = fopen(filename, "r"); if (!hnd) { fprintf(stderr, "Cannot open file %s for reading\n", filename); res = -EIO; goto exit; } file_size = fread(buffer, 1, flash_size, hnd); fclose(hnd); if (file_size <= 0) { fprintf(stderr, "Cannot read %s\n", filename); res = -EIO; goto exit; } printf("Verify %d bytes at 0x%08x\n", file_size, offset); res = command_read_pages(ftdi, offset, flash_size, buffer2); draw_spinner(flash_size-res, flash_size); res = memcmp(buffer, buffer2, file_size); if (res != 0) { fprintf(stderr, "Verify Error!! "); goto exit; } printf("\n\rVerify Done.\n"); exit: free(buffer); free(buffer2); return res; } 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 ] [-p ] [-i <1|2>] " "[-s ] [-u] [-e] [-r ] [-w ]\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] : 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] : read the flash content and " "write it into \n"); fprintf(stderr, "--w[rite] : read 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); /* Call DBGR Rest to clear the EC lock status */ dbgr_reset(hnd); } 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; ret = verify_flash(hnd, output_filename, 0); if (ret) goto terminate; } /* Normal exit */ ret = 0; terminate: /* Exit DBGR mode */ exit_dbgr_mode(hnd); /* Close the FTDI USB handle */ ftdi_usb_close(hnd); ftdi_free(hnd); return ret; }