From 215ffecfcade20abc326410868504184c66cd82b Mon Sep 17 00:00:00 2001 From: Ting Shen Date: Fri, 13 Nov 2020 14:47:22 +0800 Subject: touchpad_elan: add support for CoachZ touchpad Port probe / update logic from linux driver code (drivers/input/mouse/elan_i2c_i2c.c), and support different page size. BUG=b:169651794 TEST=flash fw successfully on Zed. BRANCH=none Signed-off-by: Ting Shen Change-Id: I2aa7316219d235e28e446de13b0d24fe3c6dac15 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2537415 Reviewed-by: Nicolas Boichat Commit-Queue: Ting Shen Tested-by: Ting Shen --- board/hammer/variants.h | 2 +- builtin/endian.h | 4 ++ driver/touchpad_elan.c | 147 +++++++++++++++++++++++++++++++++++++----------- 3 files changed, 118 insertions(+), 35 deletions(-) diff --git a/board/hammer/variants.h b/board/hammer/variants.h index 794abf543a..0b568aa1b3 100644 --- a/board/hammer/variants.h +++ b/board/hammer/variants.h @@ -91,7 +91,7 @@ #define CONFIG_USB_HID_TOUCHPAD_LOGICAL_MAX_PRESSURE 511 #define CONFIG_USB_HID_TOUCHPAD_PHYSICAL_MAX_X 1018 /* tenth of mm */ #define CONFIG_USB_HID_TOUCHPAD_PHYSICAL_MAX_Y 566 /* tenth of mm */ -#define CONFIG_TOUCHPAD_VIRTUAL_SIZE (48*1024) +#define CONFIG_TOUCHPAD_VIRTUAL_SIZE (64*1024) #else #error "No touchpad information for board." #endif diff --git a/builtin/endian.h b/builtin/endian.h index 8a50cb4815..65c064bb78 100644 --- a/builtin/endian.h +++ b/builtin/endian.h @@ -36,6 +36,10 @@ static inline uint64_t be64toh(uint64_t in) #define htobe32 be32toh #define htobe64 be64toh +#define htole16(x) (uint16_t)(x) +#define htole32(x) (uint32_t)(x) +#define htole64(x) (uint64_t)(x) + #endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ */ #ifdef __cplusplus diff --git a/driver/touchpad_elan.c b/driver/touchpad_elan.c index 8838130c60..0882f03874 100644 --- a/driver/touchpad_elan.c +++ b/driver/touchpad_elan.c @@ -3,6 +3,7 @@ * found in the LICENSE file. */ +#include "byteorder.h" #include "common.h" #include "console.h" #include "gpio.h" @@ -46,8 +47,10 @@ #define ETP_I2C_MAX_X_AXIS_CMD 0x0106 #define ETP_I2C_MAX_Y_AXIS_CMD 0x0107 #define ETP_I2C_RESOLUTION_CMD 0x0108 +#define ETP_I2C_IAP_VERSION_CMD 0x0110 #define ETP_I2C_PRESSURE_CMD 0x010A #define ETP_I2C_SET_CMD 0x0300 +#define ETP_I2C_IAP_TYPE_CMD 0x0304 #define ETP_I2C_POWER_CMD 0x0307 #define ETP_I2C_FW_CHECKSUM_CMD 0x030F @@ -88,7 +91,6 @@ #ifdef CONFIG_USB_UPDATE /* The actual FW_SIZE depends on IC. */ #define FW_SIZE CONFIG_TOUCHPAD_VIRTUAL_SIZE -#define FW_PAGE_SIZE 64 #endif struct { @@ -100,6 +102,10 @@ struct { uint16_t width_y; /* Pressure adjustment */ uint8_t pressure_adj; + uint16_t ic_type; + uint16_t page_count; + uint16_t page_size; + uint16_t iap_version; } elan_tp_params; /* @@ -270,6 +276,41 @@ static int elan_tp_read_report(void) return 0; } +static int elan_get_fwinfo(void) +{ + uint16_t ic_type = elan_tp_params.ic_type; + uint16_t iap_version = elan_tp_params.iap_version; + + switch (ic_type) { + case 0x09: + elan_tp_params.page_count = 768; + break; + case 0x0D: + elan_tp_params.page_count = 896; + break; + case 0x00: + case 0x10: + case 0x14: + elan_tp_params.page_count = 1024; + break; + default: + CPRINTS("unknown ic_type: %d", ic_type); + return EC_ERROR_UNKNOWN; + } + + if (ic_type == 0x14 && iap_version >= 2) { + elan_tp_params.page_count /= 8; + elan_tp_params.page_size = 512; + } else if (ic_type >= 0x0D && iap_version >= 1) { + elan_tp_params.page_count /= 2; + elan_tp_params.page_size = 128; + } else { + elan_tp_params.page_size = 64; + } + + return EC_SUCCESS; +} + /* Initialize the controller ICs after reset */ static void elan_tp_init(void) { @@ -289,6 +330,24 @@ static void elan_tp_init(void) if (rv) goto out; + /* Read IC type, IAP version */ + rv = elan_tp_read_cmd(ETP_I2C_OSM_VERSION_CMD, &elan_tp_params.ic_type); + CPRINTS("%s: ic_type:%04X.", __func__, elan_tp_params.ic_type); + elan_tp_params.ic_type >>= 8; + if (rv) + goto out; + + rv = elan_tp_read_cmd(ETP_I2C_IAP_VERSION_CMD, + &elan_tp_params.iap_version); + CPRINTS("%s: iap_version:%04X.", __func__, elan_tp_params.iap_version); + elan_tp_params.iap_version >>= 8; + if (rv) + goto out; + + rv = elan_get_fwinfo(); + if (rv) + goto out; + /* Read min/max */ rv = elan_tp_read_cmd(ETP_I2C_MAX_X_AXIS_CMD, &elan_tp_params.max_x); if (rv) @@ -401,22 +460,23 @@ static int elan_in_main_mode(void) return val & ETP_I2C_MAIN_MODE_ON; } -static int elan_get_ic_page_count(void) +static int elan_read_write_iap_type(void) { - uint16_t ic_type; + for (int retry = 0; retry < 3; ++retry) { + uint16_t val; + + if (elan_tp_write_cmd(ETP_I2C_IAP_TYPE_CMD, + elan_tp_params.page_size / 2)) + return EC_ERROR_UNKNOWN; + + if (elan_tp_read_cmd(ETP_I2C_IAP_TYPE_CMD, &val)) + return EC_ERROR_UNKNOWN; + + if (val == elan_tp_params.page_size / 2) + return EC_SUCCESS; - elan_tp_read_cmd(ETP_I2C_OSM_VERSION_CMD, &ic_type); - CPRINTS("%s: ic_type:%04X.", __func__, ic_type); - switch (ic_type >> 8) { - case 0x09: - return 768; - case 0x0D: - return 896; - case 0x00: - case 0x10: - return 1024; } - return -1; + return EC_ERROR_UNKNOWN; } static int elan_prepare_for_update(void) @@ -440,6 +500,11 @@ static int elan_prepare_for_update(void) return EC_ERROR_UNKNOWN; } + if (elan_tp_params.ic_type >= 0x0D && elan_tp_params.iap_version >= 1) { + if (elan_read_write_iap_type()) + return EC_ERROR_UNKNOWN; + } + /* Send the passphrase again */ elan_tp_write_cmd(ETP_I2C_IAP_CMD, ETP_I2C_IAP_PASSWORD); msleep(30); @@ -459,26 +524,39 @@ static int elan_prepare_for_update(void) static int touchpad_update_page(const uint8_t *data) { - uint8_t page_store[FW_PAGE_SIZE + 4]; + const uint8_t cmd[2] = {ETP_I2C_IAP_REG_L, ETP_I2C_IAP_REG_H}; uint16_t checksum = 0; uint16_t rx_buf; int i, rv; - for (i = 0; i < FW_PAGE_SIZE; i += 2) + for (i = 0; i < elan_tp_params.page_size; i += 2) checksum += ((uint16_t)(data[i + 1]) << 8) | (data[i]); + checksum = htole16(checksum); - page_store[0] = ETP_I2C_IAP_REG_L; - page_store[1] = ETP_I2C_IAP_REG_H; - memcpy(page_store + 2, data, FW_PAGE_SIZE); - page_store[FW_PAGE_SIZE + 2 + 0] = checksum & 0xff; - page_store[FW_PAGE_SIZE + 2 + 1] = (checksum >> 8) & 0xff; + i2c_lock(CONFIG_TOUCHPAD_I2C_PORT, 1); - rv = i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT, - CONFIG_TOUCHPAD_I2C_ADDR_FLAGS, - page_store, sizeof(page_store), NULL, 0); + rv = i2c_xfer_unlocked(CONFIG_TOUCHPAD_I2C_PORT, + CONFIG_TOUCHPAD_I2C_ADDR_FLAGS, + cmd, sizeof(cmd), NULL, 0, I2C_XFER_START); + if (rv) + goto fail; + rv = i2c_xfer_unlocked(CONFIG_TOUCHPAD_I2C_PORT, + CONFIG_TOUCHPAD_I2C_ADDR_FLAGS, + data, elan_tp_params.page_size, NULL, 0, 0); + if (rv) + goto fail; + rv = i2c_xfer_unlocked(CONFIG_TOUCHPAD_I2C_PORT, + CONFIG_TOUCHPAD_I2C_ADDR_FLAGS, + (uint8_t *)&checksum, sizeof(checksum), NULL, 0, + I2C_XFER_STOP); + if (rv) + goto fail; + +fail: + i2c_lock(CONFIG_TOUCHPAD_I2C_PORT, 0); if (rv) return rv; - msleep(20); + msleep(elan_tp_params.page_size >= 512 ? 50 : 35); rv = elan_tp_read_cmd(ETP_I2C_IAP_CTRL_CMD, &rx_buf); @@ -493,16 +571,17 @@ static int touchpad_update_page(const uint8_t *data) int touchpad_update_write(int offset, int size, const uint8_t *data) { static int iap_addr = -1; - int addr, rv, page_count; + int addr, rv; CPRINTS("%s %08x %d", __func__, offset, size); if (offset == 0) { /* Verify the IC type is aligned with defined firmware size */ - page_count = elan_get_ic_page_count(); - if (FW_PAGE_SIZE * page_count != FW_SIZE) { + if (elan_tp_params.page_size * elan_tp_params.page_count + != FW_SIZE) { CPRINTS("%s: IC(%d*%d) size and FW_SIZE(%d) mismatch", - __func__, page_count, FW_PAGE_SIZE, FW_SIZE); + __func__, elan_tp_params.page_count, + elan_tp_params.page_size, FW_SIZE); return EC_ERROR_UNKNOWN; } @@ -521,18 +600,18 @@ int touchpad_update_write(int offset, int size, const uint8_t *data) CPRINTS("%s: payload starts from 0x%x.", __func__, iap_addr); } - /* Data that comes in must align with FW_PAGE_SIZE */ - if (offset % FW_PAGE_SIZE) + /* Data that comes in must align with page_size */ + if (offset % elan_tp_params.page_size) return EC_ERROR_INVAL; - for (addr = (offset / FW_PAGE_SIZE) * FW_PAGE_SIZE; - addr < (offset + size); addr += FW_PAGE_SIZE) { + for (addr = offset; addr < (offset + size); + addr += elan_tp_params.page_size) { if (iap_addr > addr) /* Skip chunk */ continue; rv = touchpad_update_page(data + addr - offset); if (rv) return rv; - CPRINTF("/p%d", addr / FW_PAGE_SIZE); + CPRINTF("/p%d", addr / elan_tp_params.page_size); watchdog_reload(); } CPRINTF("\n"); -- cgit v1.2.1