From patchwork Wed Aug 3 10:16:38 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: HungNien Chen X-Patchwork-Id: 9260975 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id ACF206048B for ; Wed, 3 Aug 2016 10:16:32 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9AF5128565 for ; Wed, 3 Aug 2016 10:16:32 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8EE392857B; Wed, 3 Aug 2016 10:16:32 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 19A5628565 for ; Wed, 3 Aug 2016 10:16:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757612AbcHCKQU (ORCPT ); Wed, 3 Aug 2016 06:16:20 -0400 Received: from ml01.weidahitech.com ([61.222.87.235]:1991 "EHLO ml01.weidahitech.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754783AbcHCKPz (ORCPT ); Wed, 3 Aug 2016 06:15:55 -0400 X-Greylist: delayed 445 seconds by postgrey-1.27 at vger.kernel.org; Wed, 03 Aug 2016 06:15:53 EDT Received: from mail02.WHT.local (mail02.wht.local [192.168.10.16]) by ml01.weidahitech.com (8.13.8/8.13.8) with ESMTP id u73AFUZM023679; Wed, 3 Aug 2016 18:15:30 +0800 Received: from x-Veriton-M4620G.WHT.local (192.168.10.88) by MAIL02.WHT.local (192.168.10.16) with Microsoft SMTP Server id 14.2.347.0; Wed, 3 Aug 2016 18:15:32 +0800 From: HungNien Chen To: CC: , , HungNien Chen Subject: [PATCH] Input: wdt87xx_i2c - support new body WDT8752 Date: Wed, 3 Aug 2016 18:16:38 +0800 Message-ID: <1470219398-10892-1-git-send-email-hn.chen@weidahitech.com> X-Mailer: git-send-email 1.9.1 MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Considering to be compatible with i2c-hid, WDT8752 has the same way in enumerating device. And also modify the part of FW update to be more efficiency. The main modification is that reducing the amount of data transmitted and using polling for time comsuming operation. Flash erase will wait 50ms for the operation complete in last driver. Extend it to 200ms since the spec says the typical is 30ms but the max is 200ms. Please review it and thanks ! Signed-off-by: HungNien Chen --- drivers/input/touchscreen/wdt87xx_i2c.c | 944 ++++++++++++++++++++++++-------- 1 file changed, 730 insertions(+), 214 deletions(-) diff --git a/drivers/input/touchscreen/wdt87xx_i2c.c b/drivers/input/touchscreen/wdt87xx_i2c.c index 73861ad..29966aa 100644 --- a/drivers/input/touchscreen/wdt87xx_i2c.c +++ b/drivers/input/touchscreen/wdt87xx_i2c.c @@ -22,10 +22,15 @@ #include #include -#define WDT87XX_NAME "wdt87xx_i2c" -#define WDT87XX_DRV_VER "0.9.7" -#define WDT87XX_FW_NAME "wdt87xx_fw.bin" -#define WDT87XX_CFG_NAME "wdt87xx_cfg.bin" +#define WDT87XX_NAME "wdt87xx_i2c" +#define WDT87XX_DRV_VER "0.9.8" +#define WDT87XX_FW_NAME "wdt87xx_fw.bin" +#define WDT87XX_CFG_NAME "wdt87xx_cfg.bin" + +#define PLT_WDT8756 0x00 +#define PLT_WDT8752 0x01 + +#define RPT_ID_TOUCH 0x01 #define MODE_ACTIVE 0x01 #define MODE_READY 0x02 @@ -34,8 +39,7 @@ #define MODE_STOP 0xFF #define WDT_MAX_FINGER 10 -#define WDT_RAW_BUF_COUNT 54 -#define WDT_V1_RAW_BUF_COUNT 74 +#define WDT_RAW_BUF_COUNT 76 #define WDT_FIRMWARE_ID 0xa9e368f5 #define PG_SIZE 0x1000 @@ -43,32 +47,23 @@ #define MAX_UNIT_AXIS 0x7FFF +#define PKT_TX_SIZE 16 #define PKT_READ_SIZE 72 #define PKT_WRITE_SIZE 80 /* the finger definition of the report event */ #define FINGER_EV_OFFSET_ID 0 -#define FINGER_EV_OFFSET_X 1 -#define FINGER_EV_OFFSET_Y 3 -#define FINGER_EV_SIZE 5 - -#define FINGER_EV_V1_OFFSET_ID 0 -#define FINGER_EV_V1_OFFSET_W 1 -#define FINGER_EV_V1_OFFSET_P 2 -#define FINGER_EV_V1_OFFSET_X 3 -#define FINGER_EV_V1_OFFSET_Y 5 -#define FINGER_EV_V1_SIZE 7 +#define FINGER_EV_OFFSET_W 1 +#define FINGER_EV_OFFSET_P 2 +#define FINGER_EV_OFFSET_X 3 +#define FINGER_EV_OFFSET_Y 5 +#define FINGER_EV_SIZE 7 /* The definition of a report packet */ #define TOUCH_PK_OFFSET_REPORT_ID 0 #define TOUCH_PK_OFFSET_EVENT 1 -#define TOUCH_PK_OFFSET_SCAN_TIME 51 -#define TOUCH_PK_OFFSET_FNGR_NUM 53 - -#define TOUCH_PK_V1_OFFSET_REPORT_ID 0 -#define TOUCH_PK_V1_OFFSET_EVENT 1 -#define TOUCH_PK_V1_OFFSET_SCAN_TIME 71 -#define TOUCH_PK_V1_OFFSET_FNGR_NUM 73 +#define TOUCH_PK_OFFSET_SCAN_TIME 71 +#define TOUCH_PK_OFFSET_FNGR_NUM 73 /* The definition of the controller parameters */ #define CTL_PARAM_OFFSET_FW_ID 0 @@ -84,6 +79,7 @@ #define CTL_PARAM_OFFSET_PHY_W 22 #define CTL_PARAM_OFFSET_PHY_H 24 #define CTL_PARAM_OFFSET_FACTOR 32 +#define CTL_PARAM_OFFSET_I2C_CFG 36 /* The definition of the device descriptor */ #define WDT_GD_DEVICE 1 @@ -95,6 +91,8 @@ #define VND_REQ_READ 0x06 #define VND_READ_DATA 0x07 #define VND_REQ_WRITE 0x08 +#define VND_REQ_FW_INFO 0xF2 +#define VND_REQ_CTRLER_INFO 0xF4 #define VND_CMD_START 0x00 #define VND_CMD_STOP 0x01 @@ -104,6 +102,8 @@ #define VND_GET_CHECKSUM 0x66 +#define VND_CMD_DEV_MODE 0x82 + #define VND_SET_DATA 0x83 #define VND_SET_COMMAND_DATA 0x84 #define VND_SET_CHECKSUM_CALC 0x86 @@ -155,11 +155,84 @@ #define FW_CHUNK_PAYLOAD_OFFSET 32 /* Controller requires minimum 300us between commands */ -#define WDT_COMMAND_DELAY_MS 2 -#define WDT_FLASH_WRITE_DELAY_MS 4 -#define WDT_FW_RESET_TIME 2500 +#define WDT_CMD_DELAY_US 300 +#define WDT_ERASE4K_DELAY_MS 500 +#define WDT_FLASH_WRITE_DELAY_MS 2 +#define WDT_FW_RESET_TIME_MS 2500 +#define WDT_POLLING_PERIOD_MS 20 +#define W8756_ERASE4K_DELAY_MS 200 + +/* The definition for WDT8752 */ +#define W8752_READ_OFFSET_MASK 0x10000 +#define W8752_DEV_INFO_READ_OFFSET 0xC +#define W8752_PKT_HEADER_SZ 4 +#define W8752_PKT_SIZE 60 + +#define W8752_STATUS_OK 0x80 +#define W8752_STATUS_BUSY 0xFE + +/* Communication commands of WDT8752 */ +#define W8752_BASIC_COMMAND 0x85 +#define W8752_FW_COMMAND 0x91 + +#define W8755_FW_GET_DEV_INFO 0x73 + +#define W8752_SET_FLASH 0x83 +#define W8752_SET_FLASH_ADDRESS 0x87 +#define W8752_SET_CHECKSUM_CALC 0x88 +#define W8752_GET_CHECKSUM 0x65 + +#define W8752_CMD_SFLOCK 0x00 +#define W8752_CMD_SFUNLOCK 0x01 +#define W8752_CMD_RESET 0x02 +#define W8752_CMD_ERASE4K 0x03 +#define W8752_CMD_DEV_MODE 0x82 + +#define W8752_DM_SENSING 0x1 +#define W8752_DM_DOZE 0x2 +#define W8752_DM_COMMAND 0x90 + +#define W8752_SFLOCK_KEY 0x9B +#define W8752_SFUNLOCK_KEY 0xDA + +/* The definition of the command packet of WDT8752 */ +#define CMD_SIZE_OFFSET 0x2 +#define CMD_ID_OFFSET 0x4 +#define CMD_DATA1_OFFSET 0x4 +#define CMD_VALUE_OFFSET 0x5 + +#define W8752_POLLING_PERIOD_US 5000 +#define W8752_FLASH_WRITE_DELAY_US 100 + +#define W8752_PROG_SECTOR_SIZE 0x100 + +#define W8752_HID_DESC_ADDR 0x20 + +/* The definition of controller parameters of WDT8752 */ +#define W8752_PARAM_KEY 0x154f +#define W8752_PARAM_KEY_OFFSET 0x2 +#define W8752_PLAT_ID_OFFSET 0x5 +#define W8752_PARAM_OFFSET 0xA +#define W8752_PARAM_LEN_OFFSET 0xC + +struct i2c_hid_desc { + u16 desc_length; + u16 bcd_version; + u16 rpt_desc_length; + u16 rpt_desc_register; + u16 input_register; + u16 max_input_length; + u16 output_register; + u16 max_output_length; + u16 cmd_register; + u16 data_register; + u16 vendor_id; + u16 product_id; + u16 version_id; + u32 reserved; +} __packed; -struct wdt87xx_sys_param { +struct wdt87xx_param { u16 fw_id; u16 plat_id; u16 xmls_id1; @@ -173,17 +246,30 @@ struct wdt87xx_sys_param { u32 max_y; u16 vendor_id; u16 product_id; -}; + u16 i2c_cfg; +} __packed; struct wdt87xx_data { struct i2c_client *client; struct input_dev *input; /* Mutex for fw update to prevent concurrent access */ struct mutex fw_mutex; - struct wdt87xx_sys_param param; + struct wdt87xx_param param; + struct i2c_hid_desc hid_desc; u8 phys[32]; + u32 plt_id; + + /* Function ptrs for different controller body */ + int (*send_cmd)(struct i2c_client *client, int cmd, int value); + int (*write_flash)(struct i2c_client *client, const char *data, + u32 addr, size_t len); + int (*chksum_check)(struct i2c_client *client, const char *data, + u32 addr, size_t len); + int (*delay)(struct i2c_client *client, u32 delay); }; +static int wdt8752_set_dev_mode(struct i2c_client *client, u8 mode); + static int wdt87xx_i2c_xfer(struct i2c_client *client, void *txdata, size_t txlen, void *rxdata, size_t rxlen) @@ -213,6 +299,8 @@ static int wdt87xx_i2c_xfer(struct i2c_client *client, return error; } + udelay(WDT_CMD_DELAY_US); + return 0; } @@ -224,8 +312,8 @@ static int wdt87xx_get_desc(struct i2c_client *client, u8 desc_idx, tx_buf[2] |= desc_idx & 0xF; - error = wdt87xx_i2c_xfer(client, tx_buf, sizeof(tx_buf), - buf, len); + error = wdt87xx_i2c_xfer(client, tx_buf, sizeof(tx_buf), buf, len); + if (error) { dev_err(&client->dev, "get desc failed: %d\n", error); return error; @@ -237,8 +325,6 @@ static int wdt87xx_get_desc(struct i2c_client *client, u8 desc_idx, return -EINVAL; } - mdelay(WDT_COMMAND_DELAY_MS); - return 0; } @@ -269,23 +355,23 @@ static int wdt87xx_get_string(struct i2c_client *client, u8 str_idx, rx_len = min_t(size_t, len, rx_buf[0]); memcpy(buf, &rx_buf[2], rx_len); - mdelay(WDT_COMMAND_DELAY_MS); - return 0; } static int wdt87xx_get_feature(struct i2c_client *client, - u8 *buf, size_t buf_size) + u8 *buf, size_t len) { - u8 tx_buf[8]; + u8 tx_buf[PKT_TX_SIZE]; u8 rx_buf[PKT_WRITE_SIZE]; size_t tx_len = 0; - size_t rx_len = buf_size + 2; + size_t rx_len = len + 2; int error; if (rx_len > sizeof(rx_buf)) return -EINVAL; + memset(tx_buf, 0, sizeof(tx_buf)); + /* Get feature command packet */ tx_buf[tx_len++] = 0x22; tx_buf[tx_len++] = 0x00; @@ -306,16 +392,14 @@ static int wdt87xx_get_feature(struct i2c_client *client, return error; } - rx_len = min_t(size_t, buf_size, get_unaligned_le16(rx_buf)); + rx_len = min_t(size_t, len, get_unaligned_le16(rx_buf)); memcpy(buf, &rx_buf[2], rx_len); - mdelay(WDT_COMMAND_DELAY_MS); - return 0; } static int wdt87xx_set_feature(struct i2c_client *client, - const u8 *buf, size_t buf_size) + const u8 *buf, size_t len) { u8 tx_buf[PKT_WRITE_SIZE]; int tx_len = 0; @@ -334,22 +418,21 @@ static int wdt87xx_set_feature(struct i2c_client *client, } tx_buf[tx_len++] = 0x23; tx_buf[tx_len++] = 0x00; - tx_buf[tx_len++] = (buf_size & 0xFF); - tx_buf[tx_len++] = ((buf_size & 0xFF00) >> 8); + tx_buf[tx_len++] = (len & 0xFF); + tx_buf[tx_len++] = ((len & 0xFF00) >> 8); - if (tx_len + buf_size > sizeof(tx_buf)) + if (tx_len + len > sizeof(tx_buf)) return -EINVAL; - memcpy(&tx_buf[tx_len], buf, buf_size); - tx_len += buf_size; + memcpy(&tx_buf[tx_len], buf, len); + tx_len += len; error = i2c_master_send(client, tx_buf, tx_len); if (error < 0) { dev_err(&client->dev, "set feature failed: %d\n", error); return error; } - - mdelay(WDT_COMMAND_DELAY_MS); + udelay(WDT_CMD_DELAY_US); return 0; } @@ -394,20 +477,392 @@ static int wdt87xx_send_command(struct i2c_client *client, int cmd, int value) return wdt87xx_set_feature(client, cmd_buf, sizeof(cmd_buf)); } +static u16 misr(u16 cur_value, u16 new_value) +{ + u32 a, b; + u32 bit0; + u32 y; + + a = cur_value; + b = new_value; + bit0 = a ^ (b & 1); + bit0 ^= a >> 1; + bit0 ^= a >> 2; + bit0 ^= a >> 4; + bit0 ^= a >> 5; + bit0 ^= a >> 7; + bit0 ^= a >> 11; + bit0 ^= a >> 15; + y = (a << 1) ^ b; + y = (y & ~1) | (bit0 & 1); + + return (u16)y; +} + +static u16 wdt87xx_calculate_checksum(const u8 *data, size_t len, int byte_mode) +{ + u16 checksum = 0; + u16 *pdata_u16; + size_t i; + + if (byte_mode) + for (i = 0; i < len; i++) + checksum = misr(checksum, (u16)data[i]); + else { + pdata_u16 = (u16 *)data; + for (i = 0; i < (len >> 1); i++) + checksum = misr(checksum, *pdata_u16++); + } + + return checksum; +} + +static int wdt8752_send_command(struct i2c_client *client, int cmd, int value) +{ + u8 cmd_buf[PKT_BUF_SIZE]; + size_t size = 2; + + /* Set the command packet and the packet size is variable in 8752 */ + cmd_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_WRITE; + cmd_buf[CMD_TYPE_OFFSET] = W8752_BASIC_COMMAND; + + switch (cmd) { + case VND_CMD_STOP: + /* + * Command STOP with STOP value, enter the command loop mode + * for operating the flash in 8752 + */ + if (value == MODE_STOP) + return wdt8752_set_dev_mode(client, W8752_DM_COMMAND); + + /* + * Command STOP with IDLE value, set the controller into DOZE + * mode in 8752 + */ + if (value == MODE_IDLE) + value = W8752_DM_DOZE; + + case VND_CMD_DEV_MODE: + cmd_buf[CMD_TYPE_OFFSET] = W8752_FW_COMMAND; + cmd_buf[CMD_ID_OFFSET] = W8752_CMD_DEV_MODE; + cmd_buf[CMD_VALUE_OFFSET] = value; + break; + + case VND_CMD_START: + return wdt8752_set_dev_mode(client, W8752_DM_SENSING); + + case VND_CMD_RESET: + cmd_buf[CMD_ID_OFFSET] = W8752_CMD_RESET; + size = 1; + break; + + case VND_CMD_SFLCK: + cmd_buf[CMD_ID_OFFSET] = W8752_CMD_SFLOCK; + cmd_buf[CMD_VALUE_OFFSET] = W8752_SFLOCK_KEY; + break; + + case VND_CMD_SFUNL: + cmd_buf[CMD_ID_OFFSET] = W8752_CMD_SFUNLOCK; + cmd_buf[CMD_VALUE_OFFSET] = W8752_SFUNLOCK_KEY; + break; + + case VND_CMD_ERASE: + cmd_buf[CMD_ID_OFFSET] = W8752_CMD_ERASE4K; + put_unaligned_le32(value, &cmd_buf[CMD_VALUE_OFFSET]); + size = 5; + break; + + default: + cmd_buf[CMD_REPORT_ID_OFFSET] = 0; + dev_err(&client->dev, "Invalid command: %d\n", cmd); + return -EINVAL; + } + + put_unaligned_le16(size, &cmd_buf[CMD_SIZE_OFFSET]); + + return wdt87xx_set_feature(client, cmd_buf, W8752_PKT_HEADER_SZ + size); +} + +static int wdt8752_exec_read_pkt(struct i2c_client *client, u8 type, + u8 *data, size_t len, int offset) +{ + u8 pkt_buf[PKT_BUF_SIZE]; + int error; + size_t size; + + /* + * Some vendor commands can read the data structure from controller, + * set the mask to indicate the offset. + */ + if (offset & W8752_READ_OFFSET_MASK) + size = offset & 0xFF; + else + size = len; + + pkt_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_READ; + pkt_buf[CMD_TYPE_OFFSET] = type; + put_unaligned_le16(size, &pkt_buf[CMD_SIZE_OFFSET]); + + error = wdt87xx_set_feature(client, pkt_buf, W8752_PKT_HEADER_SZ); + if (error) + return error; + + pkt_buf[CMD_REPORT_ID_OFFSET] = VND_READ_DATA; + pkt_buf[CMD_TYPE_OFFSET] = type; + error = wdt87xx_get_feature(client, pkt_buf, PKT_BUF_SIZE); + if (error) + return error; + + if (pkt_buf[CMD_REPORT_ID_OFFSET] != VND_READ_DATA) { + dev_err(&client->dev, "wrong id of fw response: 0x%x\n", + pkt_buf[CMD_REPORT_ID_OFFSET]); + return -EINVAL; + } + memcpy(data, &pkt_buf[CMD_DATA1_OFFSET], len); + + return 0; +} + +static int wdt8752_get_device_mode(struct i2c_client *client, u8 *pmode) +{ + u8 cmd_buf[PKT_BUF_SIZE]; + int error; + + error = wdt8752_exec_read_pkt(client, W8755_FW_GET_DEV_INFO, cmd_buf, + W8752_PKT_SIZE, W8752_READ_OFFSET_MASK | + W8752_DEV_INFO_READ_OFFSET); + if (error) + return error; + + *pmode = cmd_buf[0]; + return 0; +} + +static int wdt8752_set_dev_mode(struct i2c_client *client, u8 mode) +{ + int count = 20; + int error; + u8 mode_r; + + do { + error = wdt8752_send_command(client, W8752_CMD_DEV_MODE, mode); + if (error) + return error; + + udelay(W8752_POLLING_PERIOD_US); + error = wdt8752_get_device_mode(client, &mode_r); + if (error) + return error; + } while (mode != mode_r && count-- > 0); + + if (mode != mode_r) { + dev_err(&client->dev, "failed to change mode: 0x%x, 0x%x\n", + mode, mode_r); + return -ETIME; + } + + return 0; +} + +static int wdt8752_exec_write_pkt(struct i2c_client *client, u8 type, u8 *data, + size_t len) +{ + u8 pkt_buf[PKT_BUF_SIZE]; + + pkt_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_WRITE; + pkt_buf[CMD_TYPE_OFFSET] = type; + put_unaligned_le16(len, &pkt_buf[CMD_SIZE_OFFSET]); + + memcpy(&pkt_buf[CMD_DATA1_OFFSET], data, len); + + return wdt87xx_set_feature(client, pkt_buf, W8752_PKT_HEADER_SZ + len); +} + +static int wdt8752_delay(struct i2c_client *client, u32 delay) +{ + u8 rc = W8752_STATUS_BUSY; + u8 raw_buf[PKT_BUF_SIZE]; + int count; + int error; + + count = (delay / WDT_POLLING_PERIOD_MS) + 1; + + do { + msleep(WDT_POLLING_PERIOD_MS); + + error = i2c_master_recv(client, raw_buf, 3); + if (error < 0) { + dev_err(&client->dev, "read raw data failed: (%d)\n", + error); + return error; + } + + rc = raw_buf[2]; + } while (rc != W8752_STATUS_OK && count-- > 0); + + return 0; +} + +static int wdt8752_checksum_check(struct i2c_client *client, const char *data, + u32 addr, size_t len) +{ + u8 pkt_buf[PKT_BUF_SIZE]; + int time_delay; + int error; + u16 dev_chksum, fw_chksum; + + put_unaligned_le32(addr, &pkt_buf[0]); + put_unaligned_le32(len, &pkt_buf[4]); + + error = wdt8752_exec_write_pkt(client, W8752_SET_CHECKSUM_CALC, + pkt_buf, 8); + if (error) { + dev_err(&client->dev, "failed to write chksum_calc\n"); + return error; + } + + /* + * It takes about 2ms for every 1K bytes doing the checksum in FW. + * Wait here for the operation to complete. + */ + time_delay = DIV_ROUND_UP(len, 1024); + error = wdt8752_delay(client, time_delay * 4); + if (error) + return error; + + error = wdt8752_exec_read_pkt(client, W8752_GET_CHECKSUM, pkt_buf, + W8752_PKT_SIZE, 0); + if (error) { + dev_err(&client->dev, "failed to read chksum\n"); + return error; + } + + dev_chksum = get_unaligned_le16(pkt_buf); + + /* Calculate the checksum in u16 */ + fw_chksum = wdt87xx_calculate_checksum(data, len, 0); + if (dev_chksum == fw_chksum) + return 0; + + dev_err(&client->dev, "checksum fail: %d vs %d\n", + dev_chksum, fw_chksum); + return -EAGAIN; +} + +static int wdt8752_flash_write_sector(struct i2c_client *client, u8 *data, + u32 addr, size_t len) +{ + int st_addr; + size_t pkt_size; + u8 *pdata; + int error; + u8 pkt_buf[PKT_BUF_SIZE]; + + /* Address and length should be 4 bytes aligned */ + if ((addr & 0x3) != 0 || (len & 0x3) != 0) { + dev_err(&client->dev, + "addr & len must be 4 bytes aligned %x, %zu\n", + addr, len); + return -EINVAL; + } + + st_addr = addr; + pdata = data; + + put_unaligned_le32(addr, &pkt_buf[0]); + + /* initialize the programming address first */ + error = wdt8752_exec_write_pkt(client, W8752_SET_FLASH_ADDRESS, pkt_buf, + sizeof(u32)); + if (error) { + dev_err(&client->dev, "failed to set flash address: 0x%x\n", + addr); + return error; + } + + while (len) { + pkt_size = min_t(size_t, len, W8752_PKT_SIZE); + + error = wdt8752_exec_write_pkt(client, W8752_SET_FLASH, pdata, + pkt_size); + if (error) { + dev_dbg(&client->dev, "failed to program flash: 0x%x\n", + st_addr); + return error; + } + + len -= pkt_size; + pdata += pkt_size; + st_addr += pkt_size; + + udelay(W8752_FLASH_WRITE_DELAY_US); + } + + return 0; +} + +static int wdt8752_write_data(struct i2c_client *client, const char *data, + u32 addr, size_t len) +{ + int error; + u8 *pdata = (u8 *)data; + u32 write_size; + + if (addr & (W8752_PROG_SECTOR_SIZE - 1)) { + dev_err(&client->dev, "start addr must be sector aligned\n"); + return -EINVAL; + } + + while (len) { + write_size = min_t(size_t, len, W8752_PROG_SECTOR_SIZE); + + error = wdt8752_flash_write_sector(client, pdata, addr, + write_size); + + if (error) + return error; + + pdata += W8752_PROG_SECTOR_SIZE; + addr += W8752_PROG_SECTOR_SIZE; + len -= write_size; + } + + return 0; +} + +static int wdt87xx_delay(struct i2c_client *client, u32 delay) +{ + /* + * According to the spec, 4k erase takes the longest time in operations + * and W8756 have to wait it at most 200 ms + */ + if (delay > W8756_ERASE4K_DELAY_MS) + delay = W8756_ERASE4K_DELAY_MS; + + if (delay > WDT_POLLING_PERIOD_MS) + msleep(delay); + else + udelay(delay * 1000); + + return 0; +} + static int wdt87xx_sw_reset(struct i2c_client *client) { int error; + struct wdt87xx_data *wdt = i2c_get_clientdata(client); dev_dbg(&client->dev, "resetting device now\n"); - error = wdt87xx_send_command(client, VND_CMD_RESET, 0); + error = wdt->send_cmd(client, VND_CMD_RESET, 0); + if (error) { dev_err(&client->dev, "reset failed\n"); return error; } /* Wait the device to be ready */ - msleep(WDT_FW_RESET_TIME); + msleep(WDT_FW_RESET_TIME_MS); return 0; } @@ -425,32 +880,16 @@ static const void *wdt87xx_get_fw_chunk(const struct firmware *fw, u32 id) chunk_size = get_unaligned_le32(fw->data + pos + FW_CHUNK_SIZE_OFFSET); - pos += chunk_size + 2 * sizeof(u32); /* chunk ID + size */ + /* chunk ID + size */ + pos += chunk_size + 2 * sizeof(u32); } return NULL; } -static int wdt87xx_get_sysparam(struct i2c_client *client, - struct wdt87xx_sys_param *param) +static void wdt87xx_parse_param(struct wdt87xx_data *wdt, u8 *buf, size_t len) { - u8 buf[PKT_READ_SIZE]; - int error; - - error = wdt87xx_get_desc(client, WDT_GD_DEVICE, buf, 18); - if (error) { - dev_err(&client->dev, "failed to get device desc\n"); - return error; - } - - param->vendor_id = get_unaligned_le16(buf + DEV_DESC_OFFSET_VID); - param->product_id = get_unaligned_le16(buf + DEV_DESC_OFFSET_PID); - - error = wdt87xx_get_string(client, STRIDX_PARAMETERS, buf, 34); - if (error) { - dev_err(&client->dev, "failed to get parameters\n"); - return error; - } + struct wdt87xx_param *param = &wdt->param; param->xmls_id1 = get_unaligned_le16(buf + CTL_PARAM_OFFSET_XMLS_ID1); param->xmls_id2 = get_unaligned_le16(buf + CTL_PARAM_OFFSET_XMLS_ID2); @@ -459,6 +898,9 @@ static int wdt87xx_get_sysparam(struct i2c_client *client, param->phy_w = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_W) / 10; param->phy_h = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_H) / 10; + /* Get the report mode */ + param->i2c_cfg = get_unaligned_le16(buf + CTL_PARAM_OFFSET_I2C_CFG); + /* Get the scaling factor of pixel to logical coordinate */ param->scaling_factor = get_unaligned_le16(buf + CTL_PARAM_OFFSET_FACTOR); @@ -466,34 +908,61 @@ static int wdt87xx_get_sysparam(struct i2c_client *client, param->max_x = MAX_UNIT_AXIS; param->max_y = DIV_ROUND_CLOSEST(MAX_UNIT_AXIS * param->phy_h, param->phy_w); +} - error = wdt87xx_get_string(client, STRIDX_PLATFORM_ID, buf, 8); - if (error) { - dev_err(&client->dev, "failed to get platform id\n"); +static int wdt87xx_get_param_hid(struct wdt87xx_data *wdt) +{ + u8 buf[PKT_READ_SIZE]; + int error; + struct i2c_client *client = wdt->client; + struct wdt87xx_param *param = &wdt->param; + + put_unaligned_le16(W8752_HID_DESC_ADDR, buf); + + error = wdt87xx_i2c_xfer(client, buf, 2, &wdt->hid_desc, + sizeof(wdt->hid_desc)); + if (error < 0) { + dev_err(&client->dev, "failed to get hid desc\n"); return error; } - param->plat_id = buf[1]; + param->vendor_id = wdt->hid_desc.vendor_id; + param->product_id = wdt->hid_desc.product_id; - buf[0] = 0xf2; - error = wdt87xx_get_feature(client, buf, 16); - if (error) { - dev_err(&client->dev, "failed to get firmware id\n"); + return 0; +} + +static int wdt87xx_get_param_private(struct wdt87xx_data *wdt) +{ + u8 buf[PKT_READ_SIZE]; + int error, str_len; + struct i2c_client *client = wdt->client; + struct wdt87xx_param *param = &wdt->param; + + error = wdt87xx_get_desc(client, WDT_GD_DEVICE, buf, 18); + if (error < 0) { + dev_err(&client->dev, "failed to get device desc\n"); return error; } - if (buf[0] != 0xf2) { - dev_err(&client->dev, "wrong id of fw response: 0x%x\n", - buf[0]); - return -EINVAL; + param->vendor_id = get_unaligned_le16(buf + DEV_DESC_OFFSET_VID); + param->product_id = get_unaligned_le16(buf + DEV_DESC_OFFSET_PID); + + str_len = wdt87xx_get_string(client, STRIDX_PARAMETERS, buf, 38); + if (str_len < 0) { + dev_err(&client->dev, "failed to get parameters\n"); + return str_len; } - param->fw_id = get_unaligned_le16(&buf[1]); + wdt87xx_parse_param(wdt, buf, str_len); - dev_info(&client->dev, - "fw_id: 0x%x, plat_id: 0x%x, xml_id1: %04x, xml_id2: %04x\n", - param->fw_id, param->plat_id, - param->xmls_id1, param->xmls_id2); + error = wdt87xx_get_string(client, STRIDX_PLATFORM_ID, buf, 8); + if (error < 0) { + dev_err(&client->dev, "failed to get platform id\n"); + return error; + } + + param->plat_id = buf[1]; return 0; } @@ -547,8 +1016,15 @@ static int wdt87xx_validate_firmware(struct wdt87xx_data *wdt, return 0; } -static int wdt87xx_validate_fw_chunk(const void *data, int id) +static int wdt87xx_validate_fw_chunk(struct i2c_client *client, + const void *data, int id) { + struct wdt87xx_data *wdt = i2c_get_clientdata(client); + + /* There is no fw_id tag could be checked in 8752 */ + if (wdt->plt_id == PLT_WDT8752) + return 0; + if (id == CHUNK_ID_FRWR) { u32 fw_id; @@ -561,37 +1037,37 @@ static int wdt87xx_validate_fw_chunk(const void *data, int id) } static int wdt87xx_write_data(struct i2c_client *client, const char *data, - u32 address, int length) + u32 addr, size_t len) { - u16 packet_size; + size_t pkt_size; int count = 0; int error; u8 pkt_buf[PKT_BUF_SIZE]; /* Address and length should be 4 bytes aligned */ - if ((address & 0x3) != 0 || (length & 0x3) != 0) { + if ((addr & 0x3) != 0 || (len & 0x3) != 0) { dev_err(&client->dev, - "addr & len must be 4 bytes aligned %x, %x\n", - address, length); + "addr & len must be 4 bytes aligned %x, %zu\n", + addr, len); return -EINVAL; } - while (length) { - packet_size = min(length, PACKET_SIZE); + while (len) { + pkt_size = min_t(size_t, len, PACKET_SIZE); pkt_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_WRITE; pkt_buf[CMD_TYPE_OFFSET] = VND_SET_DATA; - put_unaligned_le16(packet_size, &pkt_buf[CMD_INDEX_OFFSET]); - put_unaligned_le32(address, &pkt_buf[CMD_LENGTH_OFFSET]); - memcpy(&pkt_buf[CMD_DATA_OFFSET], data, packet_size); + put_unaligned_le16(pkt_size, &pkt_buf[CMD_INDEX_OFFSET]); + put_unaligned_le32(addr, &pkt_buf[CMD_LENGTH_OFFSET]); + memcpy(&pkt_buf[CMD_DATA_OFFSET], data, pkt_size); error = wdt87xx_set_feature(client, pkt_buf, sizeof(pkt_buf)); if (error) return error; - length -= packet_size; - data += packet_size; - address += packet_size; + len -= pkt_size; + data += pkt_size; + addr += pkt_size; /* Wait for the controller to finish the write */ mdelay(WDT_FLASH_WRITE_DELAY_MS); @@ -605,61 +1081,30 @@ static int wdt87xx_write_data(struct i2c_client *client, const char *data, return 0; } -static u16 misr(u16 cur_value, u8 new_value) -{ - u32 a, b; - u32 bit0; - u32 y; - - a = cur_value; - b = new_value; - bit0 = a ^ (b & 1); - bit0 ^= a >> 1; - bit0 ^= a >> 2; - bit0 ^= a >> 4; - bit0 ^= a >> 5; - bit0 ^= a >> 7; - bit0 ^= a >> 11; - bit0 ^= a >> 15; - y = (a << 1) ^ b; - y = (y & ~1) | (bit0 & 1); - - return (u16)y; -} - -static u16 wdt87xx_calculate_checksum(const u8 *data, size_t length) -{ - u16 checksum = 0; - size_t i; - - for (i = 0; i < length; i++) - checksum = misr(checksum, data[i]); +static int wdt87xx_checksum_check(struct i2c_client *client, const char *data, + u32 addr, size_t len) - return checksum; -} - -static int wdt87xx_get_checksum(struct i2c_client *client, u16 *checksum, - u32 address, int length) { int error; int time_delay; u8 pkt_buf[PKT_BUF_SIZE]; u8 cmd_buf[CMD_BUF_SIZE]; + u16 dev_chksum, fw_chksum; - error = wdt87xx_send_command(client, VND_SET_CHECKSUM_LENGTH, length); + error = wdt87xx_send_command(client, VND_SET_CHECKSUM_LENGTH, len); if (error) { dev_err(&client->dev, "failed to set checksum length\n"); return error; } - error = wdt87xx_send_command(client, VND_SET_CHECKSUM_CALC, address); + error = wdt87xx_send_command(client, VND_SET_CHECKSUM_CALC, addr); if (error) { dev_err(&client->dev, "failed to set checksum address\n"); return error; } /* Wait the operation to complete */ - time_delay = DIV_ROUND_UP(length, 1024); + time_delay = DIV_ROUND_UP(len, 1024); msleep(time_delay * 30); memset(cmd_buf, 0, sizeof(cmd_buf)); @@ -679,82 +1124,81 @@ static int wdt87xx_get_checksum(struct i2c_client *client, u16 *checksum, return error; } - *checksum = get_unaligned_le16(&pkt_buf[CMD_DATA_OFFSET]); - return 0; + dev_chksum = get_unaligned_le16(&pkt_buf[CMD_DATA_OFFSET]); + + /* Calculate the checksum in bytes */ + fw_chksum = wdt87xx_calculate_checksum(data, len, 1); + if (dev_chksum == fw_chksum) + return 0; + + dev_err(&client->dev, + "checksum fail: %d vs %d\n", dev_chksum, fw_chksum); + return -EAGAIN; } static int wdt87xx_write_firmware(struct i2c_client *client, const void *chunk) { - u32 start_addr = get_unaligned_le32(chunk + FW_CHUNK_TGT_START_OFFSET); - u32 size = get_unaligned_le32(chunk + FW_CHUNK_PAYLOAD_LEN_OFFSET); + u32 st_addr = get_unaligned_le32(chunk + FW_CHUNK_TGT_START_OFFSET); + size_t len = get_unaligned_le32(chunk + FW_CHUNK_PAYLOAD_LEN_OFFSET); const void *data = chunk + FW_CHUNK_PAYLOAD_OFFSET; int error; int err1; - int page_size; + int pg_size; int retry = 0; - u16 device_checksum, firmware_checksum; + struct wdt87xx_data *wdt = i2c_get_clientdata(client); dev_dbg(&client->dev, "start 4k page program\n"); - error = wdt87xx_send_command(client, VND_CMD_STOP, MODE_STOP); + error = wdt->send_cmd(client, VND_CMD_STOP, MODE_STOP); if (error) { - dev_err(&client->dev, "stop report mode failed\n"); + dev_err(&client->dev, "failed to stop report\n"); return error; } - error = wdt87xx_send_command(client, VND_CMD_SFUNL, 0); + error = wdt->send_cmd(client, VND_CMD_SFUNL, 0); if (error) { - dev_err(&client->dev, "unlock failed\n"); + dev_err(&client->dev, "failed to unlock flash\n"); goto out_enable_reporting; } - mdelay(10); + msleep(20); - while (size) { - dev_dbg(&client->dev, "%s: %x, %x\n", __func__, - start_addr, size); + while (len) { + dev_dbg(&client->dev, "%s: %x, %zu\n", __func__, st_addr, len); - page_size = min_t(u32, size, PG_SIZE); - size -= page_size; + pg_size = min_t(size_t, len, PG_SIZE); for (retry = 0; retry < MAX_RETRIES; retry++) { - error = wdt87xx_send_command(client, VND_CMD_ERASE, - start_addr); + error = wdt->send_cmd(client, VND_CMD_ERASE, st_addr); if (error) { dev_err(&client->dev, - "erase failed at %#08x\n", start_addr); + "erase failed at %#08x\n", st_addr); break; } - msleep(50); - - error = wdt87xx_write_data(client, data, start_addr, - page_size); + error = wdt->delay(client, WDT_ERASE4K_DELAY_MS); if (error) { dev_err(&client->dev, - "write failed at %#08x (%d bytes)\n", - start_addr, page_size); + "delay failed at %#08x\n", st_addr); break; } - error = wdt87xx_get_checksum(client, &device_checksum, - start_addr, page_size); + error = wdt->write_flash(client, data, st_addr, + pg_size); if (error) { dev_err(&client->dev, - "failed to retrieve checksum for %#08x (len: %d)\n", - start_addr, page_size); + "write failed at %#08x (%d bytes)\n", + st_addr, pg_size); break; } - firmware_checksum = - wdt87xx_calculate_checksum(data, page_size); - - if (device_checksum == firmware_checksum) + error = wdt->chksum_check(client, data, st_addr, + pg_size); + if (error != -EAGAIN) break; - dev_err(&client->dev, - "checksum fail: %d vs %d, retry %d\n", - device_checksum, firmware_checksum, retry); + dev_err(&client->dev, "checksum retry (%d) at 0x%x\n", + retry, st_addr); } if (retry == MAX_RETRIES) { @@ -762,22 +1206,22 @@ static int wdt87xx_write_firmware(struct i2c_client *client, const void *chunk) error = -EIO; goto out_lock_device; } - - start_addr = start_addr + page_size; - data = data + page_size; + len -= pg_size; + st_addr += pg_size; + data += pg_size; } out_lock_device: - err1 = wdt87xx_send_command(client, VND_CMD_SFLCK, 0); + err1 = wdt->send_cmd(client, VND_CMD_SFLCK, 0); if (err1) - dev_err(&client->dev, "lock failed\n"); + dev_err(&client->dev, "failed to lock flash\n"); - mdelay(10); + msleep(20); out_enable_reporting: - err1 = wdt87xx_send_command(client, VND_CMD_START, 0); + err1 = wdt->send_cmd(client, VND_CMD_START, 0); if (err1) - dev_err(&client->dev, "start to report failed\n"); + dev_err(&client->dev, "failed to restart to report\n"); return error ? error : err1; } @@ -795,7 +1239,7 @@ static int wdt87xx_load_chunk(struct i2c_client *client, return -EINVAL; } - error = wdt87xx_validate_fw_chunk(chunk, ck_id); + error = wdt87xx_validate_fw_chunk(client, chunk, ck_id); if (error) { dev_err(&client->dev, "invalid chunk (type %d): %d\n", ck_id, error); @@ -813,6 +1257,70 @@ static int wdt87xx_load_chunk(struct i2c_client *client, return 0; } +static int wdt87xx_get_param(struct wdt87xx_data *wdt) +{ + u8 buf[PKT_READ_SIZE]; + int error; + struct i2c_client *client = wdt->client; + struct wdt87xx_param *param = &wdt->param; + u16 param_key; + + buf[CMD_REPORT_ID_OFFSET] = VND_REQ_CTRLER_INFO; + error = wdt87xx_get_feature(client, buf, PACKET_SIZE); + if (error) + dev_err(&client->dev, "failed to get i2c cfg\n"); + + param_key = get_unaligned_le16(buf + W8752_PARAM_KEY_OFFSET); + if (buf[CMD_REPORT_ID_OFFSET] == VND_REQ_CTRLER_INFO && + param_key == W8752_PARAM_KEY) { + param->plat_id = buf[W8752_PLAT_ID_OFFSET]; + wdt87xx_parse_param(wdt, buf + W8752_PARAM_OFFSET, + get_unaligned_le16(buf + + W8752_PARAM_LEN_OFFSET)); + wdt->plt_id = PLT_WDT8752; + wdt->send_cmd = wdt8752_send_command; + wdt->write_flash = wdt8752_write_data; + wdt->delay = wdt8752_delay; + wdt->chksum_check = wdt8752_checksum_check; + error = wdt87xx_get_param_hid(wdt); + } else { + wdt->send_cmd = wdt87xx_send_command; + wdt->write_flash = wdt87xx_write_data; + wdt->delay = wdt87xx_delay; + wdt->chksum_check = wdt87xx_checksum_check; + error = wdt87xx_get_param_private(wdt); + } + + if (error < 0) + return error; + + dev_info(&client->dev, "pid: %04x, vid: %04x, w: %d, h: %d, i_sz: %d\n", + param->vendor_id, param->product_id, param->phy_w, + param->phy_h, wdt->hid_desc.max_input_length); + + buf[CMD_REPORT_ID_OFFSET] = VND_REQ_FW_INFO; + error = wdt87xx_get_feature(client, buf, 16); + if (error) { + dev_err(&client->dev, "failed to get firmware id\n"); + return error; + } + + if (buf[CMD_REPORT_ID_OFFSET] != VND_REQ_FW_INFO) { + dev_err(&client->dev, "wrong id of fw response: 0x%x\n", + buf[CMD_REPORT_ID_OFFSET]); + return -EINVAL; + } + + param->fw_id = get_unaligned_le16(&buf[1]); + + dev_info(&client->dev, + "fw_id: 0x%x, i2c_cfg: 0x%x, xml_id1: %04x, xml_id2: %04x\n", + param->fw_id, param->i2c_cfg, + param->xmls_id1, param->xmls_id2); + + return 0; +} + static int wdt87xx_do_update_firmware(struct i2c_client *client, const struct firmware *fw, unsigned int chunk_id) @@ -845,10 +1353,10 @@ static int wdt87xx_do_update_firmware(struct i2c_client *client, } /* Refresh the parameters */ - error = wdt87xx_get_sysparam(client, &wdt->param); + error = wdt87xx_get_param(wdt); if (error) dev_err(&client->dev, - "failed to refresh system parameters: %d\n", error); + "failed to refresh parameters: %d\n", error); out: enable_irq(client->irq); mutex_unlock(&wdt->fw_mutex); @@ -856,8 +1364,8 @@ out: return error ? error : 0; } -static int wdt87xx_update_firmware(struct device *dev, - const char *fw_name, unsigned int chunk_id) +static int wdt87xx_update_firmware(struct device *dev, const char *fw_name, + unsigned int chunk_id) { struct i2c_client *client = to_i2c_client(dev); const struct firmware *fw; @@ -949,30 +1457,29 @@ static const struct attribute_group wdt87xx_attr_group = { .attrs = wdt87xx_attrs, }; -static void wdt87xx_report_contact(struct input_dev *input, - struct wdt87xx_sys_param *param, - u8 *buf) +static void wdt87xx_report_contact(struct wdt87xx_data *wdt, + struct wdt87xx_param *param, u8 *buf) { + struct input_dev *input = wdt->input; int finger_id; u32 x, y, w; u8 p; - finger_id = (buf[FINGER_EV_V1_OFFSET_ID] >> 3) - 1; + finger_id = (buf[FINGER_EV_OFFSET_ID] >> 3) - 1; if (finger_id < 0) return; - /* Check if this is an active contact */ - if (!(buf[FINGER_EV_V1_OFFSET_ID] & 0x1)) + if (!(buf[FINGER_EV_OFFSET_ID] & 0x1)) return; - w = buf[FINGER_EV_V1_OFFSET_W]; + w = buf[FINGER_EV_OFFSET_W]; w *= param->scaling_factor; - p = buf[FINGER_EV_V1_OFFSET_P]; + p = buf[FINGER_EV_OFFSET_P]; - x = get_unaligned_le16(buf + FINGER_EV_V1_OFFSET_X); + x = get_unaligned_le16(buf + FINGER_EV_OFFSET_X); - y = get_unaligned_le16(buf + FINGER_EV_V1_OFFSET_Y); + y = get_unaligned_le16(buf + FINGER_EV_OFFSET_Y); y = DIV_ROUND_CLOSEST(y * param->phy_h, param->phy_w); /* Refuse incorrect coordinates */ @@ -996,23 +1503,30 @@ static irqreturn_t wdt87xx_ts_interrupt(int irq, void *dev_id) struct i2c_client *client = wdt->client; int i, fingers; int error; - u8 raw_buf[WDT_V1_RAW_BUF_COUNT] = {0}; + int offset = 0; + u8 raw_buf[WDT_RAW_BUF_COUNT] = {0}; + + if (wdt->hid_desc.max_input_length) { + offset = 2; + error = i2c_master_recv(client, raw_buf, + wdt->hid_desc.max_input_length); + } else { + error = i2c_master_recv(client, raw_buf, WDT_RAW_BUF_COUNT); + } - error = i2c_master_recv(client, raw_buf, WDT_V1_RAW_BUF_COUNT); if (error < 0) { - dev_err(&client->dev, "read v1 raw data failed: %d\n", error); + dev_err(&client->dev, "read raw data failed: %d\n", error); goto irq_exit; } - fingers = raw_buf[TOUCH_PK_V1_OFFSET_FNGR_NUM]; + fingers = raw_buf[offset + TOUCH_PK_OFFSET_FNGR_NUM]; if (!fingers) goto irq_exit; for (i = 0; i < WDT_MAX_FINGER; i++) - wdt87xx_report_contact(wdt->input, - &wdt->param, - &raw_buf[TOUCH_PK_V1_OFFSET_EVENT + - i * FINGER_EV_V1_SIZE]); + wdt87xx_report_contact(wdt, &wdt->param, + &raw_buf[offset + TOUCH_PK_OFFSET_EVENT + + i * FINGER_EV_SIZE]); input_mt_sync_frame(wdt->input); input_sync(wdt->input); @@ -1029,6 +1543,7 @@ static int wdt87xx_ts_create_input_device(struct wdt87xx_data *wdt) int error; input = devm_input_allocate_device(dev); + if (!input) { dev_err(dev, "failed to allocate input device\n"); return -ENOMEM; @@ -1088,7 +1603,7 @@ static int wdt87xx_ts_probe(struct i2c_client *client, snprintf(wdt->phys, sizeof(wdt->phys), "i2c-%u-%04x/input0", client->adapter->nr, client->addr); - error = wdt87xx_get_sysparam(client, &wdt->param); + error = wdt87xx_get_param(wdt); if (error) return error; @@ -1124,16 +1639,16 @@ static int wdt87xx_ts_remove(struct i2c_client *client) static int __maybe_unused wdt87xx_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); + struct wdt87xx_data *wdt = i2c_get_clientdata(client); int error; disable_irq(client->irq); - error = wdt87xx_send_command(client, VND_CMD_STOP, MODE_IDLE); + error = wdt->send_cmd(client, VND_CMD_STOP, MODE_IDLE); if (error) { enable_irq(client->irq); dev_err(&client->dev, - "failed to stop device when suspending: %d\n", - error); + "failed to stop device when suspending: %d\n", error); return error; } @@ -1143,19 +1658,19 @@ static int __maybe_unused wdt87xx_suspend(struct device *dev) static int __maybe_unused wdt87xx_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); + struct wdt87xx_data *wdt = i2c_get_clientdata(client); int error; /* * The chip may have been reset while system is resuming, * give it some time to settle. */ - mdelay(100); + mdelay(250); - error = wdt87xx_send_command(client, VND_CMD_START, 0); + error = wdt->send_cmd(client, VND_CMD_START, 0); if (error) dev_err(&client->dev, - "failed to start device when resuming: %d\n", - error); + "failed to start device when resuming: %d\n", error); enable_irq(client->irq); @@ -1168,6 +1683,7 @@ static const struct i2c_device_id wdt87xx_dev_id[] = { { WDT87XX_NAME, 0 }, { } }; + MODULE_DEVICE_TABLE(i2c, wdt87xx_dev_id); static const struct acpi_device_id wdt87xx_acpi_id[] = {