diff options
author | Wei-Han Chen <stimim@google.com> | 2018-03-12 20:25:04 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-03-27 08:06:03 -0700 |
commit | 9ea406a2f863264a028fb68f37022d403ee8b354 (patch) | |
tree | 64ef046dec876881d29c01e93a2f99e55fbb49d8 /driver | |
parent | ce4392e123613a7c692930e961a2e2c7bde73074 (diff) | |
download | chrome-ec-9ea406a2f863264a028fb68f37022d403ee8b354.tar.gz |
driver/touchpad_st.c: implement touchpad fw update
BRANCH=none
BUG=b:70482333
TEST=make BOARD=whiskers
TEST=sudo ./extra/usb_updater/usb_updater2 -d 18d1:5030 -p <file>
Signed-off-by: Wei-Han Chen <stimim@chromium.org>
Change-Id: I6e3e73a01571ae4cf31891edca588c44e5f41194
Reviewed-on: https://chromium-review.googlesource.com/958896
Commit-Ready: Wei-Han Chen <stimim@chromium.org>
Tested-by: Wei-Han Chen <stimim@chromium.org>
Reviewed-by: Nicolas Boichat <drinkcat@chromium.org>
Diffstat (limited to 'driver')
-rw-r--r-- | driver/touchpad_st.c | 233 |
1 files changed, 232 insertions, 1 deletions
diff --git a/driver/touchpad_st.c b/driver/touchpad_st.c index 0806f93a6a..340923e587 100644 --- a/driver/touchpad_st.c +++ b/driver/touchpad_st.c @@ -36,6 +36,7 @@ static struct st_tp_system_info_t system_info; static int st_tp_read_all_events(void); static int st_tp_send_ack(void); static int st_tp_start_scan(void); +static int st_tp_stop_scan(void); static int st_tp_read_host_buffer_header(void); /* @@ -282,6 +283,17 @@ static int st_tp_read_host_data_memory(uint16_t addr, void *rx_buf, int len) { return spi_transaction(SPI, tx_buf, sizeof(tx_buf), rx_buf, len); } +static int st_tp_stop_scan(void) +{ + int new_state = 0; + int mask = SYSTEM_STATE_ACTIVE_MODE; + int ret; + + ret = st_tp_update_system_state(new_state, mask); + st_tp_enable_interrupt(0); + return ret; +} + static int st_tp_load_host_data(uint8_t mem_id) { uint8_t tx_buf[] = { @@ -388,6 +400,7 @@ static void st_tp_init(void) st_tp_start_scan(); } +DECLARE_DEFERRED(st_tp_init); #ifdef CONFIG_USB_UPDATE int touchpad_get_info(struct touchpad_info *tp) @@ -415,6 +428,190 @@ int touchpad_get_info(struct touchpad_info *tp) } /* + * Helper functions for firmware update + * + * There is no documentation about ST_TP_CMD_WRITE_HW_REG (0xFA). + * All implementations below are based on sample code from ST. + */ +static int write_hwreg_cmd32(uint32_t address, uint32_t data) +{ + uint8_t tx_buf[] = { + ST_TP_CMD_WRITE_HW_REG, + (address >> 24) & 0xFF, + (address >> 16) & 0xFF, + (address >> 8) & 0xFF, + (address >> 0) & 0xFF, + (data >> 24) & 0xFF, + (data >> 16) & 0xFF, + (data >> 8) & 0xFF, + (data >> 0) & 0xFF, + }; + + return spi_transaction(SPI, tx_buf, sizeof(tx_buf), NULL, 0); +} + +static int write_hwreg_cmd8(uint32_t address, uint8_t data) +{ + uint8_t tx_buf[] = { + ST_TP_CMD_WRITE_HW_REG, + (address >> 24) & 0xFF, + (address >> 16) & 0xFF, + (address >> 8) & 0xFF, + (address >> 0) & 0xFF, + data, + }; + + return spi_transaction(SPI, tx_buf, sizeof(tx_buf), NULL, 0); +} + +static int wait_for_flash_ready(uint8_t type) +{ + uint8_t tx_buf[] = { + ST_TP_CMD_READ_HW_REG, + 0x20, 0x00, 0x00, type, + }; + int ret = EC_SUCCESS, retry = 200; + + while (retry--) { + ret = spi_transaction(SPI, tx_buf, sizeof(tx_buf), + (uint8_t *)&rx_buf, 2); + if (ret == EC_SUCCESS && !(rx_buf.bytes[0] & 0x80)) + break; + usleep(50 * MSEC); + } + return retry >= 0 ? ret : EC_ERROR_TIMEOUT; +} + +static int erase_flash(void) +{ + int ret; + + /* Erase everything, except CX */ + ret = write_hwreg_cmd32(0x20000128, 0xFFFFFF83); + if (ret) + return ret; + ret = write_hwreg_cmd8(0x2000006B, 0x00); + if (ret) + return ret; + ret = write_hwreg_cmd8(0x2000006A, 0xA0); + if (ret) + return ret; + return wait_for_flash_ready(0x6A); +} + +static int st_tp_prepare_for_update(void) +{ + /* hold m3 */ + write_hwreg_cmd8(0x20000024, 0x01); + /* unlock flash */ + write_hwreg_cmd8(0x20000025, 0x20); + /* unlock flash erase */ + write_hwreg_cmd8(0x200000DE, 0x03); + erase_flash(); + + return EC_SUCCESS; +} + +static int st_tp_start_flash_dma(void) +{ + int ret; + + ret = write_hwreg_cmd8(0x20000071, 0xC0); + if (ret) + return ret; + ret = wait_for_flash_ready(0x71); + return ret; +} + +static int st_tp_write_one_chunk(const uint8_t *head, + uint32_t addr, uint32_t chunk_size) +{ + uint8_t tx_buf[ST_TP_DMA_CHUNK_SIZE + 5]; + uint32_t index = 0; + int ret; + + index = 0; + + tx_buf[index++] = ST_TP_CMD_WRITE_HW_REG; + tx_buf[index++] = (addr >> 24) & 0xFF; + tx_buf[index++] = (addr >> 16) & 0xFF; + tx_buf[index++] = (addr >> 8) & 0xFF; + tx_buf[index++] = (addr >> 0) & 0xFF; + memcpy(tx_buf + index, head, chunk_size); + ret = spi_transaction(SPI, tx_buf, chunk_size + 5, NULL, 0); + + return ret; +} + +/* + * @param offset: offset in memory to copy the data (in bytes). + * @param size: length of data (in bytes). + * @param data: pointer to data bytes. + */ +static int st_tp_write_flash(int offset, int size, const uint8_t *data) +{ + uint8_t tx_buf[12] = {0}; + const uint8_t *head = data, *tail = data + size; + uint32_t addr, index, chunk_size; + uint32_t flash_buffer_size; + int ret; + + offset >>= 2; /* offset should be count in words */ + /* + * To write to flash, the data has to be separated into several chunks. + * Each chunk will be no more than `ST_TP_DMA_CHUNK_SIZE` bytes. + * The chunks will first be saved into a buffer, the buffer can only + * holds `ST_TP_FLASH_BUFFER_SIZE` bytes. We have to flush the buffer + * when the capacity is reached. + */ + while (head < tail) { + addr = 0x00100000; + flash_buffer_size = 0; + while (flash_buffer_size < ST_TP_FLASH_BUFFER_SIZE) { + chunk_size = MIN(ST_TP_DMA_CHUNK_SIZE, tail - head); + ret = st_tp_write_one_chunk(head, addr, chunk_size); + if (ret) + return ret; + + flash_buffer_size += chunk_size; + addr += chunk_size; + head += chunk_size; + + if (head >= tail) + break; + } + + /* configuring the DMA */ + flash_buffer_size = flash_buffer_size / 4 - 1; + index = 0; + + tx_buf[index++] = ST_TP_CMD_WRITE_HW_REG; + tx_buf[index++] = 0x20; + tx_buf[index++] = 0x00; + tx_buf[index++] = 0x00; + tx_buf[index++] = 0x72; /* flash DMA config */ + tx_buf[index++] = 0x00; + tx_buf[index++] = 0x00; + + tx_buf[index++] = offset & 0xFF; + tx_buf[index++] = (offset >> 8) & 0xFF; + tx_buf[index++] = flash_buffer_size & 0xFF; + tx_buf[index++] = (flash_buffer_size >> 8) & 0xFF; + tx_buf[index++] = 0x00; + + ret = spi_transaction(SPI, tx_buf, index, NULL, 0); + if (ret) + return ret; + ret = st_tp_start_flash_dma(); + if (ret) + return ret; + + offset += ST_TP_FLASH_BUFFER_SIZE / 4; + } + return EC_SUCCESS; +} + +/* * @param offset: should be address between 0 to 1M, aligned with * ST_TP_DMA_CHUNK_SIZE. * @param size: length of `data` array. @@ -422,9 +619,43 @@ int touchpad_get_info(struct touchpad_info *tp) */ int touchpad_update_write(int offset, int size, const uint8_t *data) { + int ret; + uint8_t tx_buf[] = { ST_TP_CMD_WRITE_SYSTEM_COMMAND, 0x00, 0x03 }; + CPRINTS("%s %08x %d", __func__, offset, size); + if (offset == 0) { + /* stop scanning, interrupt, etc... */ + st_tp_stop_scan(); - return EC_RES_INVALID_COMMAND; + ret = st_tp_prepare_for_update(); + if (ret) + return ret; + } + + if (offset % ST_TP_DMA_CHUNK_SIZE) + return EC_ERROR_INVAL; + + if (offset >= ST_TP_FLASH_OFFSET_CX && + offset < ST_TP_FLASH_OFFSET_CONFIG) + /* don't update CX section */ + return EC_SUCCESS; + + ret = st_tp_write_flash(offset, size, data); + if (ret) + return ret; + + if (offset + size == CONFIG_TOUCHPAD_VIRTUAL_SIZE) { + CPRINTS("%s: End update, wait for reset.", __func__); + + board_touchpad_reset(); + + /* Full panel initialization */ + spi_transaction(SPI, tx_buf, sizeof(tx_buf), NULL, 0); + + hook_call_deferred(&st_tp_init_data, 10 * MSEC); + } + + return EC_SUCCESS; } int touchpad_debug(const uint8_t *param, unsigned int param_size, |