/* Copyright 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 #include #include #include #include #include #include #include "compile_time_macros.h" #include "usb_if.h" /* Default FTDI device : Servo v2. */ #define SERVO_USB_VID 0x18d1 #define SERVO_USB_PID 0x5002 #define SERVO_INTERFACE INTERFACE_B /* Default CCD device: Cr50. */ #define CR50_USB_VID 0x18d1 #define CR50_USB_PID 0x5014 /* Cr50 exposed properties of the USB I2C endpoint. */ #define CR50_I2C_SUBCLASS 82 #define CR50_I2C_PROTOCOL 1 #define CROS_CMD_ADDR 0x78 /* USB_I2C_CMD_ADDR 0xF0 >> 1 */ #define CROS_CMD_ITE_SYNC 0 /* DBGR I2C addresses */ #define I2C_CMD_ADDR 0x5A #define I2C_DATA_ADDR 0x35 #define I2C_BLOCK_ADDR 0x79 #define FTDI_I2C_FREQ 400000 /* I2C pins on the FTDI interface */ #define SCL_BIT BIT(0) #define SDA_BIT BIT(1) /* Chip ID register value */ #define CHIP_ID 0x8380 /* Embedded flash page size */ #define PAGE_SIZE (1<<8) /* Embedded flash block write size for different programming modes. */ #define FTDI_BLOCK_WRITE_SIZE (1<<16) /* 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 #define SPI_CMD_EWSR 0x50 /* Enable Write Status Register */ #define SPI_CMD_WRSR 0x01 /* Write Status Register */ /* Size for FTDI outgoing buffer */ #define FTDI_CMD_BUF_SIZE (1<<12) /* Reset Status */ #define RSTS_VCCDO_PW_ON 0x40 #define RSTS_VFSPIPG 0x20 #define RSTS_HGRST 0x08 #define RSTS_GRST 0x04 /* I2C MUX Configuration: TCA9543 or PCA9546 */ #define I2C_MUX_CMD_ADDR 0x70 #define I2C_MUX_CMD_NONE 0x00 #define I2C_MUX_CMD_INAS 0x01 #define I2C_MUX_CMD_EC 0x02 static volatile sig_atomic_t exit_requested; struct i2c_interface; /* Config mostly comes from the command line. Defaults are set in main(). */ struct iteflash_config { char *input_filename; char *output_filename; int send_waveform; /* boolean */ int erase; /* boolean */ int i2c_mux; /* boolean */ int debug; /* boolean */ int disable_watchdog; /* boolean */ int disable_protect_path; /* boolean */ int block_write_size; int usb_interface; int usb_vid; int usb_pid; char *usb_serial; char *i2c_dev_path; const struct i2c_interface *i2c_if; size_t range_base; size_t range_size; }; struct common_hnd { struct iteflash_config conf; int flash_size; int flash_cmd_v2; /* boolean */ int dbgr_addr_3bytes; /* boolean */ union { int i2c_dev_fd; struct usb_endpoint uep; struct ftdi_context *ftdi_hnd; }; }; /* For all callback return values, zero indicates success, non-zero failure. */ struct i2c_interface { /* Optional, may be NULL. */ int (*interface_init)(struct common_hnd *chnd); /* Always called if non-NULL, even if special waveform is skipped! */ /* Optional, may be NULL. */ int (*interface_post_waveform)(struct common_hnd *chnd); /* Called exactly once if and only if interface_init() succeeded. */ /* Optional, may be NULL. */ int (*interface_shutdown)(struct common_hnd *chnd); /* Optional, may be NULL (unsupported for this I2C interface type). */ int (*send_special_waveform)(struct common_hnd *chnd); /* Required, must not be NULL. */ int (*byte_transfer)(struct common_hnd *chnd, uint8_t addr, uint8_t *data, int write, int numbytes); /* Required, must be positive. */ int default_block_write_size; }; static void null_and_free(void **ptr) { void *holder; if (*ptr) { holder = *ptr; *ptr = NULL; free(holder); } } /* This releases any memory owned by *conf. This does NOT free conf itself! */ /* Not all pointers in conf necessarily point to memory owned by it. */ static void config_release(struct iteflash_config *conf) { null_and_free((void **)&conf->input_filename); null_and_free((void **)&conf->output_filename); null_and_free((void **)&conf->usb_serial); null_and_free((void **)&conf->i2c_dev_path); } /* number of bytes to send consecutively before checking for ACKs */ #define FTDI_TX_BUFFER_LIMIT 32 static inline int i2c_byte_transfer(struct common_hnd *chnd, uint8_t addr, uint8_t *data, int write, int numbytes) { return chnd->conf.i2c_if->byte_transfer(chnd, addr, data, write, numbytes); } static int linux_i2c_byte_transfer(struct common_hnd *chnd, uint8_t addr, uint8_t *data, int write, int numbytes) { static const int nmsgs = 1; int ret, extra_int; struct i2c_msg i2cmsg = {}; struct i2c_rdwr_ioctl_data msgset = {}; i2cmsg.addr = addr; if (!write) i2cmsg.flags |= I2C_M_RD; i2cmsg.buf = data; i2cmsg.len = numbytes; msgset.msgs = &i2cmsg; msgset.nmsgs = nmsgs; ret = ioctl(chnd->i2c_dev_fd, I2C_RDWR, &msgset); if (ret < 0) { extra_int = errno; fprintf(stderr, "%s: ioctl() failed with return value %d and " "errno %d\n", __func__, ret, extra_int); if (ret == -1 && extra_int) ret = -abs(extra_int); } else if (ret < nmsgs) { fprintf(stderr, "%s: failed to send %d of %d I2C messages\n", __func__, (nmsgs - ret), nmsgs); ret = -1; } else { ret = 0; } return ret; } static int i2c_add_send_byte(struct ftdi_context *ftdi, uint8_t *buf, uint8_t *ptr, uint8_t *tbuf, int tcnt, int debug) { int ret, i, j, remaining_data, ack_idx; int tx_buffered = 0; static uint8_t ack[FTDI_TX_BUFFER_LIMIT]; uint8_t *b = ptr; uint8_t failed_ack = 0; for (i = 0; i < tcnt; i++) { /* If we got a termination signal, stop sending data */ if (exit_requested) return -1; /* 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 FTDI_TX_BUFFER_LIMIT bytes, read * the ACK bits. */ if (i == tcnt-1 || (tx_buffered == FTDI_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 */ remaining_data = tx_buffered; ack_idx = 0; do { ret = ftdi_read_data(ftdi, &ack[ack_idx], remaining_data); if (ret < 0) { fprintf(stderr, "read ACK failed\n"); return ret; } remaining_data -= ret; ack_idx += ret; } while (remaining_data); 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, rbuf_idx; 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; } rbuf_idx = 0; do { /* If we got a termination signal, stop sending data */ if (exit_requested) return -1; ret = ftdi_read_data(ftdi, &rbuf[rbuf_idx], rcnt); if (ret < 0) { fprintf(stderr, "read byte failed\n"); break; } rcnt -= ret; rbuf_idx += ret; } while (rcnt); return ret; } #define USB_I2C_HEADER_SIZE 4 static int ccd_i2c_byte_transfer(struct common_hnd *chnd, uint8_t addr, uint8_t *data, int write, int numbytes) { uint8_t usb_buffer[USB_I2C_HEADER_SIZE + numbytes + (((!write * numbytes) > 0x7f) ? 2 : 0)]; size_t response_size; size_t extra = 0; /* Do nothing if user wants to quit. */ if (exit_requested) return -1; /* * Build a message following format described in ./include/usb_i2c.h. * * Hardcode port, the lowest 4 bits of the first byte, to 0; may need * to make this a command line option. */ usb_buffer[0] = 0; usb_buffer[1] = addr; if (write) { /* * Write count might spill over into the top 4 bits of the * first byte. We trust the caller not to pass numbytes * exceeding (2^12 - 1). */ if (numbytes > 255) usb_buffer[0] |= (numbytes >> 4) & 0xf0; usb_buffer[2] = numbytes & 0xff; usb_buffer[3] = 0; memcpy(usb_buffer + USB_I2C_HEADER_SIZE, data, numbytes); } else { usb_buffer[2] = 0; if (numbytes < 0x80) { usb_buffer[3] = numbytes; } else { usb_buffer[3] = (numbytes & 0x7f) | 0x80; usb_buffer[4] = numbytes >> 7; usb_buffer[5] = 0; extra = 2; } } response_size = 0; usb_trx(&chnd->uep, usb_buffer, write ? sizeof(usb_buffer) : USB_I2C_HEADER_SIZE + extra, usb_buffer, sizeof(usb_buffer), 1, &response_size); if (response_size < (USB_I2C_HEADER_SIZE + (write ? 0 : numbytes))) { fprintf(stderr, "%s: got too few bytes (%zd) in response\n", __func__, response_size); return -1; } if (usb_buffer[0]) { uint32_t rv; /* * Error is reported as a 16 bit value in little endian byte * order. */ rv = usb_buffer[1]; rv = (rv << 8) + usb_buffer[0]; fprintf(stderr, "%s: usb i2c error %d\n", __func__, (((uint16_t)usb_buffer[1]) << 8) + usb_buffer[0]); return -rv; } if (!write) memcpy(data, usb_buffer + USB_I2C_HEADER_SIZE, numbytes); return 0; } static int ftdi_i2c_byte_transfer(struct common_hnd *chnd, uint8_t addr, uint8_t *data, int write, int numbytes) { int ret, rets; static uint8_t buf[FTDI_CMD_BUF_SIZE]; uint8_t *b; uint8_t slave_addr; struct ftdi_context *ftdi; ret = 0; b = buf; ftdi = chnd->ftdi_hnd; /* 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, chnd->conf.debug); if (ret < 0) { if (chnd->conf.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, chnd->conf.debug); 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 common_hnd *chnd, uint8_t cmd, uint8_t data) { int ret; ret = i2c_byte_transfer(chnd, I2C_CMD_ADDR, &cmd, 1, 1); if (ret < 0) return -EIO; ret = i2c_byte_transfer(chnd, I2C_DATA_ADDR, &data, 1, 1); if (ret < 0) return -EIO; return 0; } static int i2c_read_byte(struct common_hnd *chnd, uint8_t cmd, uint8_t *data) { int ret; ret = i2c_byte_transfer(chnd, I2C_CMD_ADDR, &cmd, 1, 1); if (ret < 0) return -EIO; ret = i2c_byte_transfer(chnd, I2C_DATA_ADDR, data, 0, 1); if (ret < 0) return -EIO; return 0; } /* Configure I2C MUX to choose EC Prog channel */ static int config_i2c_mux(struct common_hnd *chnd, uint8_t cmd) { int ret; ret = i2c_byte_transfer(chnd, I2C_MUX_CMD_ADDR, &cmd, 1, 1); if (ret < 0) { fprintf(stderr, "Failed to configure I2C MUX."); return -EIO; } return 0; } /* Get 3rd Byte Chip ID */ static int get_3rd_chip_id_byte(struct common_hnd *chnd, uint8_t *chip_id) { int ret = 0; ret = i2c_write_byte(chnd, 0x80, 0xf0); ret |= i2c_write_byte(chnd, 0x2f, 0x20); ret |= i2c_write_byte(chnd, 0x2e, 0x85); ret |= i2c_read_byte(chnd, 0x30, chip_id); if (ret < 0) fprintf(stderr, "Failed to get id of 3rd byte."); return ret; } /* Fills in chnd->flash_size */ static int check_chipid(struct common_hnd *chnd) { int ret; uint8_t ver = 0xff; uint32_t id = 0xffff; uint16_t v2[5] = {128, 192, 256, 384, 512}; /* * Chip Version is mapping from bit 3-0 * Flash size is mapping from bit 7-4 * * Chip Version (bit 3-0) * 0: AX * 1: BX * 2: CX * 3: DX * * CX before flash size (bit 7-4) * 0:128KB * 4:192KB * 8:256KB * * DX flash size(bit 7-4) * 0:128KB * 2:192KB * 4:256KB * 6:384KB * 8:512KB * * flash size(bit 7-4) of it8xxx1 or it8xxx2 series * 0:128KB * 2:192KB * 4:256KB * 6:384KB * 8:512KB */ ret = i2c_read_byte(chnd, 0x00, (uint8_t *)&id + 1); if (ret < 0) return ret; ret = i2c_read_byte(chnd, 0x01, (uint8_t *)&id); if (ret < 0) return ret; ret = i2c_read_byte(chnd, 0x02, &ver); if (ret < 0) return ret; if ((id & 0xff00) != (CHIP_ID & 0xff00)) { id |= 0xff0000; ret = get_3rd_chip_id_byte(chnd, (uint8_t *)&id+2); if (ret < 0) return ret; if ((id & 0xf000f) == 0x80001 || (id & 0xf000f) == 0x80002) { chnd->flash_cmd_v2 = 1; chnd->dbgr_addr_3bytes = 1; } else { fprintf(stderr, "Invalid chip id: %05x\n", id); return -EINVAL; } } else { chnd->dbgr_addr_3bytes = 0; if ((ver & 0x0f) >= 0x03) chnd->flash_cmd_v2 = 1; else chnd->flash_cmd_v2 = 0; } /* compute embedded flash size from CHIPVER field */ if (chnd->flash_cmd_v2) chnd->flash_size = v2[(ver & 0xF0)>>5] * 1024; else chnd->flash_size = (128 + (ver & 0xF0)) * 1024; printf("CHIPID %05x, CHIPVER %02x, Flash size %d kB\n", id, ver, chnd->flash_size / 1024); return 0; } /* DBGR Reset */ static int dbgr_reset(struct common_hnd *chnd, unsigned char val) { int ret = 0; /* Reset CPU only, and we keep power state until flashing is done. */ if (chnd->dbgr_addr_3bytes) ret |= i2c_write_byte(chnd, 0x80, 0xf0); ret |= i2c_write_byte(chnd, 0x2f, 0x20); ret |= i2c_write_byte(chnd, 0x2e, 0x06); /* Enable the Reset Status by val */ ret |= i2c_write_byte(chnd, 0x30, val); ret |= i2c_write_byte(chnd, 0x27, 0x80); if (ret < 0) fprintf(stderr, "DBGR RESET FAILED\n"); return 0; } /* disable watchdog */ static int dbgr_disable_watchdog(struct common_hnd *chnd) { int ret = 0; printf("Disabling watchdog...\n"); if (chnd->dbgr_addr_3bytes) ret |= i2c_write_byte(chnd, 0x80, 0xf0); ret |= i2c_write_byte(chnd, 0x2f, 0x1f); ret |= i2c_write_byte(chnd, 0x2e, 0x05); ret |= i2c_write_byte(chnd, 0x30, 0x30); if (ret < 0) fprintf(stderr, "DBGR DISABLE WATCHDOG FAILED!\n"); return ret; } /* disable protect path from DBGR */ static int dbgr_disable_protect_path(struct common_hnd *chnd) { int ret = 0, i; printf("Disabling protect path...\n"); if (chnd->dbgr_addr_3bytes) ret |= i2c_write_byte(chnd, 0x80, 0xf0); ret |= i2c_write_byte(chnd, 0x2f, 0x20); for (i = 0; i < 32; i++) { ret |= i2c_write_byte(chnd, 0x2e, 0xa0+i); ret |= i2c_write_byte(chnd, 0x30, 0); } if (ret < 0) fprintf(stderr, "DISABLE PROTECT PATH FROM DBGR FAILED!\n"); return ret; } /* Enter follow mode and FSCE# high level */ static int spi_flash_follow_mode(struct common_hnd *chnd, char *desc) { int ret = 0; ret |= i2c_write_byte(chnd, 0x07, 0x7f); ret |= i2c_write_byte(chnd, 0x06, 0xff); ret |= i2c_write_byte(chnd, 0x05, 0xfe); ret |= i2c_write_byte(chnd, 0x04, 0x00); ret |= i2c_write_byte(chnd, 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 common_hnd *chnd, char *desc) { int ret = 0; ret |= i2c_write_byte(chnd, 0x07, 0x00); ret |= i2c_write_byte(chnd, 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 common_hnd *chnd, uint8_t cmd, char *desc) { int ret = 0; ret |= i2c_write_byte(chnd, 0x05, 0xfe); ret |= i2c_write_byte(chnd, 0x08, 0x00); ret |= i2c_write_byte(chnd, 0x05, 0xfd); ret |= i2c_write_byte(chnd, 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 common_hnd *chnd, int page, char *desc) { int ret = 0; ret |= i2c_write_byte(chnd, 0x08, page >> 8); ret |= i2c_write_byte(chnd, 0x08, page & 0xff); ret |= i2c_write_byte(chnd, 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 common_hnd *chnd, char *desc) { uint8_t reg = 0xff; int ret = -EIO; if (spi_flash_command_short(chnd, 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(chnd, 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 common_hnd *chnd, char *desc) { uint8_t reg = 0xff; int ret = -EIO; if (spi_flash_command_short(chnd, 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(chnd, 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 ftdi_config_i2c(struct ftdi_context *ftdi) { int ret; static const uint16_t divisor = 60000000 / (2 * FTDI_I2C_FREQ * 3 / 2 /* 3-phase CLK */) - 1; uint8_t clock_buf[] = { EN_3_PHASE, DIS_DIV_5, TCK_DIVISOR, divisor & 0xff, divisor >> 8}; 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 */ ret = ftdi_write_data(ftdi, clock_buf, sizeof(clock_buf)); if (ret < 0) return ret; return 0; } /* 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 connect_to_ccd_i2c_bridge(struct common_hnd *chnd) { int rv; rv = usb_findit(chnd->conf.usb_serial, chnd->conf.usb_vid, chnd->conf.usb_pid, CR50_I2C_SUBCLASS, CR50_I2C_PROTOCOL, &chnd->uep); if (rv) { fprintf(stderr, "%s: usb_findit returned error %d\n", __func__, rv); } return rv; } static int ccd_trigger_special_waveform(struct common_hnd *chnd) { uint8_t response[20]; size_t rsize; uint8_t req[] = { 0, /* Port 0. Might be necessary to modify. */ CROS_CMD_ADDR, /* Chrome OS dedicated address. */ 1, /* Will send a single byte command. */ 0, /* No need to read back anything. */ CROS_CMD_ITE_SYNC }; usb_trx(&chnd->uep, req, sizeof(req), response, sizeof(response), 1, &rsize); if (rsize < USB_I2C_HEADER_SIZE) return -1; if (response[0]) return -response[0]; /* * The target is about to get reset, let's shut down the USB * connection. */ usb_shut_down(&chnd->uep); sleep(3); return connect_to_ccd_i2c_bridge(chnd); } static int ftdi_send_special_waveform(struct common_hnd *chnd) { int ret; int i; uint64_t *wave; struct ftdi_context *ftdi = chnd->ftdi_hnd; uint8_t release_lines[] = {SET_BITS_LOW, 0, 0}; wave = malloc(SPECIAL_BUFFER_SIZE); if (!wave) { fprintf(stderr, "malloc(%zu) failed\n", (size_t)SPECIAL_BUFFER_SIZE); return -1; } /* Reset the FTDI into a known state */ ret = ftdi_set_bitmode(ftdi, 0xFF, BITMODE_RESET); if (ret) { fprintf(stderr, "failed to reset FTDI\n"); goto free_and_return; } /* * set the clock divider, so we output a new bitbang value every * 2.5us. */ ret = ftdi_set_baudrate(ftdi, 160000); if (ret) { fprintf(stderr, "failed to set bitbang clock\n"); goto free_and_return; } /* Enable asynchronous bit-bang mode */ ret = ftdi_set_bitmode(ftdi, 0xFF, BITMODE_BITBANG); if (ret) { fprintf(stderr, "failed to set bitbang mode\n"); goto free_and_return; } /* 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"); else /* no error */ ret = 0; /* clean everything to go back to regular I2C communication */ ftdi_usb_purge_buffers(ftdi); ftdi_set_bitmode(ftdi, 0xff, BITMODE_RESET); ftdi_config_i2c(ftdi); ftdi_write_data(ftdi, release_lines, sizeof(release_lines)); free_and_return: free(wave); return ret; } static int send_special_waveform(struct common_hnd *chnd) { const int max_iterations = 10; int ret; int iterations; if (!chnd->conf.i2c_if->send_special_waveform) { fprintf(stderr, "This binary does not support sending the ITE " "special waveform with the chosen I2C interface.\n"); return -1; } iterations = 0; do { ret = chnd->conf.i2c_if->send_special_waveform(chnd); if (ret) break; /* wait for PLL stable for 5ms (plus remaining USB transfers) */ usleep(10 * MSEC); if (spi_flash_follow_mode(chnd, "enter follow mode") >= 0) { spi_flash_follow_mode_exit(chnd, "exit follow mode"); /* * If we can talk to chip, then we can break the retry * loop. */ ret = check_chipid(chnd); } else { ret = -1; if (!(iterations % max_iterations)) fprintf(stderr, "!please reset EC if flashing" " sequence is not starting!\n"); } } while (ret && (iterations++ < max_iterations)); if (ret) fprintf(stderr, "Failed to send special waveform!\n"); else printf("Done with sending special waveform.\n"); 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; fprintf(stderr, "\r%c%3d%%", wheel[windex++], percent); windex %= sizeof(wheel); } static int command_read_pages(struct common_hnd *chnd, uint32_t address, uint32_t size, uint8_t *buffer) { int res = -EIO; uint32_t remaining = size; int cnt; uint8_t addr_H, addr_M; if (address & 0xFF) { fprintf(stderr, "page read requested at non-page boundary: " "0x%X\n", address); return -EINVAL; } if (spi_flash_follow_mode(chnd, "fast read") < 0) goto failed_read; while (remaining) { uint8_t cmd = 0x9; cnt = (remaining > PAGE_SIZE) ? PAGE_SIZE : remaining; addr_H = (address >> 16) & 0xFF; addr_M = (address >> 8) & 0xFF; draw_spinner(remaining, size); /* Fast Read command */ if (spi_flash_command_short(chnd, SPI_CMD_FAST_READ, "fast read") < 0) goto failed_read; res = i2c_write_byte(chnd, 0x08, addr_H); res += i2c_write_byte(chnd, 0x08, addr_M); res += i2c_write_byte(chnd, 0x08, 0x00); /* addr_L */ res += i2c_write_byte(chnd, 0x08, 0x00); if (res < 0) { fprintf(stderr, "page address set failed\n"); goto failed_read; } /* read page data */ res = i2c_byte_transfer(chnd, I2C_CMD_ADDR, &cmd, 1, 1); res = i2c_byte_transfer(chnd, 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(chnd, "fast read") < 0) res = -EIO; return res; } static int command_write_pages(struct common_hnd *chnd, uint32_t address, uint32_t size, uint8_t *buffer) { int res = -EIO; int block_write_size = chnd->conf.block_write_size; uint32_t remaining = size; int cnt; uint8_t addr_H, addr_M, addr_L; uint8_t data; if (spi_flash_follow_mode(chnd, "AAI write") < 0) goto failed_write; while (remaining) { cnt = (remaining > block_write_size) ? block_write_size : remaining; addr_H = (address >> 16) & 0xFF; addr_M = (address >> 8) & 0xFF; addr_L = address & 0xFF; draw_spinner(remaining, size); /* Write enable */ if (spi_flash_command_short(chnd, SPI_CMD_WRITE_ENABLE, "write enable for AAI write") < 0) goto failed_write; /* Check write enable bit */ if (spi_check_write_enable(chnd, "AAI write") < 0) goto failed_write; /* Setup write */ if (spi_flash_command_short(chnd, SPI_CMD_WORD_PROGRAM, "AAI write") < 0) goto failed_write; /* Set eflash page address */ res = i2c_byte_transfer(chnd, I2C_DATA_ADDR, &addr_H, 1, 1); res |= i2c_byte_transfer(chnd, I2C_DATA_ADDR, &addr_M, 1, 1); res |= i2c_byte_transfer(chnd, I2C_DATA_ADDR, &addr_L, 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(chnd, "AAI write") < 0) goto failed_write; /* Write up to block_write_size data */ res = i2c_write_byte(chnd, 0x10, 0x20); res = i2c_byte_transfer(chnd, I2C_BLOCK_ADDR, buffer, 1, cnt); buffer += cnt; if (res < 0) { fprintf(stderr, "Flash data write failed\n"); goto failed_write; } data = 0xFF; res = i2c_byte_transfer(chnd, I2C_DATA_ADDR, &data, 1, 1); res |= i2c_write_byte(chnd, 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(chnd, SPI_CMD_WRITE_DISABLE, "write disable for AAI write") < 0) goto failed_write; /* Wait until available */ if (spi_poll_busy(chnd, "write disable for AAI write") < 0) goto failed_write; address += cnt; remaining -= cnt; } draw_spinner(remaining, size); /* No error so far */ res = size; failed_write: if (spi_flash_command_short(chnd, SPI_CMD_WRITE_DISABLE, "write disable exit AAI write") < 0) res = -EIO; if (spi_flash_follow_mode_exit(chnd, "AAI write") < 0) res = -EIO; return res; } /* * Write another program flow to match the * original ITE 8903 Download board. */ static int command_write_pages2(struct common_hnd *chnd, uint32_t address, uint32_t size, uint8_t *buffer, int block_write_size) { int res = 0; uint8_t addr_H, addr_M, addr_L; uint8_t data; res |= i2c_write_byte(chnd, 0x07, 0x7f); res |= i2c_write_byte(chnd, 0x06, 0xff); res |= i2c_write_byte(chnd, 0x04, 0xFF); /* SMB_SPI_Flash_Enable_Write_Status */ if (spi_flash_command_short(chnd, SPI_CMD_EWSR, "Enable Write Status Register") < 0) { res = -EIO; goto failed_write; } /* SMB_SPI_Flash_Write_Status_Reg */ res |= i2c_write_byte(chnd, 0x05, 0xfe); res |= i2c_write_byte(chnd, 0x08, 0x00); res |= i2c_write_byte(chnd, 0x05, 0xfd); res |= i2c_write_byte(chnd, 0x08, 0x01); res |= i2c_write_byte(chnd, 0x08, 0x00); /* SMB_SPI_Flash_Write_Enable */ if (spi_flash_command_short(chnd, SPI_CMD_WRITE_ENABLE, "SPI Command Write Enable") < 0) { res = -EIO; goto failed_write; } /* SMB_SST_SPI_Flash_AAI2_Program */ if (spi_flash_command_short(chnd, SPI_CMD_WORD_PROGRAM, "SPI AAI2 Program") < 0) { res = -EIO; goto failed_write; } addr_H = (address >> 16) & 0xFF; addr_M = (address >> 8) & 0xFF; addr_L = address & 0xFF; res = i2c_byte_transfer(chnd, I2C_DATA_ADDR, &addr_H, 1, 1); res |= i2c_byte_transfer(chnd, I2C_DATA_ADDR, &addr_M, 1, 1); res |= i2c_byte_transfer(chnd, I2C_DATA_ADDR, &addr_L, 1, 1); res |= i2c_byte_transfer(chnd, I2C_DATA_ADDR, buffer++, 1, 1); res |= i2c_byte_transfer(chnd, I2C_DATA_ADDR, buffer++, 1, 1); /* Wait until not busy */ if (spi_poll_busy(chnd, "AAI write") < 0) goto failed_write; res = i2c_write_byte(chnd, 0x10, 0x20); res = i2c_byte_transfer(chnd, I2C_BLOCK_ADDR, buffer, 1, block_write_size-2); /* No error so far */ res = size; data = 0xFF; res = i2c_byte_transfer(chnd, I2C_DATA_ADDR, &data, 1, 1); res = i2c_write_byte(chnd, 0x10, 0x00); failed_write: if (spi_flash_command_short(chnd, SPI_CMD_WRITE_DISABLE, "write disable exit AAI write") < 0) res = -EIO; return res; } static int command_erase(struct common_hnd *chnd, uint32_t len, uint32_t off) { int res = -EIO; int page = 0; uint32_t remaining = len; printf("Erasing chip...\n"); if (off != 0 || len != chnd->flash_size) { fprintf(stderr, "Only full chip erase is supported\n"); return -EINVAL; } if (spi_flash_follow_mode(chnd, "erase") < 0) goto failed_erase; while (remaining) { draw_spinner(remaining, len); if (spi_flash_command_short(chnd, SPI_CMD_WRITE_ENABLE, "write enable for erase") < 0) goto failed_erase; if (spi_check_write_enable(chnd, "erase") < 0) goto failed_erase; /* do chip erase */ if (remaining == chnd->flash_size) { if (spi_flash_command_short(chnd, SPI_CMD_CHIP_ERASE, "chip erase") < 0) goto failed_erase; goto wait_busy_cleared; } /* do sector erase */ if (spi_flash_command_short(chnd, SPI_CMD_SECTOR_ERASE, "sector erase") < 0) goto failed_erase; if (spi_flash_set_erase_page(chnd, page, "sector erase") < 0) goto failed_erase; wait_busy_cleared: if (spi_poll_busy(chnd, "erase") < 0) goto failed_erase; if (spi_flash_command_short(chnd, SPI_CMD_WRITE_DISABLE, "write disable for erase") < 0) goto failed_erase; if (remaining == chnd->flash_size) { remaining = 0; draw_spinner(remaining, len); } else { page += SECTOR_ERASE_PAGES; remaining -= SECTOR_ERASE_PAGES * PAGE_SIZE; } } /* No error so far */ printf("\n\rErasing Done.\n"); res = 0; failed_erase: if (spi_flash_command_short(chnd, SPI_CMD_WRITE_DISABLE, "write disable exit erase") < 0) res = -EIO; if (spi_flash_follow_mode_exit(chnd, "erase") < 0) res = -EIO; return res; } /* * This function can Erase First Sector or Erase All Sector by reset value * Some F/W will produce the H/W watchdog reset and it will happen * reset issue while flash. * Add such function to prevent the reset issue. */ static int command_erase2(struct common_hnd *chnd, uint32_t len, uint32_t off, uint32_t reset) { int res = -EIO; int page = 0; uint32_t remaining = len; /* * TODOD(b/<>): * Using sector erase instead of chip erase * For some new chip , the chip erase may not work * well on the original flow */ printf("Erasing flash...erase size=%d\n", len); if (off != 0 || len != chnd->flash_size) { fprintf(stderr, "Only full chip erase is supported\n"); return -EINVAL; } if (spi_flash_follow_mode(chnd, "erase") < 0) goto failed_erase; while (remaining) { draw_spinner(remaining, len); if (spi_flash_command_short(chnd, SPI_CMD_WRITE_ENABLE, "write enable for erase") < 0) goto failed_erase; if (spi_check_write_enable(chnd, "erase") < 0) goto failed_erase; /* do sector erase */ if (spi_flash_command_short(chnd, SPI_CMD_SECTOR_ERASE, "sector erase") < 0) goto failed_erase; if (spi_flash_set_erase_page(chnd, page, "sector erase") < 0) goto failed_erase; if (spi_poll_busy(chnd, "erase") < 0) goto failed_erase; if (spi_flash_command_short(chnd, SPI_CMD_WRITE_DISABLE, "write disable for erase") < 0) goto failed_erase; if (reset) { printf("\n\rreset to prevent the watchdog reset...\n"); break; } page += SECTOR_ERASE_PAGES; remaining -= SECTOR_ERASE_PAGES * PAGE_SIZE; draw_spinner(remaining, len); } /* No error so far */ printf("\n\rErasing Done.\n"); res = 0; failed_erase: if (spi_flash_command_short(chnd, SPI_CMD_WRITE_DISABLE, "write disable exit erase") < 0) res = -EIO; if (spi_flash_follow_mode_exit(chnd, "erase") < 0) res = -EIO; return res; } /* Return zero on success, a negative error value on failures. */ static int read_flash(struct common_hnd *chnd) { int res; FILE *hnd; uint8_t *buffer; const char *filename = chnd->conf.input_filename; size_t offset = chnd->conf.range_base; size_t size; if (!offset && !chnd->conf.range_size) { size = chnd->flash_size; } else { /* * Zero conf.range_size means the user did not enter range * size in the command line. */ if (chnd->conf.range_size) size = chnd->conf.range_size; else size = chnd->flash_size - offset; if (!size) { fprintf(stderr, "Error: not reading a zero sized range!\n"); return -EINVAL; } if ((size + offset) > chnd->flash_size) { fprintf(stderr, "Error: Read range exceeds flash size!\n"); return -EINVAL; } } buffer = malloc(size); if (!buffer) { fprintf(stderr, "Cannot allocate %zd 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; } printf("Reading %zd bytes at %#08zx\n", size, offset); res = command_read_pages(chnd, 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. */ static int write_flash(struct common_hnd *chnd, const char *filename, uint32_t offset) { int res, written; FILE *hnd; int size = chnd->flash_size; uint8_t *buffer = malloc(size); if (!buffer) { fprintf(stderr, "%s: Cannot allocate %d bytes\n", __func__, size); return -ENOMEM; } hnd = fopen(filename, "r"); if (!hnd) { fprintf(stderr, "%s: Cannot open file %s for reading\n", __func__, filename); free(buffer); return -EIO; } res = fread(buffer, 1, size, hnd); if (res <= 0) { fprintf(stderr, "%s: Failed to read %d bytes from %s with " "ferror() %d\n", __func__, size, filename, ferror(hnd)); free(buffer); fclose(hnd); return -EIO; } fclose(hnd); printf("Writing %d bytes at 0x%08x\n", res, offset); written = command_write_pages(chnd, offset, res, buffer); if (written != res) { fprintf(stderr, "%s: Error writing to flash\n", __func__); free(buffer); return -EIO; } printf("\n\rWriting Done.\n"); free(buffer); return 0; } /* * Return zero on success, a negative error value on failures. * * Change the program command to match the ITE Download * The original flow may not work on the DX chip. * */ static int write_flash2(struct common_hnd *chnd, const char *filename, uint32_t offset) { int res, written; int block_write_size = chnd->conf.block_write_size; FILE *hnd; int size = chnd->flash_size; int cnt; uint8_t *buffer = malloc(size); if (!buffer) { fprintf(stderr, "%s: Cannot allocate %d bytes\n", __func__, size); return -ENOMEM; } hnd = fopen(filename, "r"); if (!hnd) { fprintf(stderr, "%s: Cannot open file %s for reading\n", __func__, filename); free(buffer); return -EIO; } res = fread(buffer, 1, size, hnd); if (res <= 0) { fprintf(stderr, "%s: Failed to read %d bytes from %s with " "ferror() %d\n", __func__, size, filename, ferror(hnd)); fclose(hnd); free(buffer); return -EIO; } fclose(hnd); offset = 0; printf("Writing %d bytes at 0x%08x.......\n", res, offset); while (res) { cnt = (res > block_write_size) ? block_write_size : res; written = command_write_pages2(chnd, offset, cnt, &buffer[offset], block_write_size); if (written == -EIO) goto failed_write; res -= cnt; offset += cnt; draw_spinner(res, res + offset); } if (written != res) { failed_write: fprintf(stderr, "%s: Error writing to flash\n", __func__); free(buffer); return -EIO; } printf("\n\rWriting Done.\n"); free(buffer); return 0; } /* Return zero on success, a non-zero value on failures. */ static int verify_flash(struct common_hnd *chnd, const char *filename, uint32_t offset) { int res; int file_size; FILE *hnd; uint8_t *buffer = malloc(chnd->flash_size); uint8_t *buffer2 = malloc(chnd->flash_size); if (!buffer || !buffer2) { fprintf(stderr, "%s: Cannot allocate %d bytes\n", __func__, chnd->flash_size); free(buffer); free(buffer2); return -ENOMEM; } hnd = fopen(filename, "r"); if (!hnd) { fprintf(stderr, "%s: Cannot open file %s for reading\n", __func__, filename); res = -EIO; goto exit; } file_size = fread(buffer, 1, chnd->flash_size, hnd); if (file_size <= 0) { fprintf(stderr, "%s: Failed to read %d bytes from %s with " "ferror() %d\n", __func__, chnd->flash_size, filename, ferror(hnd)); fclose(hnd); res = -EIO; goto exit; } fclose(hnd); printf("Verify %d bytes at 0x%08x\n", file_size, offset); res = command_read_pages(chnd, offset, chnd->flash_size, buffer2); if (res > 0) res = memcmp(buffer, buffer2, file_size); printf("\n\rVerify %s\n", res ? "Failed!" : "Done."); exit: free(buffer); free(buffer2); return res; } static struct ftdi_context *open_ftdi_device(int vid, int pid, int interface, const 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 int linux_i2c_interface_init(struct common_hnd *chnd) { int err; if (!chnd->conf.i2c_dev_path) { fprintf(stderr, "Must set --i2c_dev_path when using " "Linux i2c-dev interface.\n"); return -1; } printf("Attempting to open Linux i2c-dev path %s\n", chnd->conf.i2c_dev_path); chnd->i2c_dev_fd = open(chnd->conf.i2c_dev_path, O_RDWR); if (chnd->i2c_dev_fd < 0) { err = errno; perror("Failed to open Linux i2c-dev file path with error"); fprintf(stderr, "Linux i2c-dev file path from --i2c_dev_path " "is: %s\n", chnd->conf.i2c_dev_path); return err ? err : -1; } printf("Successfully opened Linux i2c-dev path %s\n", chnd->conf.i2c_dev_path); return 0; } static int linux_i2c_interface_shutdown(struct common_hnd *chnd) { int err; printf("Attempting to close Linux i2c-dev file descriptor %d\n", chnd->i2c_dev_fd); if (close(chnd->i2c_dev_fd)) { err = errno; perror("Failed to close Linux i2c-dev file descriptor with " "error"); return err ? err : -1; } printf("Successfully closed Linux i2c-dev file descriptor %d\n", chnd->i2c_dev_fd); return 0; } static int ccd_i2c_interface_init(struct common_hnd *chnd) { chnd->conf.usb_vid = CR50_USB_VID; chnd->conf.usb_pid = CR50_USB_PID; return connect_to_ccd_i2c_bridge(chnd); } static int ccd_i2c_interface_shutdown(struct common_hnd *chnd) { usb_shut_down(&chnd->uep); return 0; } static int ftdi_i2c_interface_init(struct common_hnd *chnd) { chnd->ftdi_hnd = open_ftdi_device(chnd->conf.usb_vid, chnd->conf.usb_pid, chnd->conf.usb_interface, chnd->conf.usb_serial); if (chnd->ftdi_hnd == NULL) return -1; return 0; } static int ftdi_i2c_interface_post_waveform(struct common_hnd *chnd) { return chnd->conf.send_waveform ? 0 : ftdi_config_i2c(chnd->ftdi_hnd); } /* Close the FTDI USB handle */ static int ftdi_i2c_interface_shutdown(struct common_hnd *chnd) { ftdi_usb_close(chnd->ftdi_hnd); ftdi_free(chnd->ftdi_hnd); return 0; } static const struct i2c_interface linux_i2c_interface = { .interface_init = linux_i2c_interface_init, .interface_shutdown = linux_i2c_interface_shutdown, .byte_transfer = linux_i2c_byte_transfer, /* * 254 bytes is the largest size that works with Servo Micro as of * 2018-11-30. Odd numbers up to 255 result in corruption, and 256 or * greater fails with a timeout from the I2C bus. Fixing that so this * can be increased to match FTDI_BLOCK_WRITE_SIZE would be a useful * speedup. * * 254 byte block sizes cause corruption with Ampton (using any kind of * servo). 128 bytes is the largest block_write_size compatible with * both Ampton and Servo Micro. * * See https://issuetracker.google.com/79684405 for background. */ .default_block_write_size = 128, }; static const struct i2c_interface ccd_i2c_interface = { .interface_init = ccd_i2c_interface_init, .interface_shutdown = ccd_i2c_interface_shutdown, .send_special_waveform = ccd_trigger_special_waveform, .byte_transfer = ccd_i2c_byte_transfer, .default_block_write_size = PAGE_SIZE, }; static const struct i2c_interface ftdi_i2c_interface = { .interface_init = ftdi_i2c_interface_init, .interface_post_waveform = ftdi_i2c_interface_post_waveform, .interface_shutdown = ftdi_i2c_interface_shutdown, .send_special_waveform = ftdi_send_special_waveform, .byte_transfer = ftdi_i2c_byte_transfer, .default_block_write_size = FTDI_BLOCK_WRITE_SIZE, }; static int post_waveform_work(struct common_hnd *chnd) { int ret; if (chnd->conf.i2c_if->interface_post_waveform) { ret = chnd->conf.i2c_if->interface_post_waveform(chnd); if (ret) return ret; } if (chnd->conf.disable_watchdog) { ret = dbgr_disable_watchdog(chnd); if (ret) return ret; } if (chnd->conf.disable_protect_path) { ret = dbgr_disable_protect_path(chnd); if (ret) return ret; } return 0; } static int strdup_with_errmsg(const char *source, char **dest, const char *name) { int ret = 0; *dest = strdup(source); if (!(*dest)) { ret = errno ? errno : -1; fprintf(stderr, "strdup() of %zu size string from %s failed.\n", strlen(source), name); } return ret; } static const struct option longopts[] = { {"block-write-size", 1, 0, 'b'}, {"debug", 0, 0, 'd'}, {"erase", 0, 0, 'e'}, {"help", 0, 0, 'h'}, {"i2c-dev-path", 1, 0, 'D'}, {"i2c-interface", 1, 0, 'c'}, {"i2c-mux", 0, 0, 'm'}, {"interface", 1, 0, 'i'}, {"nodisable-protect-path", 0, 0, 'Z'}, {"nodisable-watchdog", 0, 0, 'z'}, {"product", 1, 0, 'p'}, {"range", 1, 0, 'R'}, {"read", 1, 0, 'r'}, {"send-waveform", 1, 0, 'W'}, {"serial", 1, 0, 's'}, {"vendor", 1, 0, 'v'}, {"write", 1, 0, 'w'}, {NULL, 0, 0, 0} }; static void display_usage(const char *program) { fprintf(stderr, "Usage: %s [-d] [-v ] [-p ] \\\n" "\t[-c ] [-D /dev/i2c-] [-i <1|2>] [-S] \\\n" "\t[-s ] [-e] [-r ] [-W <0|1|false|true>] \\\n" "\t[-w ] [-R base[:size]] [-m] [-b ]\n", program); fprintf(stderr, "-d, --debug : Output debug traces.\n"); fprintf(stderr, "-e, --erase : Erase all the flash content.\n"); fprintf(stderr, "-c, --i2c-interface : I2C interface " "to use\n"); fprintf(stderr, "-D, --i2c-dev-path /dev/i2c- : Path to " "Linux i2c-dev file e.g. /dev/i2c-5;\n" "\tonly applicable with --i2c-interface=linux\n"); fprintf(stderr, "-i, --interface <1> : FTDI interface: A=1, B=2," " ...\n"); fprintf(stderr, "-m, --i2c-mux : Enable i2c-mux (to EC).\n" "\tSpecify this flag only if the board has an I2C MUX and\n" "\tyou are not using servod.\n"); fprintf(stderr, "-b, --block-write-size : Perform writes in\n" "\tblocks of this many bytes.\n"); fprintf(stderr, "-p, --product <0x1234> : USB product ID\n"); fprintf(stderr, "-R, --range base[:size] : Allow to read or write" " just a slice\n" "\tof the file, starting at : bytes, or til\n" "\tthe end of the file if is not specified, expressed\n" "\tin hex.\n"); fprintf(stderr, "-r, --read : Read the flash content and" " write it into .\n"); fprintf(stderr, "-s, --serial : USB serial string\n"); fprintf(stderr, "-v, --vendor <0x1234> : USB vendor ID\n"); fprintf(stderr, "-W, --send-waveform <0|1|false|true> : Send the" " special waveform.\n" "\tDefault is true. Set to false if ITE direct firmware\n" "\tupdate mode has already been enabled.\n"); fprintf(stderr, "-w, --write : Write to flash.\n"); fprintf(stderr, "-z, --nodisable-watchdog : Do *not* disable EC " "watchdog.\n"); fprintf(stderr, "-Z, --nodisable-protect-path : Do *not* disable EC " "protect path.\n"); } /* * Parses -R command line option parameter, returns zero on success and * -1 on errors (non hex values, missing values, etc.). */ static int parse_range_options(char *str, struct iteflash_config *conf) { char *size; if (!str) { fprintf(stderr, "missing range base address specification\n"); return -1; } conf->range_base = strtoul(str, &size, 16); if (!size || !*size) return 0; if (*size++ != ':') { fprintf(stderr, "wrong range base address specification\n"); return -1; } if (!*size) { fprintf(stderr, "missing range size specification\n"); return -1; } conf->range_size = strtoul(size, &size, 16); if ((size && *size) || !conf->range_size) { fprintf(stderr, "wrong range size specification\n"); return -1; } return 0; } static int parse_parameters(int argc, char **argv, struct iteflash_config *conf) { int opt, idx, ret = 0; while (!ret && (opt = getopt_long(argc, argv, "?b:c:D:dehi:mp:R:r:s:uv:W:w:Zz", longopts, &idx)) != -1) { switch (opt) { case 'b': conf->block_write_size = strtol(optarg, NULL, 10); break; case 'c': if (!strcasecmp(optarg, "linux")) { conf->i2c_if = &linux_i2c_interface; } else if (!strcasecmp(optarg, "ccd")) { conf->i2c_if = &ccd_i2c_interface; } else if (!strcasecmp(optarg, "ftdi")) { conf->i2c_if = &ftdi_i2c_interface; } else { fprintf(stderr, "Unexpected -c / " "--i2c-interface value: %s\n", optarg); ret = -1; } break; case 'D': ret = strdup_with_errmsg(optarg, &conf->i2c_dev_path, "-D / --i2c-dev-path"); break; case 'd': conf->debug = 1; break; case 'e': conf->erase = 1; break; case 'h': case '?': display_usage(argv[0]); ret = 2; break; case 'i': conf->usb_interface = strtol(optarg, NULL, 10); break; case 'm': conf->i2c_mux = 1; break; case 'p': conf->usb_pid = strtol(optarg, NULL, 16); break; case 'R': ret = parse_range_options(optarg, conf); break; case 'r': ret = strdup_with_errmsg(optarg, &conf->input_filename, "-r / --read"); break; case 's': ret = strdup_with_errmsg(optarg, &conf->usb_serial, "-s / --serial"); break; case 'v': conf->usb_vid = strtol(optarg, NULL, 16); break; case 'W': if (!strcmp(optarg, "0") || !strcasecmp(optarg, "false")) { conf->send_waveform = 0; break; } if (!strcmp(optarg, "1") || !strcasecmp(optarg, "true")) { conf->send_waveform = 1; break; } fprintf(stderr, "Unexpected -W / --special-waveform " "value: %s\n", optarg); ret = -1; break; case 'w': ret = strdup_with_errmsg(optarg, &conf->output_filename, "-w / --write"); break; case 'z': conf->disable_watchdog = 0; break; case 'Z': conf->disable_protect_path = 0; break; } } if (ret) config_release(conf); return ret; } static void sighandler(int signum) { printf("\nCaught signal %d: %s\nExiting...\n", signum, sys_siglist[signum]); exit_requested = 1; } static void register_sigaction(void) { struct sigaction sigact; sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); } int main(int argc, char **argv) { int ret = 1, other_ret; struct common_hnd chnd = { /* Default flag settings. */ .conf = { .send_waveform = 1, .disable_watchdog = 1, .disable_protect_path = 1, .usb_interface = SERVO_INTERFACE, .usb_vid = SERVO_USB_VID, .usb_pid = SERVO_USB_PID, .i2c_if = &ftdi_i2c_interface, }, }; /* Parse command line options */ other_ret = parse_parameters(argc, argv, &chnd.conf); if (other_ret) return other_ret; /* Fill in block_write_size if not set from command line. */ if (!chnd.conf.block_write_size) chnd.conf.block_write_size = chnd.conf.i2c_if->default_block_write_size; /* Open the communications channel. */ if (chnd.conf.i2c_if->interface_init && chnd.conf.i2c_if->interface_init(&chnd)) goto return_after_parse; /* Register signal handler after opening the communications channel. */ register_sigaction(); if (chnd.conf.i2c_mux) { printf("configuring I2C MUX to EC.\n"); if (config_i2c_mux(&chnd, I2C_MUX_CMD_EC)) goto return_after_init; } /* Trigger embedded monitor detection */ if (chnd.conf.send_waveform) { if (send_special_waveform(&chnd)) goto return_after_init; } else { ret = check_chipid(&chnd); if (ret) { fprintf(stderr, "Failed to get ITE chip ID. This " "could be because the ITE direct firmware " "update (DFU) mode is not enabled.\n"); goto return_after_init; } } ret = post_waveform_work(&chnd); if (ret) goto return_after_init; if (chnd.conf.input_filename) { ret = read_flash(&chnd); if (ret) goto return_after_init; } if (chnd.conf.erase) { if (chnd.flash_cmd_v2) /* Do Normal Erase Function */ command_erase2(&chnd, chnd.flash_size, 0, 0); else command_erase(&chnd, chnd.flash_size, 0); /* Call DBGR Rest to clear the EC lock status after erasing */ dbgr_reset(&chnd, RSTS_VCCDO_PW_ON|RSTS_HGRST|RSTS_GRST); } if (chnd.conf.output_filename) { if (chnd.flash_cmd_v2) ret = write_flash2(&chnd, chnd.conf.output_filename, 0); else ret = write_flash(&chnd, chnd.conf.output_filename, 0); if (ret) goto return_after_init; ret = verify_flash(&chnd, chnd.conf.output_filename, 0); if (ret) goto return_after_init; } /* Normal exit */ ret = 0; return_after_init: /* Enable EC Host Global Reset to reset EC resource and EC domain. */ dbgr_reset(&chnd, RSTS_VCCDO_PW_ON|RSTS_HGRST|RSTS_GRST); if (chnd.conf.i2c_mux) { printf("configuring I2C MUX to none.\n"); config_i2c_mux(&chnd, I2C_MUX_CMD_NONE); } if (chnd.conf.i2c_if->interface_shutdown) { other_ret = chnd.conf.i2c_if->interface_shutdown(&chnd); if (!ret && other_ret) ret = other_ret; } return_after_parse: config_release(&chnd.conf); return ret; }