xref: /openbmc/linux/drivers/input/mouse/cyapa.c (revision 572081a4)
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 	int irq;
210d7e34d12SBenson Leung 	bool irq_wake;  /* irq wake is enabled */
2116ddaf744SBenson Leung 	bool smbus;
212d7e34d12SBenson Leung 
213d7e34d12SBenson Leung 	/* read from query data region. */
214d7e34d12SBenson Leung 	char product_id[16];
215d7e34d12SBenson Leung 	u8 btn_capability;
216d7e34d12SBenson Leung 	u8 gen;
217d7e34d12SBenson Leung 	int max_abs_x;
218d7e34d12SBenson Leung 	int max_abs_y;
219d7e34d12SBenson Leung 	int physical_size_x;
220d7e34d12SBenson Leung 	int physical_size_y;
221d7e34d12SBenson Leung };
222d7e34d12SBenson Leung 
223d7e34d12SBenson Leung static const u8 bl_deactivate[] = { 0x00, 0xff, 0x3b, 0x00, 0x01, 0x02, 0x03,
224d7e34d12SBenson Leung 		0x04, 0x05, 0x06, 0x07 };
225d7e34d12SBenson Leung static const u8 bl_exit[] = { 0x00, 0xff, 0xa5, 0x00, 0x01, 0x02, 0x03, 0x04,
226d7e34d12SBenson Leung 		0x05, 0x06, 0x07 };
227d7e34d12SBenson Leung 
228d7e34d12SBenson Leung struct cyapa_cmd_len {
229d7e34d12SBenson Leung 	u8 cmd;
230d7e34d12SBenson Leung 	u8 len;
231d7e34d12SBenson Leung };
232d7e34d12SBenson Leung 
2336ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_NONE   0
2346ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_I2C    1
2356ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_SMBUS  2
2366ddaf744SBenson Leung #define CYAPA_ADAPTER_FUNC_BOTH   3
2376ddaf744SBenson Leung 
2386ddaf744SBenson Leung /*
2396ddaf744SBenson Leung  * macros for SMBus communication
2406ddaf744SBenson Leung  */
2416ddaf744SBenson Leung #define SMBUS_READ   0x01
2426ddaf744SBenson Leung #define SMBUS_WRITE 0x00
2436ddaf744SBenson Leung #define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1))
2446ddaf744SBenson Leung #define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01))
2456ddaf744SBenson Leung #define SMBUS_BYTE_BLOCK_CMD_MASK 0x80
2466ddaf744SBenson Leung #define SMBUS_GROUP_BLOCK_CMD_MASK 0x40
2476ddaf744SBenson Leung 
2486ddaf744SBenson Leung  /* for byte read/write command */
2496ddaf744SBenson Leung #define CMD_RESET 0
2506ddaf744SBenson Leung #define CMD_POWER_MODE 1
2516ddaf744SBenson Leung #define CMD_DEV_STATUS 2
2526ddaf744SBenson Leung #define SMBUS_BYTE_CMD(cmd) (((cmd) & 0x3f) << 1)
2536ddaf744SBenson Leung #define CYAPA_SMBUS_RESET SMBUS_BYTE_CMD(CMD_RESET)
2546ddaf744SBenson Leung #define CYAPA_SMBUS_POWER_MODE SMBUS_BYTE_CMD(CMD_POWER_MODE)
2556ddaf744SBenson Leung #define CYAPA_SMBUS_DEV_STATUS SMBUS_BYTE_CMD(CMD_DEV_STATUS)
2566ddaf744SBenson Leung 
2576ddaf744SBenson Leung  /* for group registers read/write command */
2586ddaf744SBenson Leung #define REG_GROUP_DATA 0
2596ddaf744SBenson Leung #define REG_GROUP_CMD 2
2606ddaf744SBenson Leung #define REG_GROUP_QUERY 3
2616ddaf744SBenson Leung #define SMBUS_GROUP_CMD(grp) (0x80 | (((grp) & 0x07) << 3))
2626ddaf744SBenson Leung #define CYAPA_SMBUS_GROUP_DATA SMBUS_GROUP_CMD(REG_GROUP_DATA)
2636ddaf744SBenson Leung #define CYAPA_SMBUS_GROUP_CMD SMBUS_GROUP_CMD(REG_GROUP_CMD)
2646ddaf744SBenson Leung #define CYAPA_SMBUS_GROUP_QUERY SMBUS_GROUP_CMD(REG_GROUP_QUERY)
2656ddaf744SBenson Leung 
2666ddaf744SBenson Leung  /* for register block read/write command */
2676ddaf744SBenson Leung #define CMD_BL_STATUS 0
2686ddaf744SBenson Leung #define CMD_BL_HEAD 1
2696ddaf744SBenson Leung #define CMD_BL_CMD 2
2706ddaf744SBenson Leung #define CMD_BL_DATA 3
2716ddaf744SBenson Leung #define CMD_BL_ALL 4
2726ddaf744SBenson Leung #define CMD_BLK_PRODUCT_ID 5
2736ddaf744SBenson Leung #define CMD_BLK_HEAD 6
2746ddaf744SBenson Leung #define SMBUS_BLOCK_CMD(cmd) (0xc0 | (((cmd) & 0x1f) << 1))
2756ddaf744SBenson Leung 
2766ddaf744SBenson Leung /* register block read/write command in bootloader mode */
2776ddaf744SBenson Leung #define CYAPA_SMBUS_BL_STATUS SMBUS_BLOCK_CMD(CMD_BL_STATUS)
2786ddaf744SBenson Leung #define CYAPA_SMBUS_BL_HEAD SMBUS_BLOCK_CMD(CMD_BL_HEAD)
2796ddaf744SBenson Leung #define CYAPA_SMBUS_BL_CMD SMBUS_BLOCK_CMD(CMD_BL_CMD)
2806ddaf744SBenson Leung #define CYAPA_SMBUS_BL_DATA SMBUS_BLOCK_CMD(CMD_BL_DATA)
2816ddaf744SBenson Leung #define CYAPA_SMBUS_BL_ALL SMBUS_BLOCK_CMD(CMD_BL_ALL)
2826ddaf744SBenson Leung 
2836ddaf744SBenson Leung /* register block read/write command in operational mode */
2846ddaf744SBenson Leung #define CYAPA_SMBUS_BLK_PRODUCT_ID SMBUS_BLOCK_CMD(CMD_BLK_PRODUCT_ID)
2856ddaf744SBenson Leung #define CYAPA_SMBUS_BLK_HEAD SMBUS_BLOCK_CMD(CMD_BLK_HEAD)
2866ddaf744SBenson Leung 
287d7e34d12SBenson Leung static const struct cyapa_cmd_len cyapa_i2c_cmds[] = {
288d7e34d12SBenson Leung 	{ CYAPA_OFFSET_SOFT_RESET, 1 },
289d7e34d12SBenson Leung 	{ REG_OFFSET_COMMAND_BASE + 1, 1 },
290d7e34d12SBenson Leung 	{ REG_OFFSET_DATA_BASE, 1 },
291d7e34d12SBenson Leung 	{ REG_OFFSET_DATA_BASE, sizeof(struct cyapa_reg_data) },
292d7e34d12SBenson Leung 	{ REG_OFFSET_COMMAND_BASE, 0 },
293d7e34d12SBenson Leung 	{ REG_OFFSET_QUERY_BASE, QUERY_DATA_SIZE },
294d7e34d12SBenson Leung 	{ BL_HEAD_OFFSET, 3 },
295d7e34d12SBenson Leung 	{ BL_HEAD_OFFSET, 16 },
296d7e34d12SBenson Leung 	{ BL_HEAD_OFFSET, 16 },
297d7e34d12SBenson Leung 	{ BL_DATA_OFFSET, 16 },
298d7e34d12SBenson Leung 	{ BL_HEAD_OFFSET, 32 },
299d7e34d12SBenson Leung 	{ REG_OFFSET_QUERY_BASE, PRODUCT_ID_SIZE },
300d7e34d12SBenson Leung 	{ REG_OFFSET_DATA_BASE, 32 }
301d7e34d12SBenson Leung };
302d7e34d12SBenson Leung 
3036ddaf744SBenson Leung static const struct cyapa_cmd_len cyapa_smbus_cmds[] = {
3046ddaf744SBenson Leung 	{ CYAPA_SMBUS_RESET, 1 },
3056ddaf744SBenson Leung 	{ CYAPA_SMBUS_POWER_MODE, 1 },
3066ddaf744SBenson Leung 	{ CYAPA_SMBUS_DEV_STATUS, 1 },
3076ddaf744SBenson Leung 	{ CYAPA_SMBUS_GROUP_DATA, sizeof(struct cyapa_reg_data) },
3086ddaf744SBenson Leung 	{ CYAPA_SMBUS_GROUP_CMD, 2 },
3096ddaf744SBenson Leung 	{ CYAPA_SMBUS_GROUP_QUERY, QUERY_DATA_SIZE },
3106ddaf744SBenson Leung 	{ CYAPA_SMBUS_BL_STATUS, 3 },
3116ddaf744SBenson Leung 	{ CYAPA_SMBUS_BL_HEAD, 16 },
3126ddaf744SBenson Leung 	{ CYAPA_SMBUS_BL_CMD, 16 },
3136ddaf744SBenson Leung 	{ CYAPA_SMBUS_BL_DATA, 16 },
3146ddaf744SBenson Leung 	{ CYAPA_SMBUS_BL_ALL, 32 },
3156ddaf744SBenson Leung 	{ CYAPA_SMBUS_BLK_PRODUCT_ID, PRODUCT_ID_SIZE },
3166ddaf744SBenson Leung 	{ CYAPA_SMBUS_BLK_HEAD, 16 },
3176ddaf744SBenson Leung };
3186ddaf744SBenson Leung 
319d7e34d12SBenson Leung static ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len,
320d7e34d12SBenson Leung 					u8 *values)
321d7e34d12SBenson Leung {
322d7e34d12SBenson Leung 	return i2c_smbus_read_i2c_block_data(cyapa->client, reg, len, values);
323d7e34d12SBenson Leung }
324d7e34d12SBenson Leung 
325d7e34d12SBenson Leung static ssize_t cyapa_i2c_reg_write_block(struct cyapa *cyapa, u8 reg,
326d7e34d12SBenson Leung 					 size_t len, const u8 *values)
327d7e34d12SBenson Leung {
328d7e34d12SBenson Leung 	return i2c_smbus_write_i2c_block_data(cyapa->client, reg, len, values);
329d7e34d12SBenson Leung }
330d7e34d12SBenson Leung 
3316ddaf744SBenson Leung /*
3326ddaf744SBenson Leung  * cyapa_smbus_read_block - perform smbus block read command
3336ddaf744SBenson Leung  * @cyapa  - private data structure of the driver
3346ddaf744SBenson Leung  * @cmd    - the properly encoded smbus command
3356ddaf744SBenson Leung  * @len    - expected length of smbus command result
3366ddaf744SBenson Leung  * @values - buffer to store smbus command result
3376ddaf744SBenson Leung  *
3386ddaf744SBenson Leung  * Returns negative errno, else the number of bytes written.
3396ddaf744SBenson Leung  *
3406ddaf744SBenson Leung  * Note:
3416ddaf744SBenson Leung  * In trackpad device, the memory block allocated for I2C register map
3426ddaf744SBenson Leung  * is 256 bytes, so the max read block for I2C bus is 256 bytes.
3436ddaf744SBenson Leung  */
3446ddaf744SBenson Leung static ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len,
3456ddaf744SBenson Leung 				      u8 *values)
3466ddaf744SBenson Leung {
3476ddaf744SBenson Leung 	ssize_t ret;
3486ddaf744SBenson Leung 	u8 index;
3496ddaf744SBenson Leung 	u8 smbus_cmd;
3506ddaf744SBenson Leung 	u8 *buf;
3516ddaf744SBenson Leung 	struct i2c_client *client = cyapa->client;
3526ddaf744SBenson Leung 
3536ddaf744SBenson Leung 	if (!(SMBUS_BYTE_BLOCK_CMD_MASK & cmd))
3546ddaf744SBenson Leung 		return -EINVAL;
3556ddaf744SBenson Leung 
3566ddaf744SBenson Leung 	if (SMBUS_GROUP_BLOCK_CMD_MASK & cmd) {
3576ddaf744SBenson Leung 		/* read specific block registers command. */
3586ddaf744SBenson Leung 		smbus_cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ);
3596ddaf744SBenson Leung 		ret = i2c_smbus_read_block_data(client, smbus_cmd, values);
3606ddaf744SBenson Leung 		goto out;
3616ddaf744SBenson Leung 	}
3626ddaf744SBenson Leung 
3636ddaf744SBenson Leung 	ret = 0;
3646ddaf744SBenson Leung 	for (index = 0; index * I2C_SMBUS_BLOCK_MAX < len; index++) {
3656ddaf744SBenson Leung 		smbus_cmd = SMBUS_ENCODE_IDX(cmd, index);
3666ddaf744SBenson Leung 		smbus_cmd = SMBUS_ENCODE_RW(smbus_cmd, SMBUS_READ);
3676ddaf744SBenson Leung 		buf = values + I2C_SMBUS_BLOCK_MAX * index;
3686ddaf744SBenson Leung 		ret = i2c_smbus_read_block_data(client, smbus_cmd, buf);
3696ddaf744SBenson Leung 		if (ret < 0)
3706ddaf744SBenson Leung 			goto out;
3716ddaf744SBenson Leung 	}
3726ddaf744SBenson Leung 
3736ddaf744SBenson Leung out:
3746ddaf744SBenson Leung 	return ret > 0 ? len : ret;
3756ddaf744SBenson Leung }
3766ddaf744SBenson Leung 
377d7e34d12SBenson Leung static s32 cyapa_read_byte(struct cyapa *cyapa, u8 cmd_idx)
378d7e34d12SBenson Leung {
3796ddaf744SBenson Leung 	u8 cmd;
380d7e34d12SBenson Leung 
3816ddaf744SBenson Leung 	if (cyapa->smbus) {
3826ddaf744SBenson Leung 		cmd = cyapa_smbus_cmds[cmd_idx].cmd;
3836ddaf744SBenson Leung 		cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ);
3846ddaf744SBenson Leung 	} else {
3856ddaf744SBenson Leung 		cmd = cyapa_i2c_cmds[cmd_idx].cmd;
3866ddaf744SBenson Leung 	}
387d7e34d12SBenson Leung 	return i2c_smbus_read_byte_data(cyapa->client, cmd);
388d7e34d12SBenson Leung }
389d7e34d12SBenson Leung 
390d7e34d12SBenson Leung static s32 cyapa_write_byte(struct cyapa *cyapa, u8 cmd_idx, u8 value)
391d7e34d12SBenson Leung {
3926ddaf744SBenson Leung 	u8 cmd;
393d7e34d12SBenson Leung 
3946ddaf744SBenson Leung 	if (cyapa->smbus) {
3956ddaf744SBenson Leung 		cmd = cyapa_smbus_cmds[cmd_idx].cmd;
3966ddaf744SBenson Leung 		cmd = SMBUS_ENCODE_RW(cmd, SMBUS_WRITE);
3976ddaf744SBenson Leung 	} else {
3986ddaf744SBenson Leung 		cmd = cyapa_i2c_cmds[cmd_idx].cmd;
3996ddaf744SBenson Leung 	}
400d7e34d12SBenson Leung 	return i2c_smbus_write_byte_data(cyapa->client, cmd, value);
401d7e34d12SBenson Leung }
402d7e34d12SBenson Leung 
403d7e34d12SBenson Leung static ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values)
404d7e34d12SBenson Leung {
4056ddaf744SBenson Leung 	u8 cmd;
4066ddaf744SBenson Leung 	size_t len;
407d7e34d12SBenson Leung 
4086ddaf744SBenson Leung 	if (cyapa->smbus) {
4096ddaf744SBenson Leung 		cmd = cyapa_smbus_cmds[cmd_idx].cmd;
4106ddaf744SBenson Leung 		len = cyapa_smbus_cmds[cmd_idx].len;
4116ddaf744SBenson Leung 		return cyapa_smbus_read_block(cyapa, cmd, len, values);
4126ddaf744SBenson Leung 	} else {
4136ddaf744SBenson Leung 		cmd = cyapa_i2c_cmds[cmd_idx].cmd;
4146ddaf744SBenson Leung 		len = cyapa_i2c_cmds[cmd_idx].len;
415d7e34d12SBenson Leung 		return cyapa_i2c_reg_read_block(cyapa, cmd, len, values);
416d7e34d12SBenson Leung 	}
4176ddaf744SBenson Leung }
418d7e34d12SBenson Leung 
419d7e34d12SBenson Leung /*
420d7e34d12SBenson Leung  * Query device for its current operating state.
421d7e34d12SBenson Leung  *
422d7e34d12SBenson Leung  */
423d7e34d12SBenson Leung static int cyapa_get_state(struct cyapa *cyapa)
424d7e34d12SBenson Leung {
425d7e34d12SBenson Leung 	int ret;
426d7e34d12SBenson Leung 	u8 status[BL_STATUS_SIZE];
427d7e34d12SBenson Leung 
428d7e34d12SBenson Leung 	cyapa->state = CYAPA_STATE_NO_DEVICE;
429d7e34d12SBenson Leung 
430d7e34d12SBenson Leung 	/*
431d7e34d12SBenson Leung 	 * Get trackpad status by reading 3 registers starting from 0.
432d7e34d12SBenson Leung 	 * If the device is in the bootloader, this will be BL_HEAD.
433d7e34d12SBenson Leung 	 * If the device is in operation mode, this will be the DATA regs.
434d7e34d12SBenson Leung 	 *
435d7e34d12SBenson Leung 	 */
436d7e34d12SBenson Leung 	ret = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, BL_STATUS_SIZE,
437d7e34d12SBenson Leung 				       status);
4386ddaf744SBenson Leung 
4396ddaf744SBenson Leung 	/*
4406ddaf744SBenson Leung 	 * On smbus systems in OP mode, the i2c_reg_read will fail with
4416ddaf744SBenson Leung 	 * -ETIMEDOUT.  In this case, try again using the smbus equivalent
4426ddaf744SBenson Leung 	 * command.  This should return a BL_HEAD indicating CYAPA_STATE_OP.
4436ddaf744SBenson Leung 	 */
4446ddaf744SBenson Leung 	if (cyapa->smbus && (ret == -ETIMEDOUT || ret == -ENXIO))
4456ddaf744SBenson Leung 		ret = cyapa_read_block(cyapa, CYAPA_CMD_BL_STATUS, status);
4466ddaf744SBenson Leung 
447d7e34d12SBenson Leung 	if (ret != BL_STATUS_SIZE)
448d7e34d12SBenson Leung 		goto error;
449d7e34d12SBenson Leung 
450d7e34d12SBenson Leung 	if ((status[REG_OP_STATUS] & OP_STATUS_SRC) == OP_STATUS_SRC) {
451d7e34d12SBenson Leung 		switch (status[REG_OP_STATUS] & OP_STATUS_DEV) {
452d7e34d12SBenson Leung 		case CYAPA_DEV_NORMAL:
453d7e34d12SBenson Leung 		case CYAPA_DEV_BUSY:
454d7e34d12SBenson Leung 			cyapa->state = CYAPA_STATE_OP;
455d7e34d12SBenson Leung 			break;
456d7e34d12SBenson Leung 		default:
457d7e34d12SBenson Leung 			ret = -EAGAIN;
458d7e34d12SBenson Leung 			goto error;
459d7e34d12SBenson Leung 		}
460d7e34d12SBenson Leung 	} else {
461d7e34d12SBenson Leung 		if (status[REG_BL_STATUS] & BL_STATUS_BUSY)
462d7e34d12SBenson Leung 			cyapa->state = CYAPA_STATE_BL_BUSY;
463d7e34d12SBenson Leung 		else if (status[REG_BL_ERROR] & BL_ERROR_BOOTLOADING)
464d7e34d12SBenson Leung 			cyapa->state = CYAPA_STATE_BL_ACTIVE;
465d7e34d12SBenson Leung 		else
466d7e34d12SBenson Leung 			cyapa->state = CYAPA_STATE_BL_IDLE;
467d7e34d12SBenson Leung 	}
468d7e34d12SBenson Leung 
469d7e34d12SBenson Leung 	return 0;
470d7e34d12SBenson Leung error:
471d7e34d12SBenson Leung 	return (ret < 0) ? ret : -EAGAIN;
472d7e34d12SBenson Leung }
473d7e34d12SBenson Leung 
474d7e34d12SBenson Leung /*
475d7e34d12SBenson Leung  * Poll device for its status in a loop, waiting up to timeout for a response.
476d7e34d12SBenson Leung  *
477d7e34d12SBenson Leung  * When the device switches state, it usually takes ~300 ms.
478d7e34d12SBenson Leung  * However, when running a new firmware image, the device must calibrate its
479d7e34d12SBenson Leung  * sensors, which can take as long as 2 seconds.
480d7e34d12SBenson Leung  *
481d7e34d12SBenson Leung  * Note: The timeout has granularity of the polling rate, which is 100 ms.
482d7e34d12SBenson Leung  *
483d7e34d12SBenson Leung  * Returns:
484d7e34d12SBenson Leung  *   0 when the device eventually responds with a valid non-busy state.
485d7e34d12SBenson Leung  *   -ETIMEDOUT if device never responds (too many -EAGAIN)
486d7e34d12SBenson Leung  *   < 0    other errors
487d7e34d12SBenson Leung  */
488d7e34d12SBenson Leung static int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout)
489d7e34d12SBenson Leung {
490d7e34d12SBenson Leung 	int ret;
491d7e34d12SBenson Leung 	int tries = timeout / 100;
492d7e34d12SBenson Leung 
493d7e34d12SBenson Leung 	ret = cyapa_get_state(cyapa);
494d7e34d12SBenson Leung 	while ((ret || cyapa->state >= CYAPA_STATE_BL_BUSY) && tries--) {
495d7e34d12SBenson Leung 		msleep(100);
496d7e34d12SBenson Leung 		ret = cyapa_get_state(cyapa);
497d7e34d12SBenson Leung 	}
498d7e34d12SBenson Leung 	return (ret == -EAGAIN || ret == -ETIMEDOUT) ? -ETIMEDOUT : ret;
499d7e34d12SBenson Leung }
500d7e34d12SBenson Leung 
501d7e34d12SBenson Leung static int cyapa_bl_deactivate(struct cyapa *cyapa)
502d7e34d12SBenson Leung {
503d7e34d12SBenson Leung 	int ret;
504d7e34d12SBenson Leung 
505d7e34d12SBenson Leung 	ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_deactivate),
506d7e34d12SBenson Leung 					bl_deactivate);
507d7e34d12SBenson Leung 	if (ret < 0)
508d7e34d12SBenson Leung 		return ret;
509d7e34d12SBenson Leung 
510d7e34d12SBenson Leung 	/* wait for bootloader to switch to idle state; should take < 100ms */
511d7e34d12SBenson Leung 	msleep(100);
512d7e34d12SBenson Leung 	ret = cyapa_poll_state(cyapa, 500);
513d7e34d12SBenson Leung 	if (ret < 0)
514d7e34d12SBenson Leung 		return ret;
515d7e34d12SBenson Leung 	if (cyapa->state != CYAPA_STATE_BL_IDLE)
516d7e34d12SBenson Leung 		return -EAGAIN;
517d7e34d12SBenson Leung 	return 0;
518d7e34d12SBenson Leung }
519d7e34d12SBenson Leung 
520d7e34d12SBenson Leung /*
521d7e34d12SBenson Leung  * Exit bootloader
522d7e34d12SBenson Leung  *
523d7e34d12SBenson Leung  * Send bl_exit command, then wait 50 - 100 ms to let device transition to
524d7e34d12SBenson Leung  * operational mode.  If this is the first time the device's firmware is
525d7e34d12SBenson Leung  * running, it can take up to 2 seconds to calibrate its sensors.  So, poll
526d7e34d12SBenson Leung  * the device's new state for up to 2 seconds.
527d7e34d12SBenson Leung  *
528d7e34d12SBenson Leung  * Returns:
529d7e34d12SBenson Leung  *   -EIO    failure while reading from device
530d7e34d12SBenson Leung  *   -EAGAIN device is stuck in bootloader, b/c it has invalid firmware
531d7e34d12SBenson Leung  *   0       device is supported and in operational mode
532d7e34d12SBenson Leung  */
533d7e34d12SBenson Leung static int cyapa_bl_exit(struct cyapa *cyapa)
534d7e34d12SBenson Leung {
535d7e34d12SBenson Leung 	int ret;
536d7e34d12SBenson Leung 
537d7e34d12SBenson Leung 	ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_exit), bl_exit);
538d7e34d12SBenson Leung 	if (ret < 0)
539d7e34d12SBenson Leung 		return ret;
540d7e34d12SBenson Leung 
541d7e34d12SBenson Leung 	/*
542d7e34d12SBenson Leung 	 * Wait for bootloader to exit, and operation mode to start.
543d7e34d12SBenson Leung 	 * Normally, this takes at least 50 ms.
544d7e34d12SBenson Leung 	 */
545d7e34d12SBenson Leung 	usleep_range(50000, 100000);
546d7e34d12SBenson Leung 	/*
547d7e34d12SBenson Leung 	 * In addition, when a device boots for the first time after being
548d7e34d12SBenson Leung 	 * updated to new firmware, it must first calibrate its sensors, which
549d7e34d12SBenson Leung 	 * can take up to an additional 2 seconds.
550d7e34d12SBenson Leung 	 */
551d7e34d12SBenson Leung 	ret = cyapa_poll_state(cyapa, 2000);
552d7e34d12SBenson Leung 	if (ret < 0)
553d7e34d12SBenson Leung 		return ret;
554d7e34d12SBenson Leung 	if (cyapa->state != CYAPA_STATE_OP)
555d7e34d12SBenson Leung 		return -EAGAIN;
556d7e34d12SBenson Leung 
557d7e34d12SBenson Leung 	return 0;
558d7e34d12SBenson Leung }
559d7e34d12SBenson Leung 
560d7e34d12SBenson Leung /*
561d7e34d12SBenson Leung  * Set device power mode
562d7e34d12SBenson Leung  *
563d7e34d12SBenson Leung  */
564d7e34d12SBenson Leung static int cyapa_set_power_mode(struct cyapa *cyapa, u8 power_mode)
565d7e34d12SBenson Leung {
566d7e34d12SBenson Leung 	struct device *dev = &cyapa->client->dev;
567d7e34d12SBenson Leung 	int ret;
568d7e34d12SBenson Leung 	u8 power;
569d7e34d12SBenson Leung 
570d7e34d12SBenson Leung 	if (cyapa->state != CYAPA_STATE_OP)
571d7e34d12SBenson Leung 		return 0;
572d7e34d12SBenson Leung 
573d7e34d12SBenson Leung 	ret = cyapa_read_byte(cyapa, CYAPA_CMD_POWER_MODE);
574d7e34d12SBenson Leung 	if (ret < 0)
575d7e34d12SBenson Leung 		return ret;
576d7e34d12SBenson Leung 
577d7e34d12SBenson Leung 	power = ret & ~PWR_MODE_MASK;
578d7e34d12SBenson Leung 	power |= power_mode & PWR_MODE_MASK;
579d7e34d12SBenson Leung 	ret = cyapa_write_byte(cyapa, CYAPA_CMD_POWER_MODE, power);
580d7e34d12SBenson Leung 	if (ret < 0)
581d7e34d12SBenson Leung 		dev_err(dev, "failed to set power_mode 0x%02x err = %d\n",
582d7e34d12SBenson Leung 			power_mode, ret);
583d7e34d12SBenson Leung 	return ret;
584d7e34d12SBenson Leung }
585d7e34d12SBenson Leung 
586d7e34d12SBenson Leung static int cyapa_get_query_data(struct cyapa *cyapa)
587d7e34d12SBenson Leung {
588d7e34d12SBenson Leung 	u8 query_data[QUERY_DATA_SIZE];
589d7e34d12SBenson Leung 	int ret;
590d7e34d12SBenson Leung 
591d7e34d12SBenson Leung 	if (cyapa->state != CYAPA_STATE_OP)
592d7e34d12SBenson Leung 		return -EBUSY;
593d7e34d12SBenson Leung 
594d7e34d12SBenson Leung 	ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_QUERY, query_data);
595d7e34d12SBenson Leung 	if (ret < 0)
596d7e34d12SBenson Leung 		return ret;
597d7e34d12SBenson Leung 	if (ret != QUERY_DATA_SIZE)
598d7e34d12SBenson Leung 		return -EIO;
599d7e34d12SBenson Leung 
600d7e34d12SBenson Leung 	memcpy(&cyapa->product_id[0], &query_data[0], 5);
601d7e34d12SBenson Leung 	cyapa->product_id[5] = '-';
602d7e34d12SBenson Leung 	memcpy(&cyapa->product_id[6], &query_data[5], 6);
603d7e34d12SBenson Leung 	cyapa->product_id[12] = '-';
604d7e34d12SBenson Leung 	memcpy(&cyapa->product_id[13], &query_data[11], 2);
605d7e34d12SBenson Leung 	cyapa->product_id[15] = '\0';
606d7e34d12SBenson Leung 
607d7e34d12SBenson Leung 	cyapa->btn_capability = query_data[19] & CAPABILITY_BTN_MASK;
608d7e34d12SBenson Leung 
609d7e34d12SBenson Leung 	cyapa->gen = query_data[20] & 0x0f;
610d7e34d12SBenson Leung 
611d7e34d12SBenson Leung 	cyapa->max_abs_x = ((query_data[21] & 0xf0) << 4) | query_data[22];
612d7e34d12SBenson Leung 	cyapa->max_abs_y = ((query_data[21] & 0x0f) << 8) | query_data[23];
613d7e34d12SBenson Leung 
614d7e34d12SBenson Leung 	cyapa->physical_size_x =
615d7e34d12SBenson Leung 		((query_data[24] & 0xf0) << 4) | query_data[25];
616d7e34d12SBenson Leung 	cyapa->physical_size_y =
617d7e34d12SBenson Leung 		((query_data[24] & 0x0f) << 8) | query_data[26];
618d7e34d12SBenson Leung 
619d7e34d12SBenson Leung 	return 0;
620d7e34d12SBenson Leung }
621d7e34d12SBenson Leung 
622d7e34d12SBenson Leung /*
623d7e34d12SBenson Leung  * Check if device is operational.
624d7e34d12SBenson Leung  *
625d7e34d12SBenson Leung  * An operational device is responding, has exited bootloader, and has
626d7e34d12SBenson Leung  * firmware supported by this driver.
627d7e34d12SBenson Leung  *
628d7e34d12SBenson Leung  * Returns:
629d7e34d12SBenson Leung  *   -EBUSY  no device or in bootloader
630d7e34d12SBenson Leung  *   -EIO    failure while reading from device
631d7e34d12SBenson Leung  *   -EAGAIN device is still in bootloader
632d7e34d12SBenson Leung  *           if ->state = CYAPA_STATE_BL_IDLE, device has invalid firmware
633d7e34d12SBenson Leung  *   -EINVAL device is in operational mode, but not supported by this driver
634d7e34d12SBenson Leung  *   0       device is supported
635d7e34d12SBenson Leung  */
636d7e34d12SBenson Leung static int cyapa_check_is_operational(struct cyapa *cyapa)
637d7e34d12SBenson Leung {
638d7e34d12SBenson Leung 	struct device *dev = &cyapa->client->dev;
639d7e34d12SBenson Leung 	static const char unique_str[] = "CYTRA";
640d7e34d12SBenson Leung 	int ret;
641d7e34d12SBenson Leung 
642d7e34d12SBenson Leung 	ret = cyapa_poll_state(cyapa, 2000);
643d7e34d12SBenson Leung 	if (ret < 0)
644d7e34d12SBenson Leung 		return ret;
645d7e34d12SBenson Leung 	switch (cyapa->state) {
646d7e34d12SBenson Leung 	case CYAPA_STATE_BL_ACTIVE:
647d7e34d12SBenson Leung 		ret = cyapa_bl_deactivate(cyapa);
648d7e34d12SBenson Leung 		if (ret)
649d7e34d12SBenson Leung 			return ret;
650d7e34d12SBenson Leung 
651d7e34d12SBenson Leung 	/* Fallthrough state */
652d7e34d12SBenson Leung 	case CYAPA_STATE_BL_IDLE:
653d7e34d12SBenson Leung 		ret = cyapa_bl_exit(cyapa);
654d7e34d12SBenson Leung 		if (ret)
655d7e34d12SBenson Leung 			return ret;
656d7e34d12SBenson Leung 
657d7e34d12SBenson Leung 	/* Fallthrough state */
658d7e34d12SBenson Leung 	case CYAPA_STATE_OP:
659d7e34d12SBenson Leung 		ret = cyapa_get_query_data(cyapa);
660d7e34d12SBenson Leung 		if (ret < 0)
661d7e34d12SBenson Leung 			return ret;
662d7e34d12SBenson Leung 
663d7e34d12SBenson Leung 		/* only support firmware protocol gen3 */
664d7e34d12SBenson Leung 		if (cyapa->gen != CYAPA_GEN3) {
665d7e34d12SBenson Leung 			dev_err(dev, "unsupported protocol version (%d)",
666d7e34d12SBenson Leung 				cyapa->gen);
667d7e34d12SBenson Leung 			return -EINVAL;
668d7e34d12SBenson Leung 		}
669d7e34d12SBenson Leung 
670d7e34d12SBenson Leung 		/* only support product ID starting with CYTRA */
671d7e34d12SBenson Leung 		if (memcmp(cyapa->product_id, unique_str,
672d7e34d12SBenson Leung 			   sizeof(unique_str) - 1) != 0) {
673d7e34d12SBenson Leung 			dev_err(dev, "unsupported product ID (%s)\n",
674d7e34d12SBenson Leung 				cyapa->product_id);
675d7e34d12SBenson Leung 			return -EINVAL;
676d7e34d12SBenson Leung 		}
677d7e34d12SBenson Leung 		return 0;
678d7e34d12SBenson Leung 
679d7e34d12SBenson Leung 	default:
680d7e34d12SBenson Leung 		return -EIO;
681d7e34d12SBenson Leung 	}
682d7e34d12SBenson Leung 	return 0;
683d7e34d12SBenson Leung }
684d7e34d12SBenson Leung 
685d7e34d12SBenson Leung static irqreturn_t cyapa_irq(int irq, void *dev_id)
686d7e34d12SBenson Leung {
687d7e34d12SBenson Leung 	struct cyapa *cyapa = dev_id;
688d7e34d12SBenson Leung 	struct device *dev = &cyapa->client->dev;
689d7e34d12SBenson Leung 	struct input_dev *input = cyapa->input;
690d7e34d12SBenson Leung 	struct cyapa_reg_data data;
691d7e34d12SBenson Leung 	int i;
692d7e34d12SBenson Leung 	int ret;
693d7e34d12SBenson Leung 	int num_fingers;
694d7e34d12SBenson Leung 
695d7e34d12SBenson Leung 	if (device_may_wakeup(dev))
696d7e34d12SBenson Leung 		pm_wakeup_event(dev, 0);
697d7e34d12SBenson Leung 
698d7e34d12SBenson Leung 	ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data);
699d7e34d12SBenson Leung 	if (ret != sizeof(data))
700d7e34d12SBenson Leung 		goto out;
701d7e34d12SBenson Leung 
702d7e34d12SBenson Leung 	if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC ||
703d7e34d12SBenson Leung 	    (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL ||
704d7e34d12SBenson Leung 	    (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) {
705d7e34d12SBenson Leung 		goto out;
706d7e34d12SBenson Leung 	}
707d7e34d12SBenson Leung 
708d7e34d12SBenson Leung 	num_fingers = (data.finger_btn >> 4) & 0x0f;
709d7e34d12SBenson Leung 	for (i = 0; i < num_fingers; i++) {
710d7e34d12SBenson Leung 		const struct cyapa_touch *touch = &data.touches[i];
711d7e34d12SBenson Leung 		/* Note: touch->id range is 1 to 15; slots are 0 to 14. */
712d7e34d12SBenson Leung 		int slot = touch->id - 1;
713d7e34d12SBenson Leung 
714d7e34d12SBenson Leung 		input_mt_slot(input, slot);
715d7e34d12SBenson Leung 		input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
716d7e34d12SBenson Leung 		input_report_abs(input, ABS_MT_POSITION_X,
717d7e34d12SBenson Leung 				 ((touch->xy_hi & 0xf0) << 4) | touch->x_lo);
718d7e34d12SBenson Leung 		input_report_abs(input, ABS_MT_POSITION_Y,
719d7e34d12SBenson Leung 				 ((touch->xy_hi & 0x0f) << 8) | touch->y_lo);
720d7e34d12SBenson Leung 		input_report_abs(input, ABS_MT_PRESSURE, touch->pressure);
721d7e34d12SBenson Leung 	}
722d7e34d12SBenson Leung 
723d7e34d12SBenson Leung 	input_mt_sync_frame(input);
724d7e34d12SBenson Leung 
725d7e34d12SBenson Leung 	if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK)
726d7e34d12SBenson Leung 		input_report_key(input, BTN_LEFT,
727d7e34d12SBenson Leung 				 data.finger_btn & OP_DATA_LEFT_BTN);
728d7e34d12SBenson Leung 
729d7e34d12SBenson Leung 	if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK)
730d7e34d12SBenson Leung 		input_report_key(input, BTN_MIDDLE,
731d7e34d12SBenson Leung 				 data.finger_btn & OP_DATA_MIDDLE_BTN);
732d7e34d12SBenson Leung 
733d7e34d12SBenson Leung 	if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK)
734d7e34d12SBenson Leung 		input_report_key(input, BTN_RIGHT,
735d7e34d12SBenson Leung 				 data.finger_btn & OP_DATA_RIGHT_BTN);
736d7e34d12SBenson Leung 
737d7e34d12SBenson Leung 	input_sync(input);
738d7e34d12SBenson Leung 
739d7e34d12SBenson Leung out:
740d7e34d12SBenson Leung 	return IRQ_HANDLED;
741d7e34d12SBenson Leung }
742d7e34d12SBenson Leung 
7436ddaf744SBenson Leung static u8 cyapa_check_adapter_functionality(struct i2c_client *client)
7446ddaf744SBenson Leung {
7456ddaf744SBenson Leung 	u8 ret = CYAPA_ADAPTER_FUNC_NONE;
7466ddaf744SBenson Leung 
7476ddaf744SBenson Leung 	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
7486ddaf744SBenson Leung 		ret |= CYAPA_ADAPTER_FUNC_I2C;
7496ddaf744SBenson Leung 	if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
7506ddaf744SBenson Leung 				     I2C_FUNC_SMBUS_BLOCK_DATA |
7516ddaf744SBenson Leung 				     I2C_FUNC_SMBUS_I2C_BLOCK))
7526ddaf744SBenson Leung 		ret |= CYAPA_ADAPTER_FUNC_SMBUS;
7536ddaf744SBenson Leung 	return ret;
7546ddaf744SBenson Leung }
7556ddaf744SBenson Leung 
756d7e34d12SBenson Leung static int cyapa_create_input_dev(struct cyapa *cyapa)
757d7e34d12SBenson Leung {
758d7e34d12SBenson Leung 	struct device *dev = &cyapa->client->dev;
759d7e34d12SBenson Leung 	int ret;
760d7e34d12SBenson Leung 	struct input_dev *input;
761d7e34d12SBenson Leung 
762d7e34d12SBenson Leung 	if (!cyapa->physical_size_x || !cyapa->physical_size_y)
763d7e34d12SBenson Leung 		return -EINVAL;
764d7e34d12SBenson Leung 
765d7e34d12SBenson Leung 	input = cyapa->input = input_allocate_device();
766d7e34d12SBenson Leung 	if (!input) {
767d7e34d12SBenson Leung 		dev_err(dev, "allocate memory for input device failed\n");
768d7e34d12SBenson Leung 		return -ENOMEM;
769d7e34d12SBenson Leung 	}
770d7e34d12SBenson Leung 
771d7e34d12SBenson Leung 	input->name = CYAPA_NAME;
772d7e34d12SBenson Leung 	input->phys = cyapa->phys;
773d7e34d12SBenson Leung 	input->id.bustype = BUS_I2C;
774d7e34d12SBenson Leung 	input->id.version = 1;
775d7e34d12SBenson Leung 	input->id.product = 0;  /* means any product in eventcomm. */
776d7e34d12SBenson Leung 	input->dev.parent = &cyapa->client->dev;
777d7e34d12SBenson Leung 
778d7e34d12SBenson Leung 	input_set_drvdata(input, cyapa);
779d7e34d12SBenson Leung 
780d7e34d12SBenson Leung 	__set_bit(EV_ABS, input->evbit);
781d7e34d12SBenson Leung 
782d7e34d12SBenson Leung 	/* finger position */
783d7e34d12SBenson Leung 	input_set_abs_params(input, ABS_MT_POSITION_X, 0, cyapa->max_abs_x, 0,
784d7e34d12SBenson Leung 			     0);
785d7e34d12SBenson Leung 	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cyapa->max_abs_y, 0,
786d7e34d12SBenson Leung 			     0);
787d7e34d12SBenson Leung 	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
788d7e34d12SBenson Leung 
789d7e34d12SBenson Leung 	input_abs_set_res(input, ABS_MT_POSITION_X,
790d7e34d12SBenson Leung 			  cyapa->max_abs_x / cyapa->physical_size_x);
791d7e34d12SBenson Leung 	input_abs_set_res(input, ABS_MT_POSITION_Y,
792d7e34d12SBenson Leung 			  cyapa->max_abs_y / cyapa->physical_size_y);
793d7e34d12SBenson Leung 
794d7e34d12SBenson Leung 	if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK)
795d7e34d12SBenson Leung 		__set_bit(BTN_LEFT, input->keybit);
796d7e34d12SBenson Leung 	if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK)
797d7e34d12SBenson Leung 		__set_bit(BTN_MIDDLE, input->keybit);
798d7e34d12SBenson Leung 	if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK)
799d7e34d12SBenson Leung 		__set_bit(BTN_RIGHT, input->keybit);
800d7e34d12SBenson Leung 
801d7e34d12SBenson Leung 	if (cyapa->btn_capability == CAPABILITY_LEFT_BTN_MASK)
802d7e34d12SBenson Leung 		__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
803d7e34d12SBenson Leung 
804d7e34d12SBenson Leung 	/* handle pointer emulation and unused slots in core */
805d7e34d12SBenson Leung 	ret = input_mt_init_slots(input, CYAPA_MAX_MT_SLOTS,
806d7e34d12SBenson Leung 				  INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
807d7e34d12SBenson Leung 	if (ret) {
808d7e34d12SBenson Leung 		dev_err(dev, "allocate memory for MT slots failed, %d\n", ret);
809d7e34d12SBenson Leung 		goto err_free_device;
810d7e34d12SBenson Leung 	}
811d7e34d12SBenson Leung 
812d7e34d12SBenson Leung 	/* Register the device in input subsystem */
813d7e34d12SBenson Leung 	ret = input_register_device(input);
814d7e34d12SBenson Leung 	if (ret) {
815d7e34d12SBenson Leung 		dev_err(dev, "input device register failed, %d\n", ret);
816d7e34d12SBenson Leung 		goto err_free_device;
817d7e34d12SBenson Leung 	}
818d7e34d12SBenson Leung 	return 0;
819d7e34d12SBenson Leung 
820d7e34d12SBenson Leung err_free_device:
821d7e34d12SBenson Leung 	input_free_device(input);
822d7e34d12SBenson Leung 	cyapa->input = NULL;
823d7e34d12SBenson Leung 	return ret;
824d7e34d12SBenson Leung }
825d7e34d12SBenson Leung 
826d7e34d12SBenson Leung static int cyapa_probe(struct i2c_client *client,
827d7e34d12SBenson Leung 		       const struct i2c_device_id *dev_id)
828d7e34d12SBenson Leung {
829d7e34d12SBenson Leung 	int ret;
8306ddaf744SBenson Leung 	u8 adapter_func;
831d7e34d12SBenson Leung 	struct cyapa *cyapa;
832d7e34d12SBenson Leung 	struct device *dev = &client->dev;
833d7e34d12SBenson Leung 
8346ddaf744SBenson Leung 	adapter_func = cyapa_check_adapter_functionality(client);
8356ddaf744SBenson Leung 	if (adapter_func == CYAPA_ADAPTER_FUNC_NONE) {
8366ddaf744SBenson Leung 		dev_err(dev, "not a supported I2C/SMBus adapter\n");
8376ddaf744SBenson Leung 		return -EIO;
8386ddaf744SBenson Leung 	}
8396ddaf744SBenson Leung 
840d7e34d12SBenson Leung 	cyapa = kzalloc(sizeof(struct cyapa), GFP_KERNEL);
841d7e34d12SBenson Leung 	if (!cyapa) {
842d7e34d12SBenson Leung 		dev_err(dev, "allocate memory for cyapa failed\n");
843d7e34d12SBenson Leung 		return -ENOMEM;
844d7e34d12SBenson Leung 	}
845d7e34d12SBenson Leung 
846d7e34d12SBenson Leung 	cyapa->gen = CYAPA_GEN3;
847d7e34d12SBenson Leung 	cyapa->client = client;
848d7e34d12SBenson Leung 	i2c_set_clientdata(client, cyapa);
849d7e34d12SBenson Leung 	sprintf(cyapa->phys, "i2c-%d-%04x/input0", client->adapter->nr,
850d7e34d12SBenson Leung 		client->addr);
851d7e34d12SBenson Leung 
8526ddaf744SBenson Leung 	/* i2c isn't supported, use smbus */
8536ddaf744SBenson Leung 	if (adapter_func == CYAPA_ADAPTER_FUNC_SMBUS)
8546ddaf744SBenson Leung 		cyapa->smbus = true;
855d7e34d12SBenson Leung 	cyapa->state = CYAPA_STATE_NO_DEVICE;
856d7e34d12SBenson Leung 	ret = cyapa_check_is_operational(cyapa);
857d7e34d12SBenson Leung 	if (ret) {
858d7e34d12SBenson Leung 		dev_err(dev, "device not operational, %d\n", ret);
859d7e34d12SBenson Leung 		goto err_mem_free;
860d7e34d12SBenson Leung 	}
861d7e34d12SBenson Leung 
862d7e34d12SBenson Leung 	ret = cyapa_create_input_dev(cyapa);
863d7e34d12SBenson Leung 	if (ret) {
864d7e34d12SBenson Leung 		dev_err(dev, "create input_dev instance failed, %d\n", ret);
865d7e34d12SBenson Leung 		goto err_mem_free;
866d7e34d12SBenson Leung 	}
867d7e34d12SBenson Leung 
868d7e34d12SBenson Leung 	ret = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE);
869d7e34d12SBenson Leung 	if (ret) {
870d7e34d12SBenson Leung 		dev_err(dev, "set active power failed, %d\n", ret);
871d7e34d12SBenson Leung 		goto err_unregister_device;
872d7e34d12SBenson Leung 	}
873d7e34d12SBenson Leung 
874d7e34d12SBenson Leung 	cyapa->irq = client->irq;
875d7e34d12SBenson Leung 	ret = request_threaded_irq(cyapa->irq,
876d7e34d12SBenson Leung 				   NULL,
877d7e34d12SBenson Leung 				   cyapa_irq,
878d7e34d12SBenson Leung 				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
879d7e34d12SBenson Leung 				   "cyapa",
880d7e34d12SBenson Leung 				   cyapa);
881d7e34d12SBenson Leung 	if (ret) {
882d7e34d12SBenson Leung 		dev_err(dev, "IRQ request failed: %d\n, ", ret);
883d7e34d12SBenson Leung 		goto err_unregister_device;
884d7e34d12SBenson Leung 	}
885d7e34d12SBenson Leung 
886d7e34d12SBenson Leung 	return 0;
887d7e34d12SBenson Leung 
888d7e34d12SBenson Leung err_unregister_device:
889d7e34d12SBenson Leung 	input_unregister_device(cyapa->input);
890d7e34d12SBenson Leung err_mem_free:
891d7e34d12SBenson Leung 	kfree(cyapa);
892d7e34d12SBenson Leung 
893d7e34d12SBenson Leung 	return ret;
894d7e34d12SBenson Leung }
895d7e34d12SBenson Leung 
896d7e34d12SBenson Leung static int cyapa_remove(struct i2c_client *client)
897d7e34d12SBenson Leung {
898d7e34d12SBenson Leung 	struct cyapa *cyapa = i2c_get_clientdata(client);
899d7e34d12SBenson Leung 
900d7e34d12SBenson Leung 	free_irq(cyapa->irq, cyapa);
901d7e34d12SBenson Leung 	input_unregister_device(cyapa->input);
902d7e34d12SBenson Leung 	cyapa_set_power_mode(cyapa, PWR_MODE_OFF);
903d7e34d12SBenson Leung 	kfree(cyapa);
904d7e34d12SBenson Leung 
905d7e34d12SBenson Leung 	return 0;
906d7e34d12SBenson Leung }
907d7e34d12SBenson Leung 
908572081a4SJingoo Han static int __maybe_unused cyapa_suspend(struct device *dev)
909d7e34d12SBenson Leung {
910d7e34d12SBenson Leung 	int ret;
911d7e34d12SBenson Leung 	u8 power_mode;
912d7e34d12SBenson Leung 	struct cyapa *cyapa = dev_get_drvdata(dev);
913d7e34d12SBenson Leung 
914d7e34d12SBenson Leung 	disable_irq(cyapa->irq);
915d7e34d12SBenson Leung 
916d7e34d12SBenson Leung 	/*
917d7e34d12SBenson Leung 	 * Set trackpad device to idle mode if wakeup is allowed,
918d7e34d12SBenson Leung 	 * otherwise turn off.
919d7e34d12SBenson Leung 	 */
920d7e34d12SBenson Leung 	power_mode = device_may_wakeup(dev) ? PWR_MODE_IDLE
921d7e34d12SBenson Leung 					    : PWR_MODE_OFF;
922d7e34d12SBenson Leung 	ret = cyapa_set_power_mode(cyapa, power_mode);
923d7e34d12SBenson Leung 	if (ret < 0)
924d7e34d12SBenson Leung 		dev_err(dev, "set power mode failed, %d\n", ret);
925d7e34d12SBenson Leung 
926d7e34d12SBenson Leung 	if (device_may_wakeup(dev))
927d7e34d12SBenson Leung 		cyapa->irq_wake = (enable_irq_wake(cyapa->irq) == 0);
928d7e34d12SBenson Leung 	return 0;
929d7e34d12SBenson Leung }
930d7e34d12SBenson Leung 
931572081a4SJingoo Han static int __maybe_unused cyapa_resume(struct device *dev)
932d7e34d12SBenson Leung {
933d7e34d12SBenson Leung 	int ret;
934d7e34d12SBenson Leung 	struct cyapa *cyapa = dev_get_drvdata(dev);
935d7e34d12SBenson Leung 
936d7e34d12SBenson Leung 	if (device_may_wakeup(dev) && cyapa->irq_wake)
937d7e34d12SBenson Leung 		disable_irq_wake(cyapa->irq);
938d7e34d12SBenson Leung 
939d7e34d12SBenson Leung 	ret = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE);
940d7e34d12SBenson Leung 	if (ret)
941d7e34d12SBenson Leung 		dev_warn(dev, "resume active power failed, %d\n", ret);
942d7e34d12SBenson Leung 
943d7e34d12SBenson Leung 	enable_irq(cyapa->irq);
944d7e34d12SBenson Leung 	return 0;
945d7e34d12SBenson Leung }
946d7e34d12SBenson Leung 
947d7e34d12SBenson Leung static SIMPLE_DEV_PM_OPS(cyapa_pm_ops, cyapa_suspend, cyapa_resume);
948d7e34d12SBenson Leung 
949d7e34d12SBenson Leung static const struct i2c_device_id cyapa_id_table[] = {
950d7e34d12SBenson Leung 	{ "cyapa", 0 },
951d7e34d12SBenson Leung 	{ },
952d7e34d12SBenson Leung };
953d7e34d12SBenson Leung MODULE_DEVICE_TABLE(i2c, cyapa_id_table);
954d7e34d12SBenson Leung 
955d7e34d12SBenson Leung static struct i2c_driver cyapa_driver = {
956d7e34d12SBenson Leung 	.driver = {
957d7e34d12SBenson Leung 		.name = "cyapa",
958d7e34d12SBenson Leung 		.owner = THIS_MODULE,
959d7e34d12SBenson Leung 		.pm = &cyapa_pm_ops,
960d7e34d12SBenson Leung 	},
961d7e34d12SBenson Leung 
962d7e34d12SBenson Leung 	.probe = cyapa_probe,
963d7e34d12SBenson Leung 	.remove = cyapa_remove,
964d7e34d12SBenson Leung 	.id_table = cyapa_id_table,
965d7e34d12SBenson Leung };
966d7e34d12SBenson Leung 
967d7e34d12SBenson Leung module_i2c_driver(cyapa_driver);
968d7e34d12SBenson Leung 
969d7e34d12SBenson Leung MODULE_DESCRIPTION("Cypress APA I2C Trackpad Driver");
970d7e34d12SBenson Leung MODULE_AUTHOR("Dudley Du <dudl@cypress.com>");
971d7e34d12SBenson Leung MODULE_LICENSE("GPL");
972