xref: /openbmc/linux/drivers/input/mouse/cyapa.c (revision f68a95cd)
1d7e34d12SBenson Leung /*
2d7e34d12SBenson Leung  * Cypress APA trackpad with I2C interface
3d7e34d12SBenson Leung  *
4d7e34d12SBenson Leung  * Author: Dudley Du <dudl@cypress.com>
5d7e34d12SBenson Leung  * Further cleanup and restructuring by:
6d7e34d12SBenson Leung  *   Daniel Kurtz <djkurtz@chromium.org>
7d7e34d12SBenson Leung  *   Benson Leung <bleung@chromium.org>
8d7e34d12SBenson Leung  *
9d7e34d12SBenson Leung  * Copyright (C) 2011-2012 Cypress Semiconductor, Inc.
10d7e34d12SBenson Leung  * Copyright (C) 2011-2012 Google, Inc.
11d7e34d12SBenson Leung  *
12d7e34d12SBenson Leung  * This file is subject to the terms and conditions of the GNU General Public
13d7e34d12SBenson Leung  * License.  See the file COPYING in the main directory of this archive for
14d7e34d12SBenson Leung  * more details.
15d7e34d12SBenson Leung  */
16d7e34d12SBenson Leung 
17d7e34d12SBenson Leung #include <linux/delay.h>
18d7e34d12SBenson Leung #include <linux/i2c.h>
19d7e34d12SBenson Leung #include <linux/input.h>
20d7e34d12SBenson Leung #include <linux/input/mt.h>
21d7e34d12SBenson Leung #include <linux/interrupt.h>
22d7e34d12SBenson Leung #include <linux/module.h>
23d7e34d12SBenson Leung #include <linux/slab.h>
24d7e34d12SBenson Leung 
25d7e34d12SBenson Leung /* APA trackpad firmware generation */
26d7e34d12SBenson Leung #define CYAPA_GEN3   0x03   /* support MT-protocol B with tracking ID. */
27d7e34d12SBenson Leung 
28d7e34d12SBenson Leung #define CYAPA_NAME   "Cypress APA Trackpad (cyapa)"
29d7e34d12SBenson Leung 
30d7e34d12SBenson Leung /* commands for read/write registers of Cypress trackpad */
31d7e34d12SBenson Leung #define CYAPA_CMD_SOFT_RESET       0x00
32d7e34d12SBenson Leung #define CYAPA_CMD_POWER_MODE       0x01
33d7e34d12SBenson Leung #define CYAPA_CMD_DEV_STATUS       0x02
34d7e34d12SBenson Leung #define CYAPA_CMD_GROUP_DATA       0x03
35d7e34d12SBenson Leung #define CYAPA_CMD_GROUP_CMD        0x04
36d7e34d12SBenson Leung #define CYAPA_CMD_GROUP_QUERY      0x05
37d7e34d12SBenson Leung #define CYAPA_CMD_BL_STATUS        0x06
38d7e34d12SBenson Leung #define CYAPA_CMD_BL_HEAD          0x07
39d7e34d12SBenson Leung #define CYAPA_CMD_BL_CMD           0x08
40d7e34d12SBenson Leung #define CYAPA_CMD_BL_DATA          0x09
41d7e34d12SBenson Leung #define CYAPA_CMD_BL_ALL           0x0a
42d7e34d12SBenson Leung #define CYAPA_CMD_BLK_PRODUCT_ID   0x0b
43d7e34d12SBenson Leung #define CYAPA_CMD_BLK_HEAD         0x0c
44d7e34d12SBenson Leung 
45d7e34d12SBenson Leung /* report data start reg offset address. */
46d7e34d12SBenson Leung #define DATA_REG_START_OFFSET  0x0000
47d7e34d12SBenson Leung 
48d7e34d12SBenson Leung #define BL_HEAD_OFFSET 0x00
49d7e34d12SBenson Leung #define BL_DATA_OFFSET 0x10
50d7e34d12SBenson Leung 
51d7e34d12SBenson Leung /*
52d7e34d12SBenson Leung  * Operational Device Status Register
53d7e34d12SBenson Leung  *
54d7e34d12SBenson Leung  * bit 7: Valid interrupt source
55d7e34d12SBenson Leung  * bit 6 - 4: Reserved
56d7e34d12SBenson Leung  * bit 3 - 2: Power status
57d7e34d12SBenson Leung  * bit 1 - 0: Device status
58d7e34d12SBenson Leung  */
59d7e34d12SBenson Leung #define REG_OP_STATUS     0x00
60d7e34d12SBenson Leung #define OP_STATUS_SRC     0x80
61d7e34d12SBenson Leung #define OP_STATUS_POWER   0x0c
62d7e34d12SBenson Leung #define OP_STATUS_DEV     0x03
63d7e34d12SBenson Leung #define OP_STATUS_MASK (OP_STATUS_SRC | OP_STATUS_POWER | OP_STATUS_DEV)
64d7e34d12SBenson Leung 
65d7e34d12SBenson Leung /*
66d7e34d12SBenson Leung  * Operational Finger Count/Button Flags Register
67d7e34d12SBenson Leung  *
68d7e34d12SBenson Leung  * bit 7 - 4: Number of touched finger
69d7e34d12SBenson Leung  * bit 3: Valid data
70d7e34d12SBenson Leung  * bit 2: Middle Physical Button
71d7e34d12SBenson Leung  * bit 1: Right Physical Button
72d7e34d12SBenson Leung  * bit 0: Left physical Button
73d7e34d12SBenson Leung  */
74d7e34d12SBenson Leung #define REG_OP_DATA1       0x01
75d7e34d12SBenson Leung #define OP_DATA_VALID      0x08
76d7e34d12SBenson Leung #define OP_DATA_MIDDLE_BTN 0x04
77d7e34d12SBenson Leung #define OP_DATA_RIGHT_BTN  0x02
78d7e34d12SBenson Leung #define OP_DATA_LEFT_BTN   0x01
79d7e34d12SBenson Leung #define OP_DATA_BTN_MASK (OP_DATA_MIDDLE_BTN | OP_DATA_RIGHT_BTN | \
80d7e34d12SBenson Leung 			  OP_DATA_LEFT_BTN)
81d7e34d12SBenson Leung 
82d7e34d12SBenson Leung /*
83d7e34d12SBenson Leung  * Bootloader Status Register
84d7e34d12SBenson Leung  *
85d7e34d12SBenson Leung  * bit 7: Busy
86d7e34d12SBenson Leung  * bit 6 - 5: Reserved
87d7e34d12SBenson Leung  * bit 4: Bootloader running
88d7e34d12SBenson Leung  * bit 3 - 1: Reserved
89d7e34d12SBenson Leung  * bit 0: Checksum valid
90d7e34d12SBenson Leung  */
91d7e34d12SBenson Leung #define REG_BL_STATUS        0x01
92d7e34d12SBenson Leung #define BL_STATUS_BUSY       0x80
93d7e34d12SBenson Leung #define BL_STATUS_RUNNING    0x10
94d7e34d12SBenson Leung #define BL_STATUS_DATA_VALID 0x08
95d7e34d12SBenson Leung #define BL_STATUS_CSUM_VALID 0x01
96d7e34d12SBenson Leung 
97d7e34d12SBenson Leung /*
98d7e34d12SBenson Leung  * Bootloader Error Register
99d7e34d12SBenson Leung  *
100d7e34d12SBenson Leung  * bit 7: Invalid
101d7e34d12SBenson Leung  * bit 6: Invalid security key
102d7e34d12SBenson Leung  * bit 5: Bootloading
103d7e34d12SBenson Leung  * bit 4: Command checksum
104d7e34d12SBenson Leung  * bit 3: Flash protection error
105d7e34d12SBenson Leung  * bit 2: Flash checksum error
106d7e34d12SBenson Leung  * bit 1 - 0: Reserved
107d7e34d12SBenson Leung  */
108d7e34d12SBenson Leung #define REG_BL_ERROR         0x02
109d7e34d12SBenson Leung #define BL_ERROR_INVALID     0x80
110d7e34d12SBenson Leung #define BL_ERROR_INVALID_KEY 0x40
111d7e34d12SBenson Leung #define BL_ERROR_BOOTLOADING 0x20
112d7e34d12SBenson Leung #define BL_ERROR_CMD_CSUM    0x10
113d7e34d12SBenson Leung #define BL_ERROR_FLASH_PROT  0x08
114d7e34d12SBenson Leung #define BL_ERROR_FLASH_CSUM  0x04
115d7e34d12SBenson Leung 
116d7e34d12SBenson Leung #define BL_STATUS_SIZE  3  /* length of bootloader status registers */
117d7e34d12SBenson Leung #define BLK_HEAD_BYTES 32
118d7e34d12SBenson Leung 
119d7e34d12SBenson Leung #define PRODUCT_ID_SIZE  16
120d7e34d12SBenson Leung #define QUERY_DATA_SIZE  27
121d7e34d12SBenson Leung #define REG_PROTOCOL_GEN_QUERY_OFFSET  20
122d7e34d12SBenson Leung 
123d7e34d12SBenson Leung #define REG_OFFSET_DATA_BASE     0x0000
124d7e34d12SBenson Leung #define REG_OFFSET_COMMAND_BASE  0x0028
125d7e34d12SBenson Leung #define REG_OFFSET_QUERY_BASE    0x002a
126d7e34d12SBenson Leung 
127d7e34d12SBenson Leung #define CAPABILITY_LEFT_BTN_MASK	(0x01 << 3)
128d7e34d12SBenson Leung #define CAPABILITY_RIGHT_BTN_MASK	(0x01 << 4)
129d7e34d12SBenson Leung #define CAPABILITY_MIDDLE_BTN_MASK	(0x01 << 5)
130d7e34d12SBenson Leung #define CAPABILITY_BTN_MASK  (CAPABILITY_LEFT_BTN_MASK | \
131d7e34d12SBenson Leung 			      CAPABILITY_RIGHT_BTN_MASK | \
132d7e34d12SBenson Leung 			      CAPABILITY_MIDDLE_BTN_MASK)
133d7e34d12SBenson Leung 
134d7e34d12SBenson Leung #define CYAPA_OFFSET_SOFT_RESET  REG_OFFSET_COMMAND_BASE
135d7e34d12SBenson Leung 
136d7e34d12SBenson Leung #define REG_OFFSET_POWER_MODE (REG_OFFSET_COMMAND_BASE + 1)
137d7e34d12SBenson Leung 
138d7e34d12SBenson Leung #define PWR_MODE_MASK   0xfc
139d7e34d12SBenson Leung #define PWR_MODE_FULL_ACTIVE (0x3f << 2)
140d7e34d12SBenson Leung #define PWR_MODE_IDLE        (0x05 << 2) /* default sleep time is 50 ms. */
141d7e34d12SBenson Leung #define PWR_MODE_OFF         (0x00 << 2)
142d7e34d12SBenson Leung 
143d7e34d12SBenson Leung #define PWR_STATUS_MASK      0x0c
144d7e34d12SBenson Leung #define PWR_STATUS_ACTIVE    (0x03 << 2)
145d7e34d12SBenson Leung #define PWR_STATUS_IDLE      (0x02 << 2)
146d7e34d12SBenson Leung #define PWR_STATUS_OFF       (0x00 << 2)
147d7e34d12SBenson Leung 
148d7e34d12SBenson Leung /*
149d7e34d12SBenson Leung  * CYAPA trackpad device states.
150d7e34d12SBenson Leung  * Used in register 0x00, bit1-0, DeviceStatus field.
151d7e34d12SBenson Leung  * Other values indicate device is in an abnormal state and must be reset.
152d7e34d12SBenson Leung  */
153d7e34d12SBenson Leung #define CYAPA_DEV_NORMAL  0x03
154d7e34d12SBenson Leung #define CYAPA_DEV_BUSY    0x01
155d7e34d12SBenson Leung 
156d7e34d12SBenson Leung enum cyapa_state {
157d7e34d12SBenson Leung 	CYAPA_STATE_OP,
158d7e34d12SBenson Leung 	CYAPA_STATE_BL_IDLE,
159d7e34d12SBenson Leung 	CYAPA_STATE_BL_ACTIVE,
160d7e34d12SBenson Leung 	CYAPA_STATE_BL_BUSY,
161d7e34d12SBenson Leung 	CYAPA_STATE_NO_DEVICE,
162d7e34d12SBenson Leung };
163d7e34d12SBenson Leung 
164d7e34d12SBenson Leung 
165d7e34d12SBenson Leung struct cyapa_touch {
166d7e34d12SBenson Leung 	/*
167d7e34d12SBenson Leung 	 * high bits or x/y position value
168d7e34d12SBenson Leung 	 * bit 7 - 4: high 4 bits of x position value
169d7e34d12SBenson Leung 	 * bit 3 - 0: high 4 bits of y position value
170d7e34d12SBenson Leung 	 */
171d7e34d12SBenson Leung 	u8 xy_hi;
172d7e34d12SBenson Leung 	u8 x_lo;  /* low 8 bits of x position value. */
173d7e34d12SBenson Leung 	u8 y_lo;  /* low 8 bits of y position value. */
174d7e34d12SBenson Leung 	u8 pressure;
175d7e34d12SBenson Leung 	/* id range is 1 - 15.  It is incremented with every new touch. */
176d7e34d12SBenson Leung 	u8 id;
177d7e34d12SBenson Leung } __packed;
178d7e34d12SBenson Leung 
179d7e34d12SBenson Leung /* The touch.id is used as the MT slot id, thus max MT slot is 15 */
180d7e34d12SBenson Leung #define CYAPA_MAX_MT_SLOTS  15
181d7e34d12SBenson Leung 
182d7e34d12SBenson Leung struct cyapa_reg_data {
183d7e34d12SBenson Leung 	/*
184d7e34d12SBenson Leung 	 * bit 0 - 1: device status
185d7e34d12SBenson Leung 	 * bit 3 - 2: power mode
186d7e34d12SBenson Leung 	 * bit 6 - 4: reserved
187d7e34d12SBenson Leung 	 * bit 7: interrupt valid bit
188d7e34d12SBenson Leung 	 */
189d7e34d12SBenson Leung 	u8 device_status;
190d7e34d12SBenson Leung 	/*
191d7e34d12SBenson Leung 	 * bit 7 - 4: number of fingers currently touching pad
192d7e34d12SBenson Leung 	 * bit 3: valid data check bit
193d7e34d12SBenson Leung 	 * bit 2: middle mechanism button state if exists
194d7e34d12SBenson Leung 	 * bit 1: right mechanism button state if exists
195d7e34d12SBenson Leung 	 * bit 0: left mechanism button state if exists
196d7e34d12SBenson Leung 	 */
197d7e34d12SBenson Leung 	u8 finger_btn;
198d7e34d12SBenson Leung 	/* CYAPA reports up to 5 touches per packet. */
199d7e34d12SBenson Leung 	struct cyapa_touch touches[5];
200d7e34d12SBenson Leung } __packed;
201d7e34d12SBenson Leung 
202d7e34d12SBenson Leung /* The main device structure */
203d7e34d12SBenson Leung struct cyapa {
204d7e34d12SBenson Leung 	enum cyapa_state state;
205d7e34d12SBenson Leung 
206d7e34d12SBenson Leung 	struct i2c_client *client;
207d7e34d12SBenson Leung 	struct input_dev *input;
208d7e34d12SBenson Leung 	char phys[32];	/* device physical location */
209d7e34d12SBenson Leung 	bool irq_wake;  /* irq wake is enabled */
2106ddaf744SBenson Leung 	bool smbus;
211d7e34d12SBenson Leung 
212d7e34d12SBenson Leung 	/* read from query data region. */
213d7e34d12SBenson Leung 	char product_id[16];
214d7e34d12SBenson Leung 	u8 btn_capability;
215d7e34d12SBenson Leung 	u8 gen;
216d7e34d12SBenson Leung 	int max_abs_x;
217d7e34d12SBenson Leung 	int max_abs_y;
218d7e34d12SBenson Leung 	int physical_size_x;
219d7e34d12SBenson Leung 	int physical_size_y;
220d7e34d12SBenson Leung };
221d7e34d12SBenson Leung 
222d7e34d12SBenson Leung static const u8 bl_deactivate[] = { 0x00, 0xff, 0x3b, 0x00, 0x01, 0x02, 0x03,
223d7e34d12SBenson Leung 		0x04, 0x05, 0x06, 0x07 };
224d7e34d12SBenson Leung static const u8 bl_exit[] = { 0x00, 0xff, 0xa5, 0x00, 0x01, 0x02, 0x03, 0x04,
225d7e34d12SBenson Leung 		0x05, 0x06, 0x07 };
226d7e34d12SBenson Leung 
227d7e34d12SBenson Leung struct cyapa_cmd_len {
228d7e34d12SBenson Leung 	u8 cmd;
229d7e34d12SBenson Leung 	u8 len;
230d7e34d12SBenson Leung };
231d7e34d12SBenson Leung 
2326ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_NONE   0
2336ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_I2C    1
2346ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_SMBUS  2
2356ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_BOTH   3
2366ddaf744SBenson Leung 
2376ddaf744SBenson Leung /*
2386ddaf744SBenson Leung  * macros for SMBus communication
2396ddaf744SBenson Leung  */
2406ddaf744SBenson Leung #define SMBUS_READ   0x01
2416ddaf744SBenson Leung #define SMBUS_WRITE 0x00
2426ddaf744SBenson Leung #define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1))
2436ddaf744SBenson Leung #define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01))
2446ddaf744SBenson Leung #define SMBUS_BYTE_BLOCK_CMD_MASK 0x80
2456ddaf744SBenson Leung #define SMBUS_GROUP_BLOCK_CMD_MASK 0x40
2466ddaf744SBenson Leung 
2476ddaf744SBenson Leung  /* for byte read/write command */
2486ddaf744SBenson Leung #define CMD_RESET 0
2496ddaf744SBenson Leung #define CMD_POWER_MODE 1
2506ddaf744SBenson Leung #define CMD_DEV_STATUS 2
2516ddaf744SBenson Leung #define SMBUS_BYTE_CMD(cmd) (((cmd) & 0x3f) << 1)
2526ddaf744SBenson Leung #define CYAPA_SMBUS_RESET SMBUS_BYTE_CMD(CMD_RESET)
2536ddaf744SBenson Leung #define CYAPA_SMBUS_POWER_MODE SMBUS_BYTE_CMD(CMD_POWER_MODE)
2546ddaf744SBenson Leung #define CYAPA_SMBUS_DEV_STATUS SMBUS_BYTE_CMD(CMD_DEV_STATUS)
2556ddaf744SBenson Leung 
2566ddaf744SBenson Leung  /* for group registers read/write command */
2576ddaf744SBenson Leung #define REG_GROUP_DATA 0
2586ddaf744SBenson Leung #define REG_GROUP_CMD 2
2596ddaf744SBenson Leung #define REG_GROUP_QUERY 3
2606ddaf744SBenson Leung #define SMBUS_GROUP_CMD(grp) (0x80 | (((grp) & 0x07) << 3))
2616ddaf744SBenson Leung #define CYAPA_SMBUS_GROUP_DATA SMBUS_GROUP_CMD(REG_GROUP_DATA)
2626ddaf744SBenson Leung #define CYAPA_SMBUS_GROUP_CMD SMBUS_GROUP_CMD(REG_GROUP_CMD)
2636ddaf744SBenson Leung #define CYAPA_SMBUS_GROUP_QUERY SMBUS_GROUP_CMD(REG_GROUP_QUERY)
2646ddaf744SBenson Leung 
2656ddaf744SBenson Leung  /* for register block read/write command */
2666ddaf744SBenson Leung #define CMD_BL_STATUS 0
2676ddaf744SBenson Leung #define CMD_BL_HEAD 1
2686ddaf744SBenson Leung #define CMD_BL_CMD 2
2696ddaf744SBenson Leung #define CMD_BL_DATA 3
2706ddaf744SBenson Leung #define CMD_BL_ALL 4
2716ddaf744SBenson Leung #define CMD_BLK_PRODUCT_ID 5
2726ddaf744SBenson Leung #define CMD_BLK_HEAD 6
2736ddaf744SBenson Leung #define SMBUS_BLOCK_CMD(cmd) (0xc0 | (((cmd) & 0x1f) << 1))
2746ddaf744SBenson Leung 
2756ddaf744SBenson Leung /* register block read/write command in bootloader mode */
2766ddaf744SBenson Leung #define CYAPA_SMBUS_BL_STATUS SMBUS_BLOCK_CMD(CMD_BL_STATUS)
2776ddaf744SBenson Leung #define CYAPA_SMBUS_BL_HEAD SMBUS_BLOCK_CMD(CMD_BL_HEAD)
2786ddaf744SBenson Leung #define CYAPA_SMBUS_BL_CMD SMBUS_BLOCK_CMD(CMD_BL_CMD)
2796ddaf744SBenson Leung #define CYAPA_SMBUS_BL_DATA SMBUS_BLOCK_CMD(CMD_BL_DATA)
2806ddaf744SBenson Leung #define CYAPA_SMBUS_BL_ALL SMBUS_BLOCK_CMD(CMD_BL_ALL)
2816ddaf744SBenson Leung 
2826ddaf744SBenson Leung /* register block read/write command in operational mode */
2836ddaf744SBenson Leung #define CYAPA_SMBUS_BLK_PRODUCT_ID SMBUS_BLOCK_CMD(CMD_BLK_PRODUCT_ID)
2846ddaf744SBenson Leung #define CYAPA_SMBUS_BLK_HEAD SMBUS_BLOCK_CMD(CMD_BLK_HEAD)
2856ddaf744SBenson Leung 
286d7e34d12SBenson Leung static const struct cyapa_cmd_len cyapa_i2c_cmds[] = {
287d7e34d12SBenson Leung 	{ CYAPA_OFFSET_SOFT_RESET, 1 },
288d7e34d12SBenson Leung 	{ REG_OFFSET_COMMAND_BASE + 1, 1 },
289d7e34d12SBenson Leung 	{ REG_OFFSET_DATA_BASE, 1 },
290d7e34d12SBenson Leung 	{ REG_OFFSET_DATA_BASE, sizeof(struct cyapa_reg_data) },
291d7e34d12SBenson Leung 	{ REG_OFFSET_COMMAND_BASE, 0 },
292d7e34d12SBenson Leung 	{ REG_OFFSET_QUERY_BASE, QUERY_DATA_SIZE },
293d7e34d12SBenson Leung 	{ BL_HEAD_OFFSET, 3 },
294d7e34d12SBenson Leung 	{ BL_HEAD_OFFSET, 16 },
295d7e34d12SBenson Leung 	{ BL_HEAD_OFFSET, 16 },
296d7e34d12SBenson Leung 	{ BL_DATA_OFFSET, 16 },
297d7e34d12SBenson Leung 	{ BL_HEAD_OFFSET, 32 },
298d7e34d12SBenson Leung 	{ REG_OFFSET_QUERY_BASE, PRODUCT_ID_SIZE },
299d7e34d12SBenson Leung 	{ REG_OFFSET_DATA_BASE, 32 }
300d7e34d12SBenson Leung };
301d7e34d12SBenson Leung 
3026ddaf744SBenson Leung static const struct cyapa_cmd_len cyapa_smbus_cmds[] = {
3036ddaf744SBenson Leung 	{ CYAPA_SMBUS_RESET, 1 },
3046ddaf744SBenson Leung 	{ CYAPA_SMBUS_POWER_MODE, 1 },
3056ddaf744SBenson Leung 	{ CYAPA_SMBUS_DEV_STATUS, 1 },
3066ddaf744SBenson Leung 	{ CYAPA_SMBUS_GROUP_DATA, sizeof(struct cyapa_reg_data) },
3076ddaf744SBenson Leung 	{ CYAPA_SMBUS_GROUP_CMD, 2 },
3086ddaf744SBenson Leung 	{ CYAPA_SMBUS_GROUP_QUERY, QUERY_DATA_SIZE },
3096ddaf744SBenson Leung 	{ CYAPA_SMBUS_BL_STATUS, 3 },
3106ddaf744SBenson Leung 	{ CYAPA_SMBUS_BL_HEAD, 16 },
3116ddaf744SBenson Leung 	{ CYAPA_SMBUS_BL_CMD, 16 },
3126ddaf744SBenson Leung 	{ CYAPA_SMBUS_BL_DATA, 16 },
3136ddaf744SBenson Leung 	{ CYAPA_SMBUS_BL_ALL, 32 },
3146ddaf744SBenson Leung 	{ CYAPA_SMBUS_BLK_PRODUCT_ID, PRODUCT_ID_SIZE },
3156ddaf744SBenson Leung 	{ CYAPA_SMBUS_BLK_HEAD, 16 },
3166ddaf744SBenson Leung };
3176ddaf744SBenson Leung 
318d7e34d12SBenson Leung static ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len,
319d7e34d12SBenson Leung 					u8 *values)
320d7e34d12SBenson Leung {
321d7e34d12SBenson Leung 	return i2c_smbus_read_i2c_block_data(cyapa->client, reg, len, values);
322d7e34d12SBenson Leung }
323d7e34d12SBenson Leung 
324d7e34d12SBenson Leung static ssize_t cyapa_i2c_reg_write_block(struct cyapa *cyapa, u8 reg,
325d7e34d12SBenson Leung 					 size_t len, const u8 *values)
326d7e34d12SBenson Leung {
327d7e34d12SBenson Leung 	return i2c_smbus_write_i2c_block_data(cyapa->client, reg, len, values);
328d7e34d12SBenson Leung }
329d7e34d12SBenson Leung 
3306ddaf744SBenson Leung /*
3316ddaf744SBenson Leung  * cyapa_smbus_read_block - perform smbus block read command
3326ddaf744SBenson Leung  * @cyapa  - private data structure of the driver
3336ddaf744SBenson Leung  * @cmd    - the properly encoded smbus command
3346ddaf744SBenson Leung  * @len    - expected length of smbus command result
3356ddaf744SBenson Leung  * @values - buffer to store smbus command result
3366ddaf744SBenson Leung  *
3376ddaf744SBenson Leung  * Returns negative errno, else the number of bytes written.
3386ddaf744SBenson Leung  *
3396ddaf744SBenson Leung  * Note:
3406ddaf744SBenson Leung  * In trackpad device, the memory block allocated for I2C register map
3416ddaf744SBenson Leung  * is 256 bytes, so the max read block for I2C bus is 256 bytes.
3426ddaf744SBenson Leung  */
3436ddaf744SBenson Leung static ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len,
3446ddaf744SBenson Leung 				      u8 *values)
3456ddaf744SBenson Leung {
3466ddaf744SBenson Leung 	ssize_t ret;
3476ddaf744SBenson Leung 	u8 index;
3486ddaf744SBenson Leung 	u8 smbus_cmd;
3496ddaf744SBenson Leung 	u8 *buf;
3506ddaf744SBenson Leung 	struct i2c_client *client = cyapa->client;
3516ddaf744SBenson Leung 
3526ddaf744SBenson Leung 	if (!(SMBUS_BYTE_BLOCK_CMD_MASK & cmd))
3536ddaf744SBenson Leung 		return -EINVAL;
3546ddaf744SBenson Leung 
3556ddaf744SBenson Leung 	if (SMBUS_GROUP_BLOCK_CMD_MASK & cmd) {
3566ddaf744SBenson Leung 		/* read specific block registers command. */
3576ddaf744SBenson Leung 		smbus_cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ);
3586ddaf744SBenson Leung 		ret = i2c_smbus_read_block_data(client, smbus_cmd, values);
3596ddaf744SBenson Leung 		goto out;
3606ddaf744SBenson Leung 	}
3616ddaf744SBenson Leung 
3626ddaf744SBenson Leung 	ret = 0;
3636ddaf744SBenson Leung 	for (index = 0; index * I2C_SMBUS_BLOCK_MAX < len; index++) {
3646ddaf744SBenson Leung 		smbus_cmd = SMBUS_ENCODE_IDX(cmd, index);
3656ddaf744SBenson Leung 		smbus_cmd = SMBUS_ENCODE_RW(smbus_cmd, SMBUS_READ);
3666ddaf744SBenson Leung 		buf = values + I2C_SMBUS_BLOCK_MAX * index;
3676ddaf744SBenson Leung 		ret = i2c_smbus_read_block_data(client, smbus_cmd, buf);
3686ddaf744SBenson Leung 		if (ret < 0)
3696ddaf744SBenson Leung 			goto out;
3706ddaf744SBenson Leung 	}
3716ddaf744SBenson Leung 
3726ddaf744SBenson Leung out:
3736ddaf744SBenson Leung 	return ret > 0 ? len : ret;
3746ddaf744SBenson Leung }
3756ddaf744SBenson Leung 
376d7e34d12SBenson Leung static s32 cyapa_read_byte(struct cyapa *cyapa, u8 cmd_idx)
377d7e34d12SBenson Leung {
3786ddaf744SBenson Leung 	u8 cmd;
379d7e34d12SBenson Leung 
3806ddaf744SBenson Leung 	if (cyapa->smbus) {
3816ddaf744SBenson Leung 		cmd = cyapa_smbus_cmds[cmd_idx].cmd;
3826ddaf744SBenson Leung 		cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ);
3836ddaf744SBenson Leung 	} else {
3846ddaf744SBenson Leung 		cmd = cyapa_i2c_cmds[cmd_idx].cmd;
3856ddaf744SBenson Leung 	}
386d7e34d12SBenson Leung 	return i2c_smbus_read_byte_data(cyapa->client, cmd);
387d7e34d12SBenson Leung }
388d7e34d12SBenson Leung 
389d7e34d12SBenson Leung static s32 cyapa_write_byte(struct cyapa *cyapa, u8 cmd_idx, u8 value)
390d7e34d12SBenson Leung {
3916ddaf744SBenson Leung 	u8 cmd;
392d7e34d12SBenson Leung 
3936ddaf744SBenson Leung 	if (cyapa->smbus) {
3946ddaf744SBenson Leung 		cmd = cyapa_smbus_cmds[cmd_idx].cmd;
3956ddaf744SBenson Leung 		cmd = SMBUS_ENCODE_RW(cmd, SMBUS_WRITE);
3966ddaf744SBenson Leung 	} else {
3976ddaf744SBenson Leung 		cmd = cyapa_i2c_cmds[cmd_idx].cmd;
3986ddaf744SBenson Leung 	}
399d7e34d12SBenson Leung 	return i2c_smbus_write_byte_data(cyapa->client, cmd, value);
400d7e34d12SBenson Leung }
401d7e34d12SBenson Leung 
402d7e34d12SBenson Leung static ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values)
403d7e34d12SBenson Leung {
4046ddaf744SBenson Leung 	u8 cmd;
4056ddaf744SBenson Leung 	size_t len;
406d7e34d12SBenson Leung 
4076ddaf744SBenson Leung 	if (cyapa->smbus) {
4086ddaf744SBenson Leung 		cmd = cyapa_smbus_cmds[cmd_idx].cmd;
4096ddaf744SBenson Leung 		len = cyapa_smbus_cmds[cmd_idx].len;
4106ddaf744SBenson Leung 		return cyapa_smbus_read_block(cyapa, cmd, len, values);
4116ddaf744SBenson Leung 	} else {
4126ddaf744SBenson Leung 		cmd = cyapa_i2c_cmds[cmd_idx].cmd;
4136ddaf744SBenson Leung 		len = cyapa_i2c_cmds[cmd_idx].len;
414d7e34d12SBenson Leung 		return cyapa_i2c_reg_read_block(cyapa, cmd, len, values);
415d7e34d12SBenson Leung 	}
4166ddaf744SBenson Leung }
417d7e34d12SBenson Leung 
418d7e34d12SBenson Leung /*
419d7e34d12SBenson Leung  * Query device for its current operating state.
420d7e34d12SBenson Leung  *
421d7e34d12SBenson Leung  */
422d7e34d12SBenson Leung static int cyapa_get_state(struct cyapa *cyapa)
423d7e34d12SBenson Leung {
424d7e34d12SBenson Leung 	int ret;
425d7e34d12SBenson Leung 	u8 status[BL_STATUS_SIZE];
426d7e34d12SBenson Leung 
427d7e34d12SBenson Leung 	cyapa->state = CYAPA_STATE_NO_DEVICE;
428d7e34d12SBenson Leung 
429d7e34d12SBenson Leung 	/*
430d7e34d12SBenson Leung 	 * Get trackpad status by reading 3 registers starting from 0.
431d7e34d12SBenson Leung 	 * If the device is in the bootloader, this will be BL_HEAD.
432d7e34d12SBenson Leung 	 * If the device is in operation mode, this will be the DATA regs.
433d7e34d12SBenson Leung 	 *
434d7e34d12SBenson Leung 	 */
435d7e34d12SBenson Leung 	ret = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, BL_STATUS_SIZE,
436d7e34d12SBenson Leung 				       status);
4376ddaf744SBenson Leung 
4386ddaf744SBenson Leung 	/*
4396ddaf744SBenson Leung 	 * On smbus systems in OP mode, the i2c_reg_read will fail with
4406ddaf744SBenson Leung 	 * -ETIMEDOUT.  In this case, try again using the smbus equivalent
4416ddaf744SBenson Leung 	 * command.  This should return a BL_HEAD indicating CYAPA_STATE_OP.
4426ddaf744SBenson Leung 	 */
4436ddaf744SBenson Leung 	if (cyapa->smbus && (ret == -ETIMEDOUT || ret == -ENXIO))
4446ddaf744SBenson Leung 		ret = cyapa_read_block(cyapa, CYAPA_CMD_BL_STATUS, status);
4456ddaf744SBenson Leung 
446d7e34d12SBenson Leung 	if (ret != BL_STATUS_SIZE)
447d7e34d12SBenson Leung 		goto error;
448d7e34d12SBenson Leung 
449d7e34d12SBenson Leung 	if ((status[REG_OP_STATUS] & OP_STATUS_SRC) == OP_STATUS_SRC) {
450d7e34d12SBenson Leung 		switch (status[REG_OP_STATUS] & OP_STATUS_DEV) {
451d7e34d12SBenson Leung 		case CYAPA_DEV_NORMAL:
452d7e34d12SBenson Leung 		case CYAPA_DEV_BUSY:
453d7e34d12SBenson Leung 			cyapa->state = CYAPA_STATE_OP;
454d7e34d12SBenson Leung 			break;
455d7e34d12SBenson Leung 		default:
456d7e34d12SBenson Leung 			ret = -EAGAIN;
457d7e34d12SBenson Leung 			goto error;
458d7e34d12SBenson Leung 		}
459d7e34d12SBenson Leung 	} else {
460d7e34d12SBenson Leung 		if (status[REG_BL_STATUS] & BL_STATUS_BUSY)
461d7e34d12SBenson Leung 			cyapa->state = CYAPA_STATE_BL_BUSY;
462d7e34d12SBenson Leung 		else if (status[REG_BL_ERROR] & BL_ERROR_BOOTLOADING)
463d7e34d12SBenson Leung 			cyapa->state = CYAPA_STATE_BL_ACTIVE;
464d7e34d12SBenson Leung 		else
465d7e34d12SBenson Leung 			cyapa->state = CYAPA_STATE_BL_IDLE;
466d7e34d12SBenson Leung 	}
467d7e34d12SBenson Leung 
468d7e34d12SBenson Leung 	return 0;
469d7e34d12SBenson Leung error:
470d7e34d12SBenson Leung 	return (ret < 0) ? ret : -EAGAIN;
471d7e34d12SBenson Leung }
472d7e34d12SBenson Leung 
473d7e34d12SBenson Leung /*
474d7e34d12SBenson Leung  * Poll device for its status in a loop, waiting up to timeout for a response.
475d7e34d12SBenson Leung  *
476d7e34d12SBenson Leung  * When the device switches state, it usually takes ~300 ms.
477d7e34d12SBenson Leung  * However, when running a new firmware image, the device must calibrate its
478d7e34d12SBenson Leung  * sensors, which can take as long as 2 seconds.
479d7e34d12SBenson Leung  *
480d7e34d12SBenson Leung  * Note: The timeout has granularity of the polling rate, which is 100 ms.
481d7e34d12SBenson Leung  *
482d7e34d12SBenson Leung  * Returns:
483d7e34d12SBenson Leung  *   0 when the device eventually responds with a valid non-busy state.
484d7e34d12SBenson Leung  *   -ETIMEDOUT if device never responds (too many -EAGAIN)
485d7e34d12SBenson Leung  *   < 0    other errors
486d7e34d12SBenson Leung  */
487d7e34d12SBenson Leung static int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout)
488d7e34d12SBenson Leung {
489d7e34d12SBenson Leung 	int ret;
490d7e34d12SBenson Leung 	int tries = timeout / 100;
491d7e34d12SBenson Leung 
492d7e34d12SBenson Leung 	ret = cyapa_get_state(cyapa);
493d7e34d12SBenson Leung 	while ((ret || cyapa->state >= CYAPA_STATE_BL_BUSY) && tries--) {
494d7e34d12SBenson Leung 		msleep(100);
495d7e34d12SBenson Leung 		ret = cyapa_get_state(cyapa);
496d7e34d12SBenson Leung 	}
497d7e34d12SBenson Leung 	return (ret == -EAGAIN || ret == -ETIMEDOUT) ? -ETIMEDOUT : ret;
498d7e34d12SBenson Leung }
499d7e34d12SBenson Leung 
500d7e34d12SBenson Leung static int cyapa_bl_deactivate(struct cyapa *cyapa)
501d7e34d12SBenson Leung {
502d7e34d12SBenson Leung 	int ret;
503d7e34d12SBenson Leung 
504d7e34d12SBenson Leung 	ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_deactivate),
505d7e34d12SBenson Leung 					bl_deactivate);
506d7e34d12SBenson Leung 	if (ret < 0)
507d7e34d12SBenson Leung 		return ret;
508d7e34d12SBenson Leung 
509d7e34d12SBenson Leung 	/* wait for bootloader to switch to idle state; should take < 100ms */
510d7e34d12SBenson Leung 	msleep(100);
511d7e34d12SBenson Leung 	ret = cyapa_poll_state(cyapa, 500);
512d7e34d12SBenson Leung 	if (ret < 0)
513d7e34d12SBenson Leung 		return ret;
514d7e34d12SBenson Leung 	if (cyapa->state != CYAPA_STATE_BL_IDLE)
515d7e34d12SBenson Leung 		return -EAGAIN;
516d7e34d12SBenson Leung 	return 0;
517d7e34d12SBenson Leung }
518d7e34d12SBenson Leung 
519d7e34d12SBenson Leung /*
520d7e34d12SBenson Leung  * Exit bootloader
521d7e34d12SBenson Leung  *
522d7e34d12SBenson Leung  * Send bl_exit command, then wait 50 - 100 ms to let device transition to
523d7e34d12SBenson Leung  * operational mode.  If this is the first time the device's firmware is
524d7e34d12SBenson Leung  * running, it can take up to 2 seconds to calibrate its sensors.  So, poll
525d7e34d12SBenson Leung  * the device's new state for up to 2 seconds.
526d7e34d12SBenson Leung  *
527d7e34d12SBenson Leung  * Returns:
528d7e34d12SBenson Leung  *   -EIO    failure while reading from device
529d7e34d12SBenson Leung  *   -EAGAIN device is stuck in bootloader, b/c it has invalid firmware
530d7e34d12SBenson Leung  *   0       device is supported and in operational mode
531d7e34d12SBenson Leung  */
532d7e34d12SBenson Leung static int cyapa_bl_exit(struct cyapa *cyapa)
533d7e34d12SBenson Leung {
534d7e34d12SBenson Leung 	int ret;
535d7e34d12SBenson Leung 
536d7e34d12SBenson Leung 	ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_exit), bl_exit);
537d7e34d12SBenson Leung 	if (ret < 0)
538d7e34d12SBenson Leung 		return ret;
539d7e34d12SBenson Leung 
540d7e34d12SBenson Leung 	/*
541d7e34d12SBenson Leung 	 * Wait for bootloader to exit, and operation mode to start.
542d7e34d12SBenson Leung 	 * Normally, this takes at least 50 ms.
543d7e34d12SBenson Leung 	 */
544d7e34d12SBenson Leung 	usleep_range(50000, 100000);
545d7e34d12SBenson Leung 	/*
546d7e34d12SBenson Leung 	 * In addition, when a device boots for the first time after being
547d7e34d12SBenson Leung 	 * updated to new firmware, it must first calibrate its sensors, which
548d7e34d12SBenson Leung 	 * can take up to an additional 2 seconds.
549d7e34d12SBenson Leung 	 */
550d7e34d12SBenson Leung 	ret = cyapa_poll_state(cyapa, 2000);
551d7e34d12SBenson Leung 	if (ret < 0)
552d7e34d12SBenson Leung 		return ret;
553d7e34d12SBenson Leung 	if (cyapa->state != CYAPA_STATE_OP)
554d7e34d12SBenson Leung 		return -EAGAIN;
555d7e34d12SBenson Leung 
556d7e34d12SBenson Leung 	return 0;
557d7e34d12SBenson Leung }
558d7e34d12SBenson Leung 
559d7e34d12SBenson Leung /*
560d7e34d12SBenson Leung  * Set device power mode
561d7e34d12SBenson Leung  *
562d7e34d12SBenson Leung  */
563d7e34d12SBenson Leung static int cyapa_set_power_mode(struct cyapa *cyapa, u8 power_mode)
564d7e34d12SBenson Leung {
565d7e34d12SBenson Leung 	struct device *dev = &cyapa->client->dev;
566d7e34d12SBenson Leung 	int ret;
567d7e34d12SBenson Leung 	u8 power;
568d7e34d12SBenson Leung 
569d7e34d12SBenson Leung 	if (cyapa->state != CYAPA_STATE_OP)
570d7e34d12SBenson Leung 		return 0;
571d7e34d12SBenson Leung 
572d7e34d12SBenson Leung 	ret = cyapa_read_byte(cyapa, CYAPA_CMD_POWER_MODE);
573d7e34d12SBenson Leung 	if (ret < 0)
574d7e34d12SBenson Leung 		return ret;
575d7e34d12SBenson Leung 
576d7e34d12SBenson Leung 	power = ret & ~PWR_MODE_MASK;
577d7e34d12SBenson Leung 	power |= power_mode & PWR_MODE_MASK;
578d7e34d12SBenson Leung 	ret = cyapa_write_byte(cyapa, CYAPA_CMD_POWER_MODE, power);
579b1cfa7b4SDudley Du 	if (ret < 0) {
580d7e34d12SBenson Leung 		dev_err(dev, "failed to set power_mode 0x%02x err = %d\n",
581d7e34d12SBenson Leung 			power_mode, ret);
582d7e34d12SBenson Leung 		return ret;
583d7e34d12SBenson Leung 	}
584d7e34d12SBenson Leung 
585b1cfa7b4SDudley Du 	return 0;
586b1cfa7b4SDudley Du }
587b1cfa7b4SDudley Du 
588d7e34d12SBenson Leung static int cyapa_get_query_data(struct cyapa *cyapa)
589d7e34d12SBenson Leung {
590d7e34d12SBenson Leung 	u8 query_data[QUERY_DATA_SIZE];
591d7e34d12SBenson Leung 	int ret;
592d7e34d12SBenson Leung 
593d7e34d12SBenson Leung 	if (cyapa->state != CYAPA_STATE_OP)
594d7e34d12SBenson Leung 		return -EBUSY;
595d7e34d12SBenson Leung 
596d7e34d12SBenson Leung 	ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_QUERY, query_data);
597d7e34d12SBenson Leung 	if (ret < 0)
598d7e34d12SBenson Leung 		return ret;
599d7e34d12SBenson Leung 	if (ret != QUERY_DATA_SIZE)
600d7e34d12SBenson Leung 		return -EIO;
601d7e34d12SBenson Leung 
602d7e34d12SBenson Leung 	memcpy(&cyapa->product_id[0], &query_data[0], 5);
603d7e34d12SBenson Leung 	cyapa->product_id[5] = '-';
604d7e34d12SBenson Leung 	memcpy(&cyapa->product_id[6], &query_data[5], 6);
605d7e34d12SBenson Leung 	cyapa->product_id[12] = '-';
606d7e34d12SBenson Leung 	memcpy(&cyapa->product_id[13], &query_data[11], 2);
607d7e34d12SBenson Leung 	cyapa->product_id[15] = '\0';
608d7e34d12SBenson Leung 
609d7e34d12SBenson Leung 	cyapa->btn_capability = query_data[19] & CAPABILITY_BTN_MASK;
610d7e34d12SBenson Leung 
611d7e34d12SBenson Leung 	cyapa->gen = query_data[20] & 0x0f;
612d7e34d12SBenson Leung 
613d7e34d12SBenson Leung 	cyapa->max_abs_x = ((query_data[21] & 0xf0) << 4) | query_data[22];
614d7e34d12SBenson Leung 	cyapa->max_abs_y = ((query_data[21] & 0x0f) << 8) | query_data[23];
615d7e34d12SBenson Leung 
616d7e34d12SBenson Leung 	cyapa->physical_size_x =
617d7e34d12SBenson Leung 		((query_data[24] & 0xf0) << 4) | query_data[25];
618d7e34d12SBenson Leung 	cyapa->physical_size_y =
619d7e34d12SBenson Leung 		((query_data[24] & 0x0f) << 8) | query_data[26];
620d7e34d12SBenson Leung 
621d7e34d12SBenson Leung 	return 0;
622d7e34d12SBenson Leung }
623d7e34d12SBenson Leung 
624d7e34d12SBenson Leung /*
625d7e34d12SBenson Leung  * Check if device is operational.
626d7e34d12SBenson Leung  *
627d7e34d12SBenson Leung  * An operational device is responding, has exited bootloader, and has
628d7e34d12SBenson Leung  * firmware supported by this driver.
629d7e34d12SBenson Leung  *
630d7e34d12SBenson Leung  * Returns:
631d7e34d12SBenson Leung  *   -EBUSY  no device or in bootloader
632d7e34d12SBenson Leung  *   -EIO    failure while reading from device
633d7e34d12SBenson Leung  *   -EAGAIN device is still in bootloader
634d7e34d12SBenson Leung  *           if ->state = CYAPA_STATE_BL_IDLE, device has invalid firmware
635d7e34d12SBenson Leung  *   -EINVAL device is in operational mode, but not supported by this driver
636d7e34d12SBenson Leung  *   0       device is supported
637d7e34d12SBenson Leung  */
638d7e34d12SBenson Leung static int cyapa_check_is_operational(struct cyapa *cyapa)
639d7e34d12SBenson Leung {
640d7e34d12SBenson Leung 	struct device *dev = &cyapa->client->dev;
641d7e34d12SBenson Leung 	static const char unique_str[] = "CYTRA";
642d7e34d12SBenson Leung 	int ret;
643d7e34d12SBenson Leung 
644d7e34d12SBenson Leung 	ret = cyapa_poll_state(cyapa, 2000);
645d7e34d12SBenson Leung 	if (ret < 0)
646d7e34d12SBenson Leung 		return ret;
647d7e34d12SBenson Leung 	switch (cyapa->state) {
648d7e34d12SBenson Leung 	case CYAPA_STATE_BL_ACTIVE:
649d7e34d12SBenson Leung 		ret = cyapa_bl_deactivate(cyapa);
650d7e34d12SBenson Leung 		if (ret)
651d7e34d12SBenson Leung 			return ret;
652d7e34d12SBenson Leung 
653d7e34d12SBenson Leung 	/* Fallthrough state */
654d7e34d12SBenson Leung 	case CYAPA_STATE_BL_IDLE:
655d7e34d12SBenson Leung 		ret = cyapa_bl_exit(cyapa);
656d7e34d12SBenson Leung 		if (ret)
657d7e34d12SBenson Leung 			return ret;
658d7e34d12SBenson Leung 
659d7e34d12SBenson Leung 	/* Fallthrough state */
660d7e34d12SBenson Leung 	case CYAPA_STATE_OP:
661d7e34d12SBenson Leung 		ret = cyapa_get_query_data(cyapa);
662d7e34d12SBenson Leung 		if (ret < 0)
663d7e34d12SBenson Leung 			return ret;
664d7e34d12SBenson Leung 
665d7e34d12SBenson Leung 		/* only support firmware protocol gen3 */
666d7e34d12SBenson Leung 		if (cyapa->gen != CYAPA_GEN3) {
667d7e34d12SBenson Leung 			dev_err(dev, "unsupported protocol version (%d)",
668d7e34d12SBenson Leung 				cyapa->gen);
669d7e34d12SBenson Leung 			return -EINVAL;
670d7e34d12SBenson Leung 		}
671d7e34d12SBenson Leung 
672d7e34d12SBenson Leung 		/* only support product ID starting with CYTRA */
673d7e34d12SBenson Leung 		if (memcmp(cyapa->product_id, unique_str,
674d7e34d12SBenson Leung 			   sizeof(unique_str) - 1) != 0) {
675d7e34d12SBenson Leung 			dev_err(dev, "unsupported product ID (%s)\n",
676d7e34d12SBenson Leung 				cyapa->product_id);
677d7e34d12SBenson Leung 			return -EINVAL;
678d7e34d12SBenson Leung 		}
679d7e34d12SBenson Leung 		return 0;
680d7e34d12SBenson Leung 
681d7e34d12SBenson Leung 	default:
682d7e34d12SBenson Leung 		return -EIO;
683d7e34d12SBenson Leung 	}
684d7e34d12SBenson Leung 	return 0;
685d7e34d12SBenson Leung }
686d7e34d12SBenson Leung 
687d7e34d12SBenson Leung static irqreturn_t cyapa_irq(int irq, void *dev_id)
688d7e34d12SBenson Leung {
689d7e34d12SBenson Leung 	struct cyapa *cyapa = dev_id;
690d7e34d12SBenson Leung 	struct device *dev = &cyapa->client->dev;
691d7e34d12SBenson Leung 	struct input_dev *input = cyapa->input;
692d7e34d12SBenson Leung 	struct cyapa_reg_data data;
693d7e34d12SBenson Leung 	int i;
694d7e34d12SBenson Leung 	int ret;
695d7e34d12SBenson Leung 	int num_fingers;
696d7e34d12SBenson Leung 
697d7e34d12SBenson Leung 	if (device_may_wakeup(dev))
698d7e34d12SBenson Leung 		pm_wakeup_event(dev, 0);
699d7e34d12SBenson Leung 
700d7e34d12SBenson Leung 	ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data);
701d7e34d12SBenson Leung 	if (ret != sizeof(data))
702d7e34d12SBenson Leung 		goto out;
703d7e34d12SBenson Leung 
704d7e34d12SBenson Leung 	if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC ||
705d7e34d12SBenson Leung 	    (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL ||
706d7e34d12SBenson Leung 	    (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) {
707d7e34d12SBenson Leung 		goto out;
708d7e34d12SBenson Leung 	}
709d7e34d12SBenson Leung 
710d7e34d12SBenson Leung 	num_fingers = (data.finger_btn >> 4) & 0x0f;
711d7e34d12SBenson Leung 	for (i = 0; i < num_fingers; i++) {
712d7e34d12SBenson Leung 		const struct cyapa_touch *touch = &data.touches[i];
713d7e34d12SBenson Leung 		/* Note: touch->id range is 1 to 15; slots are 0 to 14. */
714d7e34d12SBenson Leung 		int slot = touch->id - 1;
715d7e34d12SBenson Leung 
716d7e34d12SBenson Leung 		input_mt_slot(input, slot);
717d7e34d12SBenson Leung 		input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
718d7e34d12SBenson Leung 		input_report_abs(input, ABS_MT_POSITION_X,
719d7e34d12SBenson Leung 				 ((touch->xy_hi & 0xf0) << 4) | touch->x_lo);
720d7e34d12SBenson Leung 		input_report_abs(input, ABS_MT_POSITION_Y,
721d7e34d12SBenson Leung 				 ((touch->xy_hi & 0x0f) << 8) | touch->y_lo);
722d7e34d12SBenson Leung 		input_report_abs(input, ABS_MT_PRESSURE, touch->pressure);
723d7e34d12SBenson Leung 	}
724d7e34d12SBenson Leung 
725d7e34d12SBenson Leung 	input_mt_sync_frame(input);
726d7e34d12SBenson Leung 
727d7e34d12SBenson Leung 	if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK)
728d7e34d12SBenson Leung 		input_report_key(input, BTN_LEFT,
729d7e34d12SBenson Leung 				 data.finger_btn & OP_DATA_LEFT_BTN);
730d7e34d12SBenson Leung 
731d7e34d12SBenson Leung 	if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK)
732d7e34d12SBenson Leung 		input_report_key(input, BTN_MIDDLE,
733d7e34d12SBenson Leung 				 data.finger_btn & OP_DATA_MIDDLE_BTN);
734d7e34d12SBenson Leung 
735d7e34d12SBenson Leung 	if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK)
736d7e34d12SBenson Leung 		input_report_key(input, BTN_RIGHT,
737d7e34d12SBenson Leung 				 data.finger_btn & OP_DATA_RIGHT_BTN);
738d7e34d12SBenson Leung 
739d7e34d12SBenson Leung 	input_sync(input);
740d7e34d12SBenson Leung 
741d7e34d12SBenson Leung out:
742d7e34d12SBenson Leung 	return IRQ_HANDLED;
743d7e34d12SBenson Leung }
744d7e34d12SBenson Leung 
7456ddaf744SBenson Leung static u8 cyapa_check_adapter_functionality(struct i2c_client *client)
7466ddaf744SBenson Leung {
7476ddaf744SBenson Leung 	u8 ret = CYAPA_ADAPTER_FUNC_NONE;
7486ddaf744SBenson Leung 
7496ddaf744SBenson Leung 	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
7506ddaf744SBenson Leung 		ret |= CYAPA_ADAPTER_FUNC_I2C;
7516ddaf744SBenson Leung 	if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
7526ddaf744SBenson Leung 				     I2C_FUNC_SMBUS_BLOCK_DATA |
7536ddaf744SBenson Leung 				     I2C_FUNC_SMBUS_I2C_BLOCK))
7546ddaf744SBenson Leung 		ret |= CYAPA_ADAPTER_FUNC_SMBUS;
7556ddaf744SBenson Leung 	return ret;
7566ddaf744SBenson Leung }
7576ddaf744SBenson Leung 
758b1cfa7b4SDudley Du static int cyapa_open(struct input_dev *input)
759b1cfa7b4SDudley Du {
760b1cfa7b4SDudley Du 	struct cyapa *cyapa = input_get_drvdata(input);
761b1cfa7b4SDudley Du 	struct i2c_client *client = cyapa->client;
762b1cfa7b4SDudley Du 	int error;
763b1cfa7b4SDudley Du 
764b1cfa7b4SDudley Du 	error = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE);
765b1cfa7b4SDudley Du 	if (error) {
766b1cfa7b4SDudley Du 		dev_err(&client->dev, "set active power failed: %d\n", error);
767b1cfa7b4SDudley Du 		return error;
768b1cfa7b4SDudley Du 	}
769b1cfa7b4SDudley Du 
770b1cfa7b4SDudley Du 	enable_irq(client->irq);
771b1cfa7b4SDudley Du 	return 0;
772b1cfa7b4SDudley Du }
773b1cfa7b4SDudley Du 
774b1cfa7b4SDudley Du static void cyapa_close(struct input_dev *input)
775b1cfa7b4SDudley Du {
776b1cfa7b4SDudley Du 	struct cyapa *cyapa = input_get_drvdata(input);
777b1cfa7b4SDudley Du 
778b1cfa7b4SDudley Du 	disable_irq(cyapa->client->irq);
779b1cfa7b4SDudley Du 	cyapa_set_power_mode(cyapa, PWR_MODE_OFF);
780b1cfa7b4SDudley Du }
781b1cfa7b4SDudley Du 
782d7e34d12SBenson Leung static int cyapa_create_input_dev(struct cyapa *cyapa)
783d7e34d12SBenson Leung {
784d7e34d12SBenson Leung 	struct device *dev = &cyapa->client->dev;
785d7e34d12SBenson Leung 	struct input_dev *input;
786b1cfa7b4SDudley Du 	int error;
787d7e34d12SBenson Leung 
788d7e34d12SBenson Leung 	if (!cyapa->physical_size_x || !cyapa->physical_size_y)
789d7e34d12SBenson Leung 		return -EINVAL;
790d7e34d12SBenson Leung 
791b1cfa7b4SDudley Du 	input = devm_input_allocate_device(dev);
792d7e34d12SBenson Leung 	if (!input) {
793d7e34d12SBenson Leung 		dev_err(dev, "allocate memory for input device failed\n");
794d7e34d12SBenson Leung 		return -ENOMEM;
795d7e34d12SBenson Leung 	}
796d7e34d12SBenson Leung 
797d7e34d12SBenson Leung 	input->name = CYAPA_NAME;
798d7e34d12SBenson Leung 	input->phys = cyapa->phys;
799d7e34d12SBenson Leung 	input->id.bustype = BUS_I2C;
800d7e34d12SBenson Leung 	input->id.version = 1;
801d7e34d12SBenson Leung 	input->id.product = 0;  /* means any product in eventcomm. */
802d7e34d12SBenson Leung 	input->dev.parent = &cyapa->client->dev;
803d7e34d12SBenson Leung 
804b1cfa7b4SDudley Du 	input->open = cyapa_open;
805b1cfa7b4SDudley Du 	input->close = cyapa_close;
806b1cfa7b4SDudley Du 
807d7e34d12SBenson Leung 	input_set_drvdata(input, cyapa);
808d7e34d12SBenson Leung 
809d7e34d12SBenson Leung 	__set_bit(EV_ABS, input->evbit);
810d7e34d12SBenson Leung 
811d7e34d12SBenson Leung 	/* finger position */
812d7e34d12SBenson Leung 	input_set_abs_params(input, ABS_MT_POSITION_X, 0, cyapa->max_abs_x, 0,
813d7e34d12SBenson Leung 			     0);
814d7e34d12SBenson Leung 	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cyapa->max_abs_y, 0,
815d7e34d12SBenson Leung 			     0);
816d7e34d12SBenson Leung 	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
817d7e34d12SBenson Leung 
818d7e34d12SBenson Leung 	input_abs_set_res(input, ABS_MT_POSITION_X,
819d7e34d12SBenson Leung 			  cyapa->max_abs_x / cyapa->physical_size_x);
820d7e34d12SBenson Leung 	input_abs_set_res(input, ABS_MT_POSITION_Y,
821d7e34d12SBenson Leung 			  cyapa->max_abs_y / cyapa->physical_size_y);
822d7e34d12SBenson Leung 
823d7e34d12SBenson Leung 	if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK)
824d7e34d12SBenson Leung 		__set_bit(BTN_LEFT, input->keybit);
825d7e34d12SBenson Leung 	if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK)
826d7e34d12SBenson Leung 		__set_bit(BTN_MIDDLE, input->keybit);
827d7e34d12SBenson Leung 	if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK)
828d7e34d12SBenson Leung 		__set_bit(BTN_RIGHT, input->keybit);
829d7e34d12SBenson Leung 
830d7e34d12SBenson Leung 	if (cyapa->btn_capability == CAPABILITY_LEFT_BTN_MASK)
831d7e34d12SBenson Leung 		__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
832d7e34d12SBenson Leung 
833d7e34d12SBenson Leung 	/* handle pointer emulation and unused slots in core */
834b1cfa7b4SDudley Du 	error = input_mt_init_slots(input, CYAPA_MAX_MT_SLOTS,
835d7e34d12SBenson Leung 				    INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
836b1cfa7b4SDudley Du 	if (error) {
837b1cfa7b4SDudley Du 		dev_err(dev, "failed to initialize MT slots: %d\n", error);
838b1cfa7b4SDudley Du 		return error;
839d7e34d12SBenson Leung 	}
840d7e34d12SBenson Leung 
841b1cfa7b4SDudley Du 	cyapa->input = input;
842d7e34d12SBenson Leung 	return 0;
843d7e34d12SBenson Leung }
844d7e34d12SBenson Leung 
845d7e34d12SBenson Leung static int cyapa_probe(struct i2c_client *client,
846d7e34d12SBenson Leung 		       const struct i2c_device_id *dev_id)
847d7e34d12SBenson Leung {
848d7e34d12SBenson Leung 	struct device *dev = &client->dev;
849b1cfa7b4SDudley Du 	struct cyapa *cyapa;
850b1cfa7b4SDudley Du 	u8 adapter_func;
851b1cfa7b4SDudley Du 	int error;
852d7e34d12SBenson Leung 
8536ddaf744SBenson Leung 	adapter_func = cyapa_check_adapter_functionality(client);
8546ddaf744SBenson Leung 	if (adapter_func == CYAPA_ADAPTER_FUNC_NONE) {
8556ddaf744SBenson Leung 		dev_err(dev, "not a supported I2C/SMBus adapter\n");
8566ddaf744SBenson Leung 		return -EIO;
8576ddaf744SBenson Leung 	}
8586ddaf744SBenson Leung 
859b1cfa7b4SDudley Du 	cyapa = devm_kzalloc(dev, sizeof(struct cyapa), GFP_KERNEL);
860b1cfa7b4SDudley Du 	if (!cyapa)
861d7e34d12SBenson Leung 		return -ENOMEM;
862d7e34d12SBenson Leung 
863d7e34d12SBenson Leung 	cyapa->gen = CYAPA_GEN3;
864d7e34d12SBenson Leung 	cyapa->client = client;
865d7e34d12SBenson Leung 	i2c_set_clientdata(client, cyapa);
866d7e34d12SBenson Leung 	sprintf(cyapa->phys, "i2c-%d-%04x/input0", client->adapter->nr,
867d7e34d12SBenson Leung 		client->addr);
868d7e34d12SBenson Leung 
8696ddaf744SBenson Leung 	/* i2c isn't supported, use smbus */
8706ddaf744SBenson Leung 	if (adapter_func == CYAPA_ADAPTER_FUNC_SMBUS)
8716ddaf744SBenson Leung 		cyapa->smbus = true;
872b1cfa7b4SDudley Du 
873d7e34d12SBenson Leung 	cyapa->state = CYAPA_STATE_NO_DEVICE;
874b1cfa7b4SDudley Du 
875b1cfa7b4SDudley Du 	error = cyapa_check_is_operational(cyapa);
876b1cfa7b4SDudley Du 	if (error) {
877b1cfa7b4SDudley Du 		dev_err(dev, "device not operational, %d\n", error);
878b1cfa7b4SDudley Du 		return error;
879d7e34d12SBenson Leung 	}
880d7e34d12SBenson Leung 
881b1cfa7b4SDudley Du 	/* Power down the device until we need it */
882b1cfa7b4SDudley Du 	error = cyapa_set_power_mode(cyapa, PWR_MODE_OFF);
883b1cfa7b4SDudley Du 	if (error) {
884b1cfa7b4SDudley Du 		dev_err(dev, "failed to quiesce the device: %d\n", error);
885b1cfa7b4SDudley Du 		return error;
886d7e34d12SBenson Leung 	}
887d7e34d12SBenson Leung 
888b1cfa7b4SDudley Du 	error = cyapa_create_input_dev(cyapa);
889b1cfa7b4SDudley Du 	if (error)
890b1cfa7b4SDudley Du 		return error;
891d7e34d12SBenson Leung 
892b1cfa7b4SDudley Du 	error = devm_request_threaded_irq(dev, client->irq,
893b1cfa7b4SDudley Du 					  NULL, cyapa_irq,
894d7e34d12SBenson Leung 					  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
895b1cfa7b4SDudley Du 					  "cyapa", cyapa);
896b1cfa7b4SDudley Du 	if (error) {
897b1cfa7b4SDudley Du 		dev_err(dev, "IRQ request failed: %d\n, ", error);
898b1cfa7b4SDudley Du 		return error;
899d7e34d12SBenson Leung 	}
900d7e34d12SBenson Leung 
901b1cfa7b4SDudley Du 	/* Disable IRQ until the device is opened */
902b1cfa7b4SDudley Du 	disable_irq(client->irq);
903d7e34d12SBenson Leung 
904b1cfa7b4SDudley Du 	/* Register the device in input subsystem */
905b1cfa7b4SDudley Du 	error = input_register_device(cyapa->input);
906b1cfa7b4SDudley Du 	if (error) {
907b1cfa7b4SDudley Du 		dev_err(dev, "failed to register input device: %d\n", error);
908b1cfa7b4SDudley Du 		return error;
909d7e34d12SBenson Leung 	}
910d7e34d12SBenson Leung 
911d7e34d12SBenson Leung 	return 0;
912d7e34d12SBenson Leung }
913d7e34d12SBenson Leung 
914572081a4SJingoo Han static int __maybe_unused cyapa_suspend(struct device *dev)
915d7e34d12SBenson Leung {
916b1cfa7b4SDudley Du 	struct i2c_client *client = to_i2c_client(dev);
917b1cfa7b4SDudley Du 	struct cyapa *cyapa = i2c_get_clientdata(client);
918b1cfa7b4SDudley Du 	struct input_dev *input = cyapa->input;
919d7e34d12SBenson Leung 	u8 power_mode;
920b1cfa7b4SDudley Du 	int error;
921d7e34d12SBenson Leung 
922b1cfa7b4SDudley Du 	error = mutex_lock_interruptible(&input->mutex);
923b1cfa7b4SDudley Du 	if (error)
924b1cfa7b4SDudley Du 		return error;
925b1cfa7b4SDudley Du 
926b1cfa7b4SDudley Du 	disable_irq(client->irq);
927d7e34d12SBenson Leung 
928d7e34d12SBenson Leung 	/*
929d7e34d12SBenson Leung 	 * Set trackpad device to idle mode if wakeup is allowed,
930d7e34d12SBenson Leung 	 * otherwise turn off.
931d7e34d12SBenson Leung 	 */
932d7e34d12SBenson Leung 	power_mode = device_may_wakeup(dev) ? PWR_MODE_IDLE
933d7e34d12SBenson Leung 					    : PWR_MODE_OFF;
934b1cfa7b4SDudley Du 	error = cyapa_set_power_mode(cyapa, power_mode);
935b1cfa7b4SDudley Du 	if (error)
936b1cfa7b4SDudley Du 		dev_err(dev, "resume: set power mode to %d failed: %d\n",
937b1cfa7b4SDudley Du 			 power_mode, error);
938d7e34d12SBenson Leung 
939d7e34d12SBenson Leung 	if (device_may_wakeup(dev))
940f68a95cdSDudley Du 		cyapa->irq_wake = (enable_irq_wake(client->irq) == 0);
941b1cfa7b4SDudley Du 
942b1cfa7b4SDudley Du 	mutex_unlock(&input->mutex);
943b1cfa7b4SDudley Du 
944d7e34d12SBenson Leung 	return 0;
945d7e34d12SBenson Leung }
946d7e34d12SBenson Leung 
947572081a4SJingoo Han static int __maybe_unused cyapa_resume(struct device *dev)
948d7e34d12SBenson Leung {
949b1cfa7b4SDudley Du 	struct i2c_client *client = to_i2c_client(dev);
950b1cfa7b4SDudley Du 	struct cyapa *cyapa = i2c_get_clientdata(client);
951b1cfa7b4SDudley Du 	struct input_dev *input = cyapa->input;
952b1cfa7b4SDudley Du 	u8 power_mode;
953b1cfa7b4SDudley Du 	int error;
954b1cfa7b4SDudley Du 
955b1cfa7b4SDudley Du 	mutex_lock(&input->mutex);
956d7e34d12SBenson Leung 
957d7e34d12SBenson Leung 	if (device_may_wakeup(dev) && cyapa->irq_wake)
958f68a95cdSDudley Du 		disable_irq_wake(client->irq);
959d7e34d12SBenson Leung 
960b1cfa7b4SDudley Du 	power_mode = input->users ? PWR_MODE_FULL_ACTIVE : PWR_MODE_OFF;
961b1cfa7b4SDudley Du 	error = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE);
962b1cfa7b4SDudley Du 	if (error)
963b1cfa7b4SDudley Du 		dev_warn(dev, "resume: set power mode to %d failed: %d\n",
964b1cfa7b4SDudley Du 			 power_mode, error);
965d7e34d12SBenson Leung 
966f68a95cdSDudley Du 	enable_irq(client->irq);
967b1cfa7b4SDudley Du 
968b1cfa7b4SDudley Du 	mutex_unlock(&input->mutex);
969b1cfa7b4SDudley Du 
970d7e34d12SBenson Leung 	return 0;
971d7e34d12SBenson Leung }
972d7e34d12SBenson Leung 
973d7e34d12SBenson Leung static SIMPLE_DEV_PM_OPS(cyapa_pm_ops, cyapa_suspend, cyapa_resume);
974d7e34d12SBenson Leung 
975d7e34d12SBenson Leung static const struct i2c_device_id cyapa_id_table[] = {
976d7e34d12SBenson Leung 	{ "cyapa", 0 },
977d7e34d12SBenson Leung 	{ },
978d7e34d12SBenson Leung };
979d7e34d12SBenson Leung MODULE_DEVICE_TABLE(i2c, cyapa_id_table);
980d7e34d12SBenson Leung 
981d7e34d12SBenson Leung static struct i2c_driver cyapa_driver = {
982d7e34d12SBenson Leung 	.driver = {
983d7e34d12SBenson Leung 		.name = "cyapa",
984d7e34d12SBenson Leung 		.owner = THIS_MODULE,
985d7e34d12SBenson Leung 		.pm = &cyapa_pm_ops,
986d7e34d12SBenson Leung 	},
987d7e34d12SBenson Leung 
988d7e34d12SBenson Leung 	.probe = cyapa_probe,
989d7e34d12SBenson Leung 	.id_table = cyapa_id_table,
990d7e34d12SBenson Leung };
991d7e34d12SBenson Leung 
992d7e34d12SBenson Leung module_i2c_driver(cyapa_driver);
993d7e34d12SBenson Leung 
994d7e34d12SBenson Leung MODULE_DESCRIPTION("Cypress APA I2C Trackpad Driver");
995d7e34d12SBenson Leung MODULE_AUTHOR("Dudley Du <dudl@cypress.com>");
996d7e34d12SBenson Leung MODULE_LICENSE("GPL");
997