@@ -22,10 +22,15 @@
#include <linux/acpi.h>
#include <asm/unaligned.h>
-#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 WDT87XX_NAME "wdt87xx_i2c"
+#define WDT87XX_DRV_VER "0.9.9"
+#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,12 +155,85 @@
#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_FLASH_ERASE_DELAY_MS 200
-#define WDT_FW_RESET_TIME 2500
+#define WDT_CMD_DELAY_US 300
+#define W8752_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 W8755_DM_SLEEP 0x3
+#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;
@@ -174,17 +247,31 @@ 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;
+ bool wake_irq_enabled;
+
+ /* 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)
@@ -214,6 +301,8 @@ static int wdt87xx_i2c_xfer(struct i2c_client *client,
return error;
}
+ udelay(WDT_CMD_DELAY_US);
+
return 0;
}
@@ -225,8 +314,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;
@@ -238,8 +327,6 @@ static int wdt87xx_get_desc(struct i2c_client *client, u8 desc_idx,
return -EINVAL;
}
- mdelay(WDT_COMMAND_DELAY_MS);
-
return 0;
}
@@ -270,23 +357,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;
@@ -307,16 +394,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;
@@ -335,22 +420,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;
}
@@ -395,20 +479,389 @@ 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);
+
+ /* Remap the value of mode to the right one */
+ if (value != MODE_ACTIVE)
+ value--;
+
+ 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;
}
@@ -426,32 +879,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);
@@ -460,6 +897,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);
@@ -467,34 +907,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;
}
@@ -548,8 +1015,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;
@@ -562,37 +1036,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);
@@ -606,61 +1080,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;
+static int wdt87xx_checksum_check(struct i2c_client *client, const char *data,
+ u32 addr, size_t len)
- 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]);
-
- 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));
@@ -680,82 +1123,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(WDT_FLASH_ERASE_DELAY_MS);
-
- error = wdt87xx_write_data(client, data, start_addr,
- page_size);
+ error = wdt->delay(client, W8752_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) {
@@ -763,22 +1205,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;
}
@@ -796,7 +1238,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);
@@ -814,6 +1256,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)
@@ -846,10 +1352,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);
@@ -857,8 +1363,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;
@@ -950,30 +1456,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 */
@@ -997,23 +1502,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);
@@ -1030,6 +1542,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;
@@ -1089,7 +1602,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;
@@ -1106,6 +1619,12 @@ static int wdt87xx_ts_probe(struct i2c_client *client,
return error;
}
+ error = device_init_wakeup(&client->dev, true);
+ if (error) {
+ dev_err(&client->dev, "inii wakeup failed: %d\n", error);
+ return error;
+ }
+
error = sysfs_create_group(&client->dev.kobj, &wdt87xx_attr_group);
if (error) {
dev_err(&client->dev, "create sysfs failed: %d\n", error);
@@ -1125,16 +1644,22 @@ 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;
+ int mode = MODE_IDLE;
disable_irq(client->irq);
- error = wdt87xx_send_command(client, VND_CMD_STOP, MODE_IDLE);
+ if (device_may_wakeup(dev))
+ wdt->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+ else if (wdt->plt_id == PLT_WDT8752)
+ mode = MODE_SLEEP;
+
+ error = wdt->send_cmd(client, VND_CMD_STOP, mode);
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;
}
@@ -1144,19 +1669,28 @@ 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);
+ u8 raw_buf[3] = {0};
int error;
+ if (device_may_wakeup(dev)) {
+ if (wdt->wake_irq_enabled)
+ disable_irq_wake(client->irq);
+ } else {
+ /* WDT8752 should wakeup device by read operation first */
+ if (wdt->plt_id == PLT_WDT8752)
+ i2c_master_recv(client, raw_buf, 3);
+ }
/*
* The chip may have been reset while system is resuming,
* give it some time to settle.
*/
mdelay(100);
- 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);
@@ -1169,6 +1703,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[] = {