xref: /openbmc/linux/drivers/input/misc/iqs626a.c (revision dbce1a7d)
1f1d2809dSJeff LaBundy // SPDX-License-Identifier: GPL-2.0+
2f1d2809dSJeff LaBundy /*
3f1d2809dSJeff LaBundy  * Azoteq IQS626A Capacitive Touch Controller
4f1d2809dSJeff LaBundy  *
5f1d2809dSJeff LaBundy  * Copyright (C) 2020 Jeff LaBundy <jeff@labundy.com>
6f1d2809dSJeff LaBundy  *
7f1d2809dSJeff LaBundy  * This driver registers up to 2 input devices: one representing capacitive or
8f1d2809dSJeff LaBundy  * inductive keys as well as Hall-effect switches, and one for a trackpad that
9f1d2809dSJeff LaBundy  * can express various gestures.
10f1d2809dSJeff LaBundy  */
11f1d2809dSJeff LaBundy 
12f1d2809dSJeff LaBundy #include <linux/bits.h>
13f1d2809dSJeff LaBundy #include <linux/completion.h>
14f1d2809dSJeff LaBundy #include <linux/delay.h>
15f1d2809dSJeff LaBundy #include <linux/device.h>
16f1d2809dSJeff LaBundy #include <linux/err.h>
17f1d2809dSJeff LaBundy #include <linux/i2c.h>
18f1d2809dSJeff LaBundy #include <linux/input.h>
19f1d2809dSJeff LaBundy #include <linux/input/touchscreen.h>
20f1d2809dSJeff LaBundy #include <linux/interrupt.h>
21f1d2809dSJeff LaBundy #include <linux/kernel.h>
22*dbce1a7dSRob Herring #include <linux/mod_devicetable.h>
23f1d2809dSJeff LaBundy #include <linux/module.h>
24f1d2809dSJeff LaBundy #include <linux/property.h>
25f1d2809dSJeff LaBundy #include <linux/regmap.h>
26f1d2809dSJeff LaBundy #include <linux/slab.h>
27f1d2809dSJeff LaBundy 
28f1d2809dSJeff LaBundy #define IQS626_VER_INFO				0x00
29f1d2809dSJeff LaBundy #define IQS626_VER_INFO_PROD_NUM		0x51
30f1d2809dSJeff LaBundy 
31f1d2809dSJeff LaBundy #define IQS626_SYS_FLAGS			0x02
32f1d2809dSJeff LaBundy #define IQS626_SYS_FLAGS_SHOW_RESET		BIT(15)
33f1d2809dSJeff LaBundy #define IQS626_SYS_FLAGS_IN_ATI			BIT(12)
34f1d2809dSJeff LaBundy #define IQS626_SYS_FLAGS_PWR_MODE_MASK		GENMASK(9, 8)
35f1d2809dSJeff LaBundy #define IQS626_SYS_FLAGS_PWR_MODE_SHIFT		8
36f1d2809dSJeff LaBundy 
37f1d2809dSJeff LaBundy #define IQS626_HALL_OUTPUT			0x23
38f1d2809dSJeff LaBundy 
39f1d2809dSJeff LaBundy #define IQS626_SYS_SETTINGS			0x80
40f1d2809dSJeff LaBundy #define IQS626_SYS_SETTINGS_CLK_DIV		BIT(15)
41f1d2809dSJeff LaBundy #define IQS626_SYS_SETTINGS_ULP_AUTO		BIT(14)
42f1d2809dSJeff LaBundy #define IQS626_SYS_SETTINGS_DIS_AUTO		BIT(13)
43f1d2809dSJeff LaBundy #define IQS626_SYS_SETTINGS_PWR_MODE_MASK	GENMASK(12, 11)
44f1d2809dSJeff LaBundy #define IQS626_SYS_SETTINGS_PWR_MODE_SHIFT	11
45f1d2809dSJeff LaBundy #define IQS626_SYS_SETTINGS_PWR_MODE_MAX	3
46f1d2809dSJeff LaBundy #define IQS626_SYS_SETTINGS_ULP_UPDATE_MASK	GENMASK(10, 8)
47f1d2809dSJeff LaBundy #define IQS626_SYS_SETTINGS_ULP_UPDATE_SHIFT	8
48f1d2809dSJeff LaBundy #define IQS626_SYS_SETTINGS_ULP_UPDATE_MAX	7
49f1d2809dSJeff LaBundy #define IQS626_SYS_SETTINGS_EVENT_MODE		BIT(5)
50f1d2809dSJeff LaBundy #define IQS626_SYS_SETTINGS_EVENT_MODE_LP	BIT(4)
51f1d2809dSJeff LaBundy #define IQS626_SYS_SETTINGS_REDO_ATI		BIT(2)
52f1d2809dSJeff LaBundy #define IQS626_SYS_SETTINGS_ACK_RESET		BIT(0)
53f1d2809dSJeff LaBundy 
54f1d2809dSJeff LaBundy #define IQS626_MISC_A_ATI_BAND_DISABLE		BIT(7)
55f1d2809dSJeff LaBundy #define IQS626_MISC_A_TPx_LTA_UPDATE_MASK	GENMASK(6, 4)
56f1d2809dSJeff LaBundy #define IQS626_MISC_A_TPx_LTA_UPDATE_SHIFT	4
57f1d2809dSJeff LaBundy #define IQS626_MISC_A_TPx_LTA_UPDATE_MAX	7
58f1d2809dSJeff LaBundy #define IQS626_MISC_A_ATI_LP_ONLY		BIT(3)
59f1d2809dSJeff LaBundy #define IQS626_MISC_A_GPIO3_SELECT_MASK		GENMASK(2, 0)
60f1d2809dSJeff LaBundy #define IQS626_MISC_A_GPIO3_SELECT_MAX		7
61f1d2809dSJeff LaBundy 
62f1d2809dSJeff LaBundy #define IQS626_EVENT_MASK_SYS			BIT(6)
63f1d2809dSJeff LaBundy #define IQS626_EVENT_MASK_GESTURE		BIT(3)
64f1d2809dSJeff LaBundy #define IQS626_EVENT_MASK_DEEP			BIT(2)
65f1d2809dSJeff LaBundy #define IQS626_EVENT_MASK_TOUCH			BIT(1)
66f1d2809dSJeff LaBundy #define IQS626_EVENT_MASK_PROX			BIT(0)
67f1d2809dSJeff LaBundy 
68f1d2809dSJeff LaBundy #define IQS626_RATE_NP_MS_MAX			255
69f1d2809dSJeff LaBundy #define IQS626_RATE_LP_MS_MAX			255
70f1d2809dSJeff LaBundy #define IQS626_RATE_ULP_MS_MAX			4080
71f1d2809dSJeff LaBundy #define IQS626_TIMEOUT_PWR_MS_MAX		130560
72f1d2809dSJeff LaBundy #define IQS626_TIMEOUT_LTA_MS_MAX		130560
73f1d2809dSJeff LaBundy 
74f1d2809dSJeff LaBundy #define IQS626_MISC_B_RESEED_UI_SEL_MASK	GENMASK(7, 6)
75f1d2809dSJeff LaBundy #define IQS626_MISC_B_RESEED_UI_SEL_SHIFT	6
76f1d2809dSJeff LaBundy #define IQS626_MISC_B_RESEED_UI_SEL_MAX		3
77f1d2809dSJeff LaBundy #define IQS626_MISC_B_THRESH_EXTEND		BIT(5)
78f1d2809dSJeff LaBundy #define IQS626_MISC_B_TRACKING_UI_ENABLE	BIT(4)
79f1d2809dSJeff LaBundy #define IQS626_MISC_B_TPx_SWIPE			BIT(3)
80f1d2809dSJeff LaBundy #define IQS626_MISC_B_RESEED_OFFSET		BIT(2)
81f1d2809dSJeff LaBundy #define IQS626_MISC_B_FILT_STR_TPx		GENMASK(1, 0)
82f1d2809dSJeff LaBundy 
83f1d2809dSJeff LaBundy #define IQS626_THRESH_SWIPE_MAX			255
84f1d2809dSJeff LaBundy #define IQS626_TIMEOUT_TAP_MS_MAX		4080
85f1d2809dSJeff LaBundy #define IQS626_TIMEOUT_SWIPE_MS_MAX		4080
86f1d2809dSJeff LaBundy 
87f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_0_MEAS_CAP_SIZE		BIT(7)
88f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_0_RX_TERM_VSS		BIT(5)
89f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_0_LINEARIZE		BIT(4)
90f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_0_DUAL_DIR		BIT(3)
91f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_0_FILT_DISABLE		BIT(2)
92f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_0_ATI_MODE_MASK		GENMASK(1, 0)
93f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_0_ATI_MODE_MAX		3
94f1d2809dSJeff LaBundy 
95f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_1_CCT_HIGH_1		BIT(7)
96f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_1_CCT_HIGH_0		BIT(6)
97f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_1_PROJ_BIAS_MASK		GENMASK(5, 4)
98f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_1_PROJ_BIAS_SHIFT	4
99f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_1_PROJ_BIAS_MAX		3
100f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_1_CCT_ENABLE		BIT(3)
101f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_1_SENSE_FREQ_MASK	GENMASK(2, 1)
102f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_1_SENSE_FREQ_SHIFT	1
103f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_1_SENSE_FREQ_MAX		3
104f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_1_ATI_BAND_TIGHTEN	BIT(0)
105f1d2809dSJeff LaBundy 
106f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_2_LOCAL_CAP_MASK		GENMASK(7, 6)
107f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_2_LOCAL_CAP_SHIFT	6
108f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_2_LOCAL_CAP_MAX		3
109f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_2_LOCAL_CAP_ENABLE	BIT(5)
110f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_2_SENSE_MODE_MASK	GENMASK(3, 0)
111f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_2_SENSE_MODE_MAX		15
112f1d2809dSJeff LaBundy 
113f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_3_TX_FREQ_MASK		GENMASK(5, 4)
114f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_3_TX_FREQ_SHIFT		4
115f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_3_TX_FREQ_MAX		3
116f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_3_INV_LOGIC		BIT(0)
117f1d2809dSJeff LaBundy 
118f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_4_RX_TERM_VREG		BIT(6)
119f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_4_CCT_LOW_1		BIT(5)
120f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_4_CCT_LOW_0		BIT(4)
121f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_4_COMP_DISABLE		BIT(1)
122f1d2809dSJeff LaBundy #define IQS626_CHx_ENG_4_STATIC_ENABLE		BIT(0)
123f1d2809dSJeff LaBundy 
124f1d2809dSJeff LaBundy #define IQS626_TPx_ATI_BASE_MIN			45
125f1d2809dSJeff LaBundy #define IQS626_TPx_ATI_BASE_MAX			300
126f1d2809dSJeff LaBundy #define IQS626_CHx_ATI_BASE_MASK		GENMASK(7, 6)
127f1d2809dSJeff LaBundy #define IQS626_CHx_ATI_BASE_75			0x00
128f1d2809dSJeff LaBundy #define IQS626_CHx_ATI_BASE_100			0x40
129f1d2809dSJeff LaBundy #define IQS626_CHx_ATI_BASE_150			0x80
130f1d2809dSJeff LaBundy #define IQS626_CHx_ATI_BASE_200			0xC0
131f1d2809dSJeff LaBundy #define IQS626_CHx_ATI_TARGET_MASK		GENMASK(5, 0)
132f1d2809dSJeff LaBundy #define IQS626_CHx_ATI_TARGET_MAX		2016
133f1d2809dSJeff LaBundy 
134f1d2809dSJeff LaBundy #define IQS626_CHx_THRESH_MAX			255
135f1d2809dSJeff LaBundy #define IQS626_CHx_HYST_DEEP_MASK		GENMASK(7, 4)
136f1d2809dSJeff LaBundy #define IQS626_CHx_HYST_DEEP_SHIFT		4
137f1d2809dSJeff LaBundy #define IQS626_CHx_HYST_TOUCH_MASK		GENMASK(3, 0)
138f1d2809dSJeff LaBundy #define IQS626_CHx_HYST_MAX			15
139f1d2809dSJeff LaBundy 
140f1d2809dSJeff LaBundy #define IQS626_FILT_STR_NP_TPx_MASK		GENMASK(7, 6)
141f1d2809dSJeff LaBundy #define IQS626_FILT_STR_NP_TPx_SHIFT		6
142f1d2809dSJeff LaBundy #define IQS626_FILT_STR_LP_TPx_MASK		GENMASK(5, 4)
143f1d2809dSJeff LaBundy #define IQS626_FILT_STR_LP_TPx_SHIFT		4
144f1d2809dSJeff LaBundy 
145f1d2809dSJeff LaBundy #define IQS626_FILT_STR_NP_CNT_MASK		GENMASK(7, 6)
146f1d2809dSJeff LaBundy #define IQS626_FILT_STR_NP_CNT_SHIFT		6
147f1d2809dSJeff LaBundy #define IQS626_FILT_STR_LP_CNT_MASK		GENMASK(5, 4)
148f1d2809dSJeff LaBundy #define IQS626_FILT_STR_LP_CNT_SHIFT		4
149f1d2809dSJeff LaBundy #define IQS626_FILT_STR_NP_LTA_MASK		GENMASK(3, 2)
150f1d2809dSJeff LaBundy #define IQS626_FILT_STR_NP_LTA_SHIFT		2
151f1d2809dSJeff LaBundy #define IQS626_FILT_STR_LP_LTA_MASK		GENMASK(1, 0)
152f1d2809dSJeff LaBundy #define IQS626_FILT_STR_MAX			3
153f1d2809dSJeff LaBundy 
154f1d2809dSJeff LaBundy #define IQS626_ULP_PROJ_ENABLE			BIT(4)
155f1d2809dSJeff LaBundy #define IQS626_GEN_WEIGHT_MAX			255
156f1d2809dSJeff LaBundy 
157f1d2809dSJeff LaBundy #define IQS626_MAX_REG				0xFF
158f1d2809dSJeff LaBundy 
159f1d2809dSJeff LaBundy #define IQS626_NUM_CH_TP_3			9
160f1d2809dSJeff LaBundy #define IQS626_NUM_CH_TP_2			6
161f1d2809dSJeff LaBundy #define IQS626_NUM_CH_GEN			3
162f1d2809dSJeff LaBundy #define IQS626_NUM_CRx_TX			8
163f1d2809dSJeff LaBundy 
164f1d2809dSJeff LaBundy #define IQS626_PWR_MODE_POLL_SLEEP_US		50000
165f1d2809dSJeff LaBundy #define IQS626_PWR_MODE_POLL_TIMEOUT_US		500000
166f1d2809dSJeff LaBundy 
167f1d2809dSJeff LaBundy #define iqs626_irq_wait()			usleep_range(350, 400)
168f1d2809dSJeff LaBundy 
169f1d2809dSJeff LaBundy enum iqs626_ch_id {
170f1d2809dSJeff LaBundy 	IQS626_CH_ULP_0,
171f1d2809dSJeff LaBundy 	IQS626_CH_TP_2,
172f1d2809dSJeff LaBundy 	IQS626_CH_TP_3,
173f1d2809dSJeff LaBundy 	IQS626_CH_GEN_0,
174f1d2809dSJeff LaBundy 	IQS626_CH_GEN_1,
175f1d2809dSJeff LaBundy 	IQS626_CH_GEN_2,
176f1d2809dSJeff LaBundy 	IQS626_CH_HALL,
177f1d2809dSJeff LaBundy };
178f1d2809dSJeff LaBundy 
179f1d2809dSJeff LaBundy enum iqs626_rx_inactive {
180f1d2809dSJeff LaBundy 	IQS626_RX_INACTIVE_VSS,
181f1d2809dSJeff LaBundy 	IQS626_RX_INACTIVE_FLOAT,
182f1d2809dSJeff LaBundy 	IQS626_RX_INACTIVE_VREG,
183f1d2809dSJeff LaBundy };
184f1d2809dSJeff LaBundy 
185f1d2809dSJeff LaBundy enum iqs626_st_offs {
186f1d2809dSJeff LaBundy 	IQS626_ST_OFFS_PROX,
187f1d2809dSJeff LaBundy 	IQS626_ST_OFFS_DIR,
188f1d2809dSJeff LaBundy 	IQS626_ST_OFFS_TOUCH,
189f1d2809dSJeff LaBundy 	IQS626_ST_OFFS_DEEP,
190f1d2809dSJeff LaBundy };
191f1d2809dSJeff LaBundy 
192f1d2809dSJeff LaBundy enum iqs626_th_offs {
193f1d2809dSJeff LaBundy 	IQS626_TH_OFFS_PROX,
194f1d2809dSJeff LaBundy 	IQS626_TH_OFFS_TOUCH,
195f1d2809dSJeff LaBundy 	IQS626_TH_OFFS_DEEP,
196f1d2809dSJeff LaBundy };
197f1d2809dSJeff LaBundy 
198f1d2809dSJeff LaBundy enum iqs626_event_id {
199f1d2809dSJeff LaBundy 	IQS626_EVENT_PROX_DN,
200f1d2809dSJeff LaBundy 	IQS626_EVENT_PROX_UP,
201f1d2809dSJeff LaBundy 	IQS626_EVENT_TOUCH_DN,
202f1d2809dSJeff LaBundy 	IQS626_EVENT_TOUCH_UP,
203f1d2809dSJeff LaBundy 	IQS626_EVENT_DEEP_DN,
204f1d2809dSJeff LaBundy 	IQS626_EVENT_DEEP_UP,
205f1d2809dSJeff LaBundy };
206f1d2809dSJeff LaBundy 
207f1d2809dSJeff LaBundy enum iqs626_gesture_id {
208f1d2809dSJeff LaBundy 	IQS626_GESTURE_FLICK_X_POS,
209f1d2809dSJeff LaBundy 	IQS626_GESTURE_FLICK_X_NEG,
210f1d2809dSJeff LaBundy 	IQS626_GESTURE_FLICK_Y_POS,
211f1d2809dSJeff LaBundy 	IQS626_GESTURE_FLICK_Y_NEG,
212f1d2809dSJeff LaBundy 	IQS626_GESTURE_TAP,
213f1d2809dSJeff LaBundy 	IQS626_GESTURE_HOLD,
214f1d2809dSJeff LaBundy 	IQS626_NUM_GESTURES,
215f1d2809dSJeff LaBundy };
216f1d2809dSJeff LaBundy 
217f1d2809dSJeff LaBundy struct iqs626_event_desc {
218f1d2809dSJeff LaBundy 	const char *name;
219f1d2809dSJeff LaBundy 	enum iqs626_st_offs st_offs;
220f1d2809dSJeff LaBundy 	enum iqs626_th_offs th_offs;
221f1d2809dSJeff LaBundy 	bool dir_up;
222f1d2809dSJeff LaBundy 	u8 mask;
223f1d2809dSJeff LaBundy };
224f1d2809dSJeff LaBundy 
225f1d2809dSJeff LaBundy static const struct iqs626_event_desc iqs626_events[] = {
226f1d2809dSJeff LaBundy 	[IQS626_EVENT_PROX_DN] = {
227f1d2809dSJeff LaBundy 		.name = "event-prox",
228f1d2809dSJeff LaBundy 		.st_offs = IQS626_ST_OFFS_PROX,
229f1d2809dSJeff LaBundy 		.th_offs = IQS626_TH_OFFS_PROX,
230f1d2809dSJeff LaBundy 		.mask = IQS626_EVENT_MASK_PROX,
231f1d2809dSJeff LaBundy 	},
232f1d2809dSJeff LaBundy 	[IQS626_EVENT_PROX_UP] = {
233f1d2809dSJeff LaBundy 		.name = "event-prox-alt",
234f1d2809dSJeff LaBundy 		.st_offs = IQS626_ST_OFFS_PROX,
235f1d2809dSJeff LaBundy 		.th_offs = IQS626_TH_OFFS_PROX,
236f1d2809dSJeff LaBundy 		.dir_up = true,
237f1d2809dSJeff LaBundy 		.mask = IQS626_EVENT_MASK_PROX,
238f1d2809dSJeff LaBundy 	},
239f1d2809dSJeff LaBundy 	[IQS626_EVENT_TOUCH_DN] = {
240f1d2809dSJeff LaBundy 		.name = "event-touch",
241f1d2809dSJeff LaBundy 		.st_offs = IQS626_ST_OFFS_TOUCH,
242f1d2809dSJeff LaBundy 		.th_offs = IQS626_TH_OFFS_TOUCH,
243f1d2809dSJeff LaBundy 		.mask = IQS626_EVENT_MASK_TOUCH,
244f1d2809dSJeff LaBundy 	},
245f1d2809dSJeff LaBundy 	[IQS626_EVENT_TOUCH_UP] = {
246f1d2809dSJeff LaBundy 		.name = "event-touch-alt",
247f1d2809dSJeff LaBundy 		.st_offs = IQS626_ST_OFFS_TOUCH,
248f1d2809dSJeff LaBundy 		.th_offs = IQS626_TH_OFFS_TOUCH,
249f1d2809dSJeff LaBundy 		.dir_up = true,
250f1d2809dSJeff LaBundy 		.mask = IQS626_EVENT_MASK_TOUCH,
251f1d2809dSJeff LaBundy 	},
252f1d2809dSJeff LaBundy 	[IQS626_EVENT_DEEP_DN] = {
253f1d2809dSJeff LaBundy 		.name = "event-deep",
254f1d2809dSJeff LaBundy 		.st_offs = IQS626_ST_OFFS_DEEP,
255f1d2809dSJeff LaBundy 		.th_offs = IQS626_TH_OFFS_DEEP,
256f1d2809dSJeff LaBundy 		.mask = IQS626_EVENT_MASK_DEEP,
257f1d2809dSJeff LaBundy 	},
258f1d2809dSJeff LaBundy 	[IQS626_EVENT_DEEP_UP] = {
259f1d2809dSJeff LaBundy 		.name = "event-deep-alt",
260f1d2809dSJeff LaBundy 		.st_offs = IQS626_ST_OFFS_DEEP,
261f1d2809dSJeff LaBundy 		.th_offs = IQS626_TH_OFFS_DEEP,
262f1d2809dSJeff LaBundy 		.dir_up = true,
263f1d2809dSJeff LaBundy 		.mask = IQS626_EVENT_MASK_DEEP,
264f1d2809dSJeff LaBundy 	},
265f1d2809dSJeff LaBundy };
266f1d2809dSJeff LaBundy 
267f1d2809dSJeff LaBundy struct iqs626_ver_info {
268f1d2809dSJeff LaBundy 	u8 prod_num;
269f1d2809dSJeff LaBundy 	u8 sw_num;
270f1d2809dSJeff LaBundy 	u8 hw_num;
271f1d2809dSJeff LaBundy 	u8 padding;
272f1d2809dSJeff LaBundy } __packed;
273f1d2809dSJeff LaBundy 
274f1d2809dSJeff LaBundy struct iqs626_flags {
275f1d2809dSJeff LaBundy 	__be16 system;
276f1d2809dSJeff LaBundy 	u8 gesture;
277f1d2809dSJeff LaBundy 	u8 padding_a;
278f1d2809dSJeff LaBundy 	u8 states[4];
279f1d2809dSJeff LaBundy 	u8 ref_active;
280f1d2809dSJeff LaBundy 	u8 padding_b;
281f1d2809dSJeff LaBundy 	u8 comp_min;
282f1d2809dSJeff LaBundy 	u8 comp_max;
283f1d2809dSJeff LaBundy 	u8 trackpad_x;
284f1d2809dSJeff LaBundy 	u8 trackpad_y;
285f1d2809dSJeff LaBundy } __packed;
286f1d2809dSJeff LaBundy 
287f1d2809dSJeff LaBundy struct iqs626_ch_reg_ulp {
288f1d2809dSJeff LaBundy 	u8 thresh[2];
289f1d2809dSJeff LaBundy 	u8 hyst;
290f1d2809dSJeff LaBundy 	u8 filter;
291f1d2809dSJeff LaBundy 	u8 engine[2];
292f1d2809dSJeff LaBundy 	u8 ati_target;
293f1d2809dSJeff LaBundy 	u8 padding;
294f1d2809dSJeff LaBundy 	__be16 ati_comp;
295f1d2809dSJeff LaBundy 	u8 rx_enable;
296f1d2809dSJeff LaBundy 	u8 tx_enable;
297f1d2809dSJeff LaBundy } __packed;
298f1d2809dSJeff LaBundy 
299f1d2809dSJeff LaBundy struct iqs626_ch_reg_tp {
300f1d2809dSJeff LaBundy 	u8 thresh;
301f1d2809dSJeff LaBundy 	u8 ati_base;
302f1d2809dSJeff LaBundy 	__be16 ati_comp;
303f1d2809dSJeff LaBundy } __packed;
304f1d2809dSJeff LaBundy 
305f1d2809dSJeff LaBundy struct iqs626_tp_grp_reg {
306f1d2809dSJeff LaBundy 	u8 hyst;
307f1d2809dSJeff LaBundy 	u8 ati_target;
308f1d2809dSJeff LaBundy 	u8 engine[2];
309f1d2809dSJeff LaBundy 	struct iqs626_ch_reg_tp ch_reg_tp[IQS626_NUM_CH_TP_3];
310f1d2809dSJeff LaBundy } __packed;
311f1d2809dSJeff LaBundy 
312f1d2809dSJeff LaBundy struct iqs626_ch_reg_gen {
313f1d2809dSJeff LaBundy 	u8 thresh[3];
314f1d2809dSJeff LaBundy 	u8 padding;
315f1d2809dSJeff LaBundy 	u8 hyst;
316f1d2809dSJeff LaBundy 	u8 ati_target;
317f1d2809dSJeff LaBundy 	__be16 ati_comp;
318f1d2809dSJeff LaBundy 	u8 engine[5];
319f1d2809dSJeff LaBundy 	u8 filter;
320f1d2809dSJeff LaBundy 	u8 rx_enable;
321f1d2809dSJeff LaBundy 	u8 tx_enable;
322f1d2809dSJeff LaBundy 	u8 assoc_select;
323f1d2809dSJeff LaBundy 	u8 assoc_weight;
324f1d2809dSJeff LaBundy } __packed;
325f1d2809dSJeff LaBundy 
326f1d2809dSJeff LaBundy struct iqs626_ch_reg_hall {
327f1d2809dSJeff LaBundy 	u8 engine;
328f1d2809dSJeff LaBundy 	u8 thresh;
329f1d2809dSJeff LaBundy 	u8 hyst;
330f1d2809dSJeff LaBundy 	u8 ati_target;
331f1d2809dSJeff LaBundy 	__be16 ati_comp;
332f1d2809dSJeff LaBundy } __packed;
333f1d2809dSJeff LaBundy 
334f1d2809dSJeff LaBundy struct iqs626_sys_reg {
335f1d2809dSJeff LaBundy 	__be16 general;
336f1d2809dSJeff LaBundy 	u8 misc_a;
337f1d2809dSJeff LaBundy 	u8 event_mask;
338f1d2809dSJeff LaBundy 	u8 active;
339f1d2809dSJeff LaBundy 	u8 reseed;
340f1d2809dSJeff LaBundy 	u8 rate_np;
341f1d2809dSJeff LaBundy 	u8 rate_lp;
342f1d2809dSJeff LaBundy 	u8 rate_ulp;
343f1d2809dSJeff LaBundy 	u8 timeout_pwr;
344f1d2809dSJeff LaBundy 	u8 timeout_rdy;
345f1d2809dSJeff LaBundy 	u8 timeout_lta;
346f1d2809dSJeff LaBundy 	u8 misc_b;
347f1d2809dSJeff LaBundy 	u8 thresh_swipe;
348f1d2809dSJeff LaBundy 	u8 timeout_tap;
349f1d2809dSJeff LaBundy 	u8 timeout_swipe;
350f1d2809dSJeff LaBundy 	u8 redo_ati;
351f1d2809dSJeff LaBundy 	u8 padding;
352f1d2809dSJeff LaBundy 	struct iqs626_ch_reg_ulp ch_reg_ulp;
353f1d2809dSJeff LaBundy 	struct iqs626_tp_grp_reg tp_grp_reg;
354f1d2809dSJeff LaBundy 	struct iqs626_ch_reg_gen ch_reg_gen[IQS626_NUM_CH_GEN];
355f1d2809dSJeff LaBundy 	struct iqs626_ch_reg_hall ch_reg_hall;
356f1d2809dSJeff LaBundy } __packed;
357f1d2809dSJeff LaBundy 
358f1d2809dSJeff LaBundy struct iqs626_channel_desc {
359f1d2809dSJeff LaBundy 	const char *name;
360f1d2809dSJeff LaBundy 	int num_ch;
361f1d2809dSJeff LaBundy 	u8 active;
362f1d2809dSJeff LaBundy 	bool events[ARRAY_SIZE(iqs626_events)];
363f1d2809dSJeff LaBundy };
364f1d2809dSJeff LaBundy 
365f1d2809dSJeff LaBundy static const struct iqs626_channel_desc iqs626_channels[] = {
366f1d2809dSJeff LaBundy 	[IQS626_CH_ULP_0] = {
367f1d2809dSJeff LaBundy 		.name = "ulp-0",
368f1d2809dSJeff LaBundy 		.num_ch = 1,
369f1d2809dSJeff LaBundy 		.active = BIT(0),
370f1d2809dSJeff LaBundy 		.events = {
371f1d2809dSJeff LaBundy 			[IQS626_EVENT_PROX_DN] = true,
372f1d2809dSJeff LaBundy 			[IQS626_EVENT_PROX_UP] = true,
373f1d2809dSJeff LaBundy 			[IQS626_EVENT_TOUCH_DN] = true,
374f1d2809dSJeff LaBundy 			[IQS626_EVENT_TOUCH_UP] = true,
375f1d2809dSJeff LaBundy 		},
376f1d2809dSJeff LaBundy 	},
377f1d2809dSJeff LaBundy 	[IQS626_CH_TP_2] = {
378f1d2809dSJeff LaBundy 		.name = "trackpad-3x2",
379f1d2809dSJeff LaBundy 		.num_ch = IQS626_NUM_CH_TP_2,
380f1d2809dSJeff LaBundy 		.active = BIT(1),
381f1d2809dSJeff LaBundy 		.events = {
382f1d2809dSJeff LaBundy 			[IQS626_EVENT_TOUCH_DN] = true,
383f1d2809dSJeff LaBundy 		},
384f1d2809dSJeff LaBundy 	},
385f1d2809dSJeff LaBundy 	[IQS626_CH_TP_3] = {
386f1d2809dSJeff LaBundy 		.name = "trackpad-3x3",
387f1d2809dSJeff LaBundy 		.num_ch = IQS626_NUM_CH_TP_3,
388f1d2809dSJeff LaBundy 		.active = BIT(2) | BIT(1),
389f1d2809dSJeff LaBundy 		.events = {
390f1d2809dSJeff LaBundy 			[IQS626_EVENT_TOUCH_DN] = true,
391f1d2809dSJeff LaBundy 		},
392f1d2809dSJeff LaBundy 	},
393f1d2809dSJeff LaBundy 	[IQS626_CH_GEN_0] = {
394f1d2809dSJeff LaBundy 		.name = "generic-0",
395f1d2809dSJeff LaBundy 		.num_ch = 1,
396f1d2809dSJeff LaBundy 		.active = BIT(4),
397f1d2809dSJeff LaBundy 		.events = {
398f1d2809dSJeff LaBundy 			[IQS626_EVENT_PROX_DN] = true,
399f1d2809dSJeff LaBundy 			[IQS626_EVENT_PROX_UP] = true,
400f1d2809dSJeff LaBundy 			[IQS626_EVENT_TOUCH_DN] = true,
401f1d2809dSJeff LaBundy 			[IQS626_EVENT_TOUCH_UP] = true,
402f1d2809dSJeff LaBundy 			[IQS626_EVENT_DEEP_DN] = true,
403f1d2809dSJeff LaBundy 			[IQS626_EVENT_DEEP_UP] = true,
404f1d2809dSJeff LaBundy 		},
405f1d2809dSJeff LaBundy 	},
406f1d2809dSJeff LaBundy 	[IQS626_CH_GEN_1] = {
407f1d2809dSJeff LaBundy 		.name = "generic-1",
408f1d2809dSJeff LaBundy 		.num_ch = 1,
409f1d2809dSJeff LaBundy 		.active = BIT(5),
410f1d2809dSJeff LaBundy 		.events = {
411f1d2809dSJeff LaBundy 			[IQS626_EVENT_PROX_DN] = true,
412f1d2809dSJeff LaBundy 			[IQS626_EVENT_PROX_UP] = true,
413f1d2809dSJeff LaBundy 			[IQS626_EVENT_TOUCH_DN] = true,
414f1d2809dSJeff LaBundy 			[IQS626_EVENT_TOUCH_UP] = true,
415f1d2809dSJeff LaBundy 			[IQS626_EVENT_DEEP_DN] = true,
416f1d2809dSJeff LaBundy 			[IQS626_EVENT_DEEP_UP] = true,
417f1d2809dSJeff LaBundy 		},
418f1d2809dSJeff LaBundy 	},
419f1d2809dSJeff LaBundy 	[IQS626_CH_GEN_2] = {
420f1d2809dSJeff LaBundy 		.name = "generic-2",
421f1d2809dSJeff LaBundy 		.num_ch = 1,
422f1d2809dSJeff LaBundy 		.active = BIT(6),
423f1d2809dSJeff LaBundy 		.events = {
424f1d2809dSJeff LaBundy 			[IQS626_EVENT_PROX_DN] = true,
425f1d2809dSJeff LaBundy 			[IQS626_EVENT_PROX_UP] = true,
426f1d2809dSJeff LaBundy 			[IQS626_EVENT_TOUCH_DN] = true,
427f1d2809dSJeff LaBundy 			[IQS626_EVENT_TOUCH_UP] = true,
428f1d2809dSJeff LaBundy 			[IQS626_EVENT_DEEP_DN] = true,
429f1d2809dSJeff LaBundy 			[IQS626_EVENT_DEEP_UP] = true,
430f1d2809dSJeff LaBundy 		},
431f1d2809dSJeff LaBundy 	},
432f1d2809dSJeff LaBundy 	[IQS626_CH_HALL] = {
433f1d2809dSJeff LaBundy 		.name = "hall",
434f1d2809dSJeff LaBundy 		.num_ch = 1,
435f1d2809dSJeff LaBundy 		.active = BIT(7),
436f1d2809dSJeff LaBundy 		.events = {
437f1d2809dSJeff LaBundy 			[IQS626_EVENT_TOUCH_DN] = true,
438f1d2809dSJeff LaBundy 			[IQS626_EVENT_TOUCH_UP] = true,
439f1d2809dSJeff LaBundy 		},
440f1d2809dSJeff LaBundy 	},
441f1d2809dSJeff LaBundy };
442f1d2809dSJeff LaBundy 
443f1d2809dSJeff LaBundy struct iqs626_private {
444f1d2809dSJeff LaBundy 	struct i2c_client *client;
445f1d2809dSJeff LaBundy 	struct regmap *regmap;
446f1d2809dSJeff LaBundy 	struct iqs626_sys_reg sys_reg;
447f1d2809dSJeff LaBundy 	struct completion ati_done;
448f1d2809dSJeff LaBundy 	struct input_dev *keypad;
449f1d2809dSJeff LaBundy 	struct input_dev *trackpad;
450f1d2809dSJeff LaBundy 	struct touchscreen_properties prop;
451f1d2809dSJeff LaBundy 	unsigned int kp_type[ARRAY_SIZE(iqs626_channels)]
452f1d2809dSJeff LaBundy 			    [ARRAY_SIZE(iqs626_events)];
453f1d2809dSJeff LaBundy 	unsigned int kp_code[ARRAY_SIZE(iqs626_channels)]
454f1d2809dSJeff LaBundy 			    [ARRAY_SIZE(iqs626_events)];
455f1d2809dSJeff LaBundy 	unsigned int tp_code[IQS626_NUM_GESTURES];
456f1d2809dSJeff LaBundy 	unsigned int suspend_mode;
457f1d2809dSJeff LaBundy };
458f1d2809dSJeff LaBundy 
459e1f5e848SJeff LaBundy static noinline_for_stack int
iqs626_parse_events(struct iqs626_private * iqs626,struct fwnode_handle * ch_node,enum iqs626_ch_id ch_id)460e1f5e848SJeff LaBundy iqs626_parse_events(struct iqs626_private *iqs626,
4614d3d2694SJeff LaBundy 		    struct fwnode_handle *ch_node, enum iqs626_ch_id ch_id)
462f1d2809dSJeff LaBundy {
463f1d2809dSJeff LaBundy 	struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
464f1d2809dSJeff LaBundy 	struct i2c_client *client = iqs626->client;
4654d3d2694SJeff LaBundy 	struct fwnode_handle *ev_node;
466f1d2809dSJeff LaBundy 	const char *ev_name;
467f1d2809dSJeff LaBundy 	u8 *thresh, *hyst;
468f1d2809dSJeff LaBundy 	unsigned int val;
4694d3d2694SJeff LaBundy 	int i;
470f1d2809dSJeff LaBundy 
471f1d2809dSJeff LaBundy 	switch (ch_id) {
472f1d2809dSJeff LaBundy 	case IQS626_CH_ULP_0:
473f1d2809dSJeff LaBundy 		thresh = sys_reg->ch_reg_ulp.thresh;
474f1d2809dSJeff LaBundy 		hyst = &sys_reg->ch_reg_ulp.hyst;
475f1d2809dSJeff LaBundy 		break;
476f1d2809dSJeff LaBundy 
477f1d2809dSJeff LaBundy 	case IQS626_CH_TP_2:
478f1d2809dSJeff LaBundy 	case IQS626_CH_TP_3:
479f1d2809dSJeff LaBundy 		thresh = &sys_reg->tp_grp_reg.ch_reg_tp[0].thresh;
480f1d2809dSJeff LaBundy 		hyst = &sys_reg->tp_grp_reg.hyst;
481f1d2809dSJeff LaBundy 		break;
482f1d2809dSJeff LaBundy 
483f1d2809dSJeff LaBundy 	case IQS626_CH_GEN_0:
484f1d2809dSJeff LaBundy 	case IQS626_CH_GEN_1:
485f1d2809dSJeff LaBundy 	case IQS626_CH_GEN_2:
486f1d2809dSJeff LaBundy 		i = ch_id - IQS626_CH_GEN_0;
487f1d2809dSJeff LaBundy 		thresh = sys_reg->ch_reg_gen[i].thresh;
488f1d2809dSJeff LaBundy 		hyst = &sys_reg->ch_reg_gen[i].hyst;
489f1d2809dSJeff LaBundy 		break;
490f1d2809dSJeff LaBundy 
491f1d2809dSJeff LaBundy 	case IQS626_CH_HALL:
492f1d2809dSJeff LaBundy 		thresh = &sys_reg->ch_reg_hall.thresh;
493f1d2809dSJeff LaBundy 		hyst = &sys_reg->ch_reg_hall.hyst;
494f1d2809dSJeff LaBundy 		break;
495f1d2809dSJeff LaBundy 
496f1d2809dSJeff LaBundy 	default:
497f1d2809dSJeff LaBundy 		return -EINVAL;
498f1d2809dSJeff LaBundy 	}
499f1d2809dSJeff LaBundy 
500f1d2809dSJeff LaBundy 	for (i = 0; i < ARRAY_SIZE(iqs626_events); i++) {
501f1d2809dSJeff LaBundy 		if (!iqs626_channels[ch_id].events[i])
502f1d2809dSJeff LaBundy 			continue;
503f1d2809dSJeff LaBundy 
504f1d2809dSJeff LaBundy 		if (ch_id == IQS626_CH_TP_2 || ch_id == IQS626_CH_TP_3) {
505f1d2809dSJeff LaBundy 			/*
506f1d2809dSJeff LaBundy 			 * Trackpad touch events are simply described under the
507f1d2809dSJeff LaBundy 			 * trackpad child node.
508f1d2809dSJeff LaBundy 			 */
5094d3d2694SJeff LaBundy 			ev_node = fwnode_handle_get(ch_node);
510f1d2809dSJeff LaBundy 		} else {
511f1d2809dSJeff LaBundy 			ev_name = iqs626_events[i].name;
512f1d2809dSJeff LaBundy 			ev_node = fwnode_get_named_child_node(ch_node, ev_name);
513f1d2809dSJeff LaBundy 			if (!ev_node)
514f1d2809dSJeff LaBundy 				continue;
515f1d2809dSJeff LaBundy 
516f1d2809dSJeff LaBundy 			if (!fwnode_property_read_u32(ev_node, "linux,code",
517f1d2809dSJeff LaBundy 						      &val)) {
518f1d2809dSJeff LaBundy 				iqs626->kp_code[ch_id][i] = val;
519f1d2809dSJeff LaBundy 
520f1d2809dSJeff LaBundy 				if (fwnode_property_read_u32(ev_node,
521f1d2809dSJeff LaBundy 							     "linux,input-type",
522f1d2809dSJeff LaBundy 							     &val)) {
523f1d2809dSJeff LaBundy 					if (ch_id == IQS626_CH_HALL)
524f1d2809dSJeff LaBundy 						val = EV_SW;
525f1d2809dSJeff LaBundy 					else
526f1d2809dSJeff LaBundy 						val = EV_KEY;
527f1d2809dSJeff LaBundy 				}
528f1d2809dSJeff LaBundy 
529f1d2809dSJeff LaBundy 				if (val != EV_KEY && val != EV_SW) {
530f1d2809dSJeff LaBundy 					dev_err(&client->dev,
531f1d2809dSJeff LaBundy 						"Invalid input type: %u\n",
532f1d2809dSJeff LaBundy 						val);
5334d3d2694SJeff LaBundy 					fwnode_handle_put(ev_node);
534f1d2809dSJeff LaBundy 					return -EINVAL;
535f1d2809dSJeff LaBundy 				}
536f1d2809dSJeff LaBundy 
537f1d2809dSJeff LaBundy 				iqs626->kp_type[ch_id][i] = val;
538f1d2809dSJeff LaBundy 
539f1d2809dSJeff LaBundy 				sys_reg->event_mask &= ~iqs626_events[i].mask;
540f1d2809dSJeff LaBundy 			}
541f1d2809dSJeff LaBundy 		}
542f1d2809dSJeff LaBundy 
543f1d2809dSJeff LaBundy 		if (!fwnode_property_read_u32(ev_node, "azoteq,hyst", &val)) {
544f1d2809dSJeff LaBundy 			if (val > IQS626_CHx_HYST_MAX) {
545f1d2809dSJeff LaBundy 				dev_err(&client->dev,
546f1d2809dSJeff LaBundy 					"Invalid %s channel hysteresis: %u\n",
547f1d2809dSJeff LaBundy 					fwnode_get_name(ch_node), val);
5484d3d2694SJeff LaBundy 				fwnode_handle_put(ev_node);
549f1d2809dSJeff LaBundy 				return -EINVAL;
550f1d2809dSJeff LaBundy 			}
551f1d2809dSJeff LaBundy 
552f1d2809dSJeff LaBundy 			if (i == IQS626_EVENT_DEEP_DN ||
553f1d2809dSJeff LaBundy 			    i == IQS626_EVENT_DEEP_UP) {
554f1d2809dSJeff LaBundy 				*hyst &= ~IQS626_CHx_HYST_DEEP_MASK;
555f1d2809dSJeff LaBundy 				*hyst |= (val << IQS626_CHx_HYST_DEEP_SHIFT);
556f1d2809dSJeff LaBundy 			} else if (i == IQS626_EVENT_TOUCH_DN ||
557f1d2809dSJeff LaBundy 				   i == IQS626_EVENT_TOUCH_UP) {
558f1d2809dSJeff LaBundy 				*hyst &= ~IQS626_CHx_HYST_TOUCH_MASK;
559f1d2809dSJeff LaBundy 				*hyst |= val;
560f1d2809dSJeff LaBundy 			}
561f1d2809dSJeff LaBundy 		}
562f1d2809dSJeff LaBundy 
563f1d2809dSJeff LaBundy 		if (ch_id != IQS626_CH_TP_2 && ch_id != IQS626_CH_TP_3 &&
564f1d2809dSJeff LaBundy 		    !fwnode_property_read_u32(ev_node, "azoteq,thresh", &val)) {
565f1d2809dSJeff LaBundy 			if (val > IQS626_CHx_THRESH_MAX) {
566f1d2809dSJeff LaBundy 				dev_err(&client->dev,
567f1d2809dSJeff LaBundy 					"Invalid %s channel threshold: %u\n",
568f1d2809dSJeff LaBundy 					fwnode_get_name(ch_node), val);
5694d3d2694SJeff LaBundy 				fwnode_handle_put(ev_node);
570f1d2809dSJeff LaBundy 				return -EINVAL;
571f1d2809dSJeff LaBundy 			}
572f1d2809dSJeff LaBundy 
573f1d2809dSJeff LaBundy 			if (ch_id == IQS626_CH_HALL)
574f1d2809dSJeff LaBundy 				*thresh = val;
575f1d2809dSJeff LaBundy 			else
576f1d2809dSJeff LaBundy 				*(thresh + iqs626_events[i].th_offs) = val;
577f1d2809dSJeff LaBundy 		}
578f1d2809dSJeff LaBundy 
5794d3d2694SJeff LaBundy 		fwnode_handle_put(ev_node);
580f1d2809dSJeff LaBundy 	}
581f1d2809dSJeff LaBundy 
582f1d2809dSJeff LaBundy 	return 0;
583f1d2809dSJeff LaBundy }
584f1d2809dSJeff LaBundy 
585e1f5e848SJeff LaBundy static noinline_for_stack int
iqs626_parse_ati_target(struct iqs626_private * iqs626,struct fwnode_handle * ch_node,enum iqs626_ch_id ch_id)586e1f5e848SJeff LaBundy iqs626_parse_ati_target(struct iqs626_private *iqs626,
5874d3d2694SJeff LaBundy 			struct fwnode_handle *ch_node, enum iqs626_ch_id ch_id)
588f1d2809dSJeff LaBundy {
589f1d2809dSJeff LaBundy 	struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
590f1d2809dSJeff LaBundy 	struct i2c_client *client = iqs626->client;
591f1d2809dSJeff LaBundy 	unsigned int val;
592f1d2809dSJeff LaBundy 	u8 *ati_target;
5934d3d2694SJeff LaBundy 	int i;
594f1d2809dSJeff LaBundy 
595f1d2809dSJeff LaBundy 	switch (ch_id) {
596f1d2809dSJeff LaBundy 	case IQS626_CH_ULP_0:
597f1d2809dSJeff LaBundy 		ati_target = &sys_reg->ch_reg_ulp.ati_target;
598f1d2809dSJeff LaBundy 		break;
599f1d2809dSJeff LaBundy 
600f1d2809dSJeff LaBundy 	case IQS626_CH_TP_2:
601f1d2809dSJeff LaBundy 	case IQS626_CH_TP_3:
602f1d2809dSJeff LaBundy 		ati_target = &sys_reg->tp_grp_reg.ati_target;
603f1d2809dSJeff LaBundy 		break;
604f1d2809dSJeff LaBundy 
605f1d2809dSJeff LaBundy 	case IQS626_CH_GEN_0:
606f1d2809dSJeff LaBundy 	case IQS626_CH_GEN_1:
607f1d2809dSJeff LaBundy 	case IQS626_CH_GEN_2:
608f1d2809dSJeff LaBundy 		i = ch_id - IQS626_CH_GEN_0;
609f1d2809dSJeff LaBundy 		ati_target = &sys_reg->ch_reg_gen[i].ati_target;
610f1d2809dSJeff LaBundy 		break;
611f1d2809dSJeff LaBundy 
612f1d2809dSJeff LaBundy 	case IQS626_CH_HALL:
613f1d2809dSJeff LaBundy 		ati_target = &sys_reg->ch_reg_hall.ati_target;
614f1d2809dSJeff LaBundy 		break;
615f1d2809dSJeff LaBundy 
616f1d2809dSJeff LaBundy 	default:
617f1d2809dSJeff LaBundy 		return -EINVAL;
618f1d2809dSJeff LaBundy 	}
619f1d2809dSJeff LaBundy 
620f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,ati-target", &val)) {
621f1d2809dSJeff LaBundy 		if (val > IQS626_CHx_ATI_TARGET_MAX) {
622f1d2809dSJeff LaBundy 			dev_err(&client->dev,
623f1d2809dSJeff LaBundy 				"Invalid %s channel ATI target: %u\n",
624f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
625f1d2809dSJeff LaBundy 			return -EINVAL;
626f1d2809dSJeff LaBundy 		}
627f1d2809dSJeff LaBundy 
628f1d2809dSJeff LaBundy 		*ati_target &= ~IQS626_CHx_ATI_TARGET_MASK;
629f1d2809dSJeff LaBundy 		*ati_target |= (val / 32);
630f1d2809dSJeff LaBundy 	}
631f1d2809dSJeff LaBundy 
632f1d2809dSJeff LaBundy 	if (ch_id != IQS626_CH_TP_2 && ch_id != IQS626_CH_TP_3 &&
633f1d2809dSJeff LaBundy 	    !fwnode_property_read_u32(ch_node, "azoteq,ati-base", &val)) {
634f1d2809dSJeff LaBundy 		switch (val) {
635f1d2809dSJeff LaBundy 		case 75:
636f1d2809dSJeff LaBundy 			val = IQS626_CHx_ATI_BASE_75;
637f1d2809dSJeff LaBundy 			break;
638f1d2809dSJeff LaBundy 
639f1d2809dSJeff LaBundy 		case 100:
640f1d2809dSJeff LaBundy 			val = IQS626_CHx_ATI_BASE_100;
641f1d2809dSJeff LaBundy 			break;
642f1d2809dSJeff LaBundy 
643f1d2809dSJeff LaBundy 		case 150:
644f1d2809dSJeff LaBundy 			val = IQS626_CHx_ATI_BASE_150;
645f1d2809dSJeff LaBundy 			break;
646f1d2809dSJeff LaBundy 
647f1d2809dSJeff LaBundy 		case 200:
648f1d2809dSJeff LaBundy 			val = IQS626_CHx_ATI_BASE_200;
649f1d2809dSJeff LaBundy 			break;
650f1d2809dSJeff LaBundy 
651f1d2809dSJeff LaBundy 		default:
652f1d2809dSJeff LaBundy 			dev_err(&client->dev,
653f1d2809dSJeff LaBundy 				"Invalid %s channel ATI base: %u\n",
654f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
655f1d2809dSJeff LaBundy 			return -EINVAL;
656f1d2809dSJeff LaBundy 		}
657f1d2809dSJeff LaBundy 
658f1d2809dSJeff LaBundy 		*ati_target &= ~IQS626_CHx_ATI_BASE_MASK;
659f1d2809dSJeff LaBundy 		*ati_target |= val;
660f1d2809dSJeff LaBundy 	}
661f1d2809dSJeff LaBundy 
662f1d2809dSJeff LaBundy 	return 0;
663f1d2809dSJeff LaBundy }
664f1d2809dSJeff LaBundy 
iqs626_parse_pins(struct iqs626_private * iqs626,struct fwnode_handle * ch_node,const char * propname,u8 * enable)665f1d2809dSJeff LaBundy static int iqs626_parse_pins(struct iqs626_private *iqs626,
6664d3d2694SJeff LaBundy 			     struct fwnode_handle *ch_node,
667f1d2809dSJeff LaBundy 			     const char *propname, u8 *enable)
668f1d2809dSJeff LaBundy {
669f1d2809dSJeff LaBundy 	struct i2c_client *client = iqs626->client;
670f1d2809dSJeff LaBundy 	unsigned int val[IQS626_NUM_CRx_TX];
671f1d2809dSJeff LaBundy 	int error, count, i;
672f1d2809dSJeff LaBundy 
673f1d2809dSJeff LaBundy 	if (!fwnode_property_present(ch_node, propname))
674f1d2809dSJeff LaBundy 		return 0;
675f1d2809dSJeff LaBundy 
676f1d2809dSJeff LaBundy 	count = fwnode_property_count_u32(ch_node, propname);
677f1d2809dSJeff LaBundy 	if (count > IQS626_NUM_CRx_TX) {
678f1d2809dSJeff LaBundy 		dev_err(&client->dev,
679f1d2809dSJeff LaBundy 			"Too many %s channel CRX/TX pins present\n",
680f1d2809dSJeff LaBundy 			fwnode_get_name(ch_node));
681f1d2809dSJeff LaBundy 		return -EINVAL;
682f1d2809dSJeff LaBundy 	} else if (count < 0) {
683f1d2809dSJeff LaBundy 		dev_err(&client->dev,
684f1d2809dSJeff LaBundy 			"Failed to count %s channel CRX/TX pins: %d\n",
685f1d2809dSJeff LaBundy 			fwnode_get_name(ch_node), count);
686f1d2809dSJeff LaBundy 		return count;
687f1d2809dSJeff LaBundy 	}
688f1d2809dSJeff LaBundy 
689f1d2809dSJeff LaBundy 	error = fwnode_property_read_u32_array(ch_node, propname, val, count);
690f1d2809dSJeff LaBundy 	if (error) {
691f1d2809dSJeff LaBundy 		dev_err(&client->dev,
692f1d2809dSJeff LaBundy 			"Failed to read %s channel CRX/TX pins: %d\n",
693f1d2809dSJeff LaBundy 			fwnode_get_name(ch_node), error);
694f1d2809dSJeff LaBundy 		return error;
695f1d2809dSJeff LaBundy 	}
696f1d2809dSJeff LaBundy 
697f1d2809dSJeff LaBundy 	*enable = 0;
698f1d2809dSJeff LaBundy 
699f1d2809dSJeff LaBundy 	for (i = 0; i < count; i++) {
700f1d2809dSJeff LaBundy 		if (val[i] >= IQS626_NUM_CRx_TX) {
701f1d2809dSJeff LaBundy 			dev_err(&client->dev,
702f1d2809dSJeff LaBundy 				"Invalid %s channel CRX/TX pin: %u\n",
703f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val[i]);
704f1d2809dSJeff LaBundy 			return -EINVAL;
705f1d2809dSJeff LaBundy 		}
706f1d2809dSJeff LaBundy 
707f1d2809dSJeff LaBundy 		*enable |= BIT(val[i]);
708f1d2809dSJeff LaBundy 	}
709f1d2809dSJeff LaBundy 
710f1d2809dSJeff LaBundy 	return 0;
711f1d2809dSJeff LaBundy }
712f1d2809dSJeff LaBundy 
iqs626_parse_trackpad(struct iqs626_private * iqs626,struct fwnode_handle * ch_node,enum iqs626_ch_id ch_id)713f1d2809dSJeff LaBundy static int iqs626_parse_trackpad(struct iqs626_private *iqs626,
7144d3d2694SJeff LaBundy 				 struct fwnode_handle *ch_node,
7154d3d2694SJeff LaBundy 				 enum iqs626_ch_id ch_id)
716f1d2809dSJeff LaBundy {
717f1d2809dSJeff LaBundy 	struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
718f1d2809dSJeff LaBundy 	struct i2c_client *client = iqs626->client;
719f1d2809dSJeff LaBundy 	u8 *hyst = &sys_reg->tp_grp_reg.hyst;
7204d3d2694SJeff LaBundy 	int error, count, i;
721f1d2809dSJeff LaBundy 	unsigned int val;
722f1d2809dSJeff LaBundy 
723f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,lta-update", &val)) {
724f1d2809dSJeff LaBundy 		if (val > IQS626_MISC_A_TPx_LTA_UPDATE_MAX) {
725f1d2809dSJeff LaBundy 			dev_err(&client->dev,
726f1d2809dSJeff LaBundy 				"Invalid %s channel update rate: %u\n",
727f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
728f1d2809dSJeff LaBundy 			return -EINVAL;
729f1d2809dSJeff LaBundy 		}
730f1d2809dSJeff LaBundy 
731f1d2809dSJeff LaBundy 		sys_reg->misc_a &= ~IQS626_MISC_A_TPx_LTA_UPDATE_MASK;
732f1d2809dSJeff LaBundy 		sys_reg->misc_a |= (val << IQS626_MISC_A_TPx_LTA_UPDATE_SHIFT);
733f1d2809dSJeff LaBundy 	}
734f1d2809dSJeff LaBundy 
735f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-trackpad",
736f1d2809dSJeff LaBundy 				      &val)) {
737f1d2809dSJeff LaBundy 		if (val > IQS626_FILT_STR_MAX) {
738f1d2809dSJeff LaBundy 			dev_err(&client->dev,
739f1d2809dSJeff LaBundy 				"Invalid %s channel filter strength: %u\n",
740f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
741f1d2809dSJeff LaBundy 			return -EINVAL;
742f1d2809dSJeff LaBundy 		}
743f1d2809dSJeff LaBundy 
744f1d2809dSJeff LaBundy 		sys_reg->misc_b &= ~IQS626_MISC_B_FILT_STR_TPx;
745f1d2809dSJeff LaBundy 		sys_reg->misc_b |= val;
746f1d2809dSJeff LaBundy 	}
747f1d2809dSJeff LaBundy 
748f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-np-cnt",
749f1d2809dSJeff LaBundy 				      &val)) {
750f1d2809dSJeff LaBundy 		if (val > IQS626_FILT_STR_MAX) {
751f1d2809dSJeff LaBundy 			dev_err(&client->dev,
752f1d2809dSJeff LaBundy 				"Invalid %s channel filter strength: %u\n",
753f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
754f1d2809dSJeff LaBundy 			return -EINVAL;
755f1d2809dSJeff LaBundy 		}
756f1d2809dSJeff LaBundy 
757f1d2809dSJeff LaBundy 		*hyst &= ~IQS626_FILT_STR_NP_TPx_MASK;
758f1d2809dSJeff LaBundy 		*hyst |= (val << IQS626_FILT_STR_NP_TPx_SHIFT);
759f1d2809dSJeff LaBundy 	}
760f1d2809dSJeff LaBundy 
761f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-lp-cnt",
762f1d2809dSJeff LaBundy 				      &val)) {
763f1d2809dSJeff LaBundy 		if (val > IQS626_FILT_STR_MAX) {
764f1d2809dSJeff LaBundy 			dev_err(&client->dev,
765f1d2809dSJeff LaBundy 				"Invalid %s channel filter strength: %u\n",
766f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
767f1d2809dSJeff LaBundy 			return -EINVAL;
768f1d2809dSJeff LaBundy 		}
769f1d2809dSJeff LaBundy 
770f1d2809dSJeff LaBundy 		*hyst &= ~IQS626_FILT_STR_LP_TPx_MASK;
771f1d2809dSJeff LaBundy 		*hyst |= (val << IQS626_FILT_STR_LP_TPx_SHIFT);
772f1d2809dSJeff LaBundy 	}
773f1d2809dSJeff LaBundy 
7744d3d2694SJeff LaBundy 	for (i = 0; i < iqs626_channels[ch_id].num_ch; i++) {
7754d3d2694SJeff LaBundy 		u8 *ati_base = &sys_reg->tp_grp_reg.ch_reg_tp[i].ati_base;
7764d3d2694SJeff LaBundy 		u8 *thresh = &sys_reg->tp_grp_reg.ch_reg_tp[i].thresh;
7774d3d2694SJeff LaBundy 		struct fwnode_handle *tc_node;
7784d3d2694SJeff LaBundy 		char tc_name[10];
7794d3d2694SJeff LaBundy 
7804d3d2694SJeff LaBundy 		snprintf(tc_name, sizeof(tc_name), "channel-%d", i);
7814d3d2694SJeff LaBundy 
7824d3d2694SJeff LaBundy 		tc_node = fwnode_get_named_child_node(ch_node, tc_name);
7834d3d2694SJeff LaBundy 		if (!tc_node)
7844d3d2694SJeff LaBundy 			continue;
7854d3d2694SJeff LaBundy 
7864d3d2694SJeff LaBundy 		if (!fwnode_property_read_u32(tc_node, "azoteq,ati-base",
7874d3d2694SJeff LaBundy 					      &val)) {
7884d3d2694SJeff LaBundy 			if (val < IQS626_TPx_ATI_BASE_MIN ||
7894d3d2694SJeff LaBundy 			    val > IQS626_TPx_ATI_BASE_MAX) {
7904d3d2694SJeff LaBundy 				dev_err(&client->dev,
7914d3d2694SJeff LaBundy 					"Invalid %s %s ATI base: %u\n",
7924d3d2694SJeff LaBundy 					fwnode_get_name(ch_node), tc_name, val);
7934d3d2694SJeff LaBundy 				fwnode_handle_put(tc_node);
7944d3d2694SJeff LaBundy 				return -EINVAL;
7954d3d2694SJeff LaBundy 			}
7964d3d2694SJeff LaBundy 
7974d3d2694SJeff LaBundy 			*ati_base = val - IQS626_TPx_ATI_BASE_MIN;
7984d3d2694SJeff LaBundy 		}
7994d3d2694SJeff LaBundy 
8004d3d2694SJeff LaBundy 		if (!fwnode_property_read_u32(tc_node, "azoteq,thresh",
8014d3d2694SJeff LaBundy 					      &val)) {
8024d3d2694SJeff LaBundy 			if (val > IQS626_CHx_THRESH_MAX) {
8034d3d2694SJeff LaBundy 				dev_err(&client->dev,
8044d3d2694SJeff LaBundy 					"Invalid %s %s threshold: %u\n",
8054d3d2694SJeff LaBundy 					fwnode_get_name(ch_node), tc_name, val);
8064d3d2694SJeff LaBundy 				fwnode_handle_put(tc_node);
8074d3d2694SJeff LaBundy 				return -EINVAL;
8084d3d2694SJeff LaBundy 			}
8094d3d2694SJeff LaBundy 
8104d3d2694SJeff LaBundy 			*thresh = val;
8114d3d2694SJeff LaBundy 		}
8124d3d2694SJeff LaBundy 
8134d3d2694SJeff LaBundy 		fwnode_handle_put(tc_node);
8144d3d2694SJeff LaBundy 	}
8154d3d2694SJeff LaBundy 
816f1d2809dSJeff LaBundy 	if (!fwnode_property_present(ch_node, "linux,keycodes"))
817f1d2809dSJeff LaBundy 		return 0;
818f1d2809dSJeff LaBundy 
819f1d2809dSJeff LaBundy 	count = fwnode_property_count_u32(ch_node, "linux,keycodes");
820f1d2809dSJeff LaBundy 	if (count > IQS626_NUM_GESTURES) {
821f1d2809dSJeff LaBundy 		dev_err(&client->dev, "Too many keycodes present\n");
822f1d2809dSJeff LaBundy 		return -EINVAL;
823f1d2809dSJeff LaBundy 	} else if (count < 0) {
824f1d2809dSJeff LaBundy 		dev_err(&client->dev, "Failed to count keycodes: %d\n", count);
825f1d2809dSJeff LaBundy 		return count;
826f1d2809dSJeff LaBundy 	}
827f1d2809dSJeff LaBundy 
828f1d2809dSJeff LaBundy 	error = fwnode_property_read_u32_array(ch_node, "linux,keycodes",
829f1d2809dSJeff LaBundy 					       iqs626->tp_code, count);
830f1d2809dSJeff LaBundy 	if (error) {
831f1d2809dSJeff LaBundy 		dev_err(&client->dev, "Failed to read keycodes: %d\n", error);
832f1d2809dSJeff LaBundy 		return error;
833f1d2809dSJeff LaBundy 	}
834f1d2809dSJeff LaBundy 
835f1d2809dSJeff LaBundy 	sys_reg->misc_b &= ~IQS626_MISC_B_TPx_SWIPE;
836f1d2809dSJeff LaBundy 	if (fwnode_property_present(ch_node, "azoteq,gesture-swipe"))
837f1d2809dSJeff LaBundy 		sys_reg->misc_b |= IQS626_MISC_B_TPx_SWIPE;
838f1d2809dSJeff LaBundy 
839f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,timeout-tap-ms",
840f1d2809dSJeff LaBundy 				      &val)) {
841f1d2809dSJeff LaBundy 		if (val > IQS626_TIMEOUT_TAP_MS_MAX) {
842f1d2809dSJeff LaBundy 			dev_err(&client->dev,
843f1d2809dSJeff LaBundy 				"Invalid %s channel timeout: %u\n",
844f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
845f1d2809dSJeff LaBundy 			return -EINVAL;
846f1d2809dSJeff LaBundy 		}
847f1d2809dSJeff LaBundy 
848f1d2809dSJeff LaBundy 		sys_reg->timeout_tap = val / 16;
849f1d2809dSJeff LaBundy 	}
850f1d2809dSJeff LaBundy 
851f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,timeout-swipe-ms",
852f1d2809dSJeff LaBundy 				      &val)) {
853f1d2809dSJeff LaBundy 		if (val > IQS626_TIMEOUT_SWIPE_MS_MAX) {
854f1d2809dSJeff LaBundy 			dev_err(&client->dev,
855f1d2809dSJeff LaBundy 				"Invalid %s channel timeout: %u\n",
856f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
857f1d2809dSJeff LaBundy 			return -EINVAL;
858f1d2809dSJeff LaBundy 		}
859f1d2809dSJeff LaBundy 
860f1d2809dSJeff LaBundy 		sys_reg->timeout_swipe = val / 16;
861f1d2809dSJeff LaBundy 	}
862f1d2809dSJeff LaBundy 
863f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,thresh-swipe",
864f1d2809dSJeff LaBundy 				      &val)) {
865f1d2809dSJeff LaBundy 		if (val > IQS626_THRESH_SWIPE_MAX) {
866f1d2809dSJeff LaBundy 			dev_err(&client->dev,
867f1d2809dSJeff LaBundy 				"Invalid %s channel threshold: %u\n",
868f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
869f1d2809dSJeff LaBundy 			return -EINVAL;
870f1d2809dSJeff LaBundy 		}
871f1d2809dSJeff LaBundy 
872f1d2809dSJeff LaBundy 		sys_reg->thresh_swipe = val;
873f1d2809dSJeff LaBundy 	}
874f1d2809dSJeff LaBundy 
875f1d2809dSJeff LaBundy 	sys_reg->event_mask &= ~IQS626_EVENT_MASK_GESTURE;
876f1d2809dSJeff LaBundy 
877f1d2809dSJeff LaBundy 	return 0;
878f1d2809dSJeff LaBundy }
879f1d2809dSJeff LaBundy 
880e1f5e848SJeff LaBundy static noinline_for_stack int
iqs626_parse_channel(struct iqs626_private * iqs626,struct fwnode_handle * ch_node,enum iqs626_ch_id ch_id)881e1f5e848SJeff LaBundy iqs626_parse_channel(struct iqs626_private *iqs626,
8824d3d2694SJeff LaBundy 		     struct fwnode_handle *ch_node, enum iqs626_ch_id ch_id)
883f1d2809dSJeff LaBundy {
884f1d2809dSJeff LaBundy 	struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
885f1d2809dSJeff LaBundy 	struct i2c_client *client = iqs626->client;
886f1d2809dSJeff LaBundy 	u8 *engine, *filter, *rx_enable, *tx_enable;
887f1d2809dSJeff LaBundy 	u8 *assoc_select, *assoc_weight;
888f1d2809dSJeff LaBundy 	unsigned int val;
889f1d2809dSJeff LaBundy 	int error, i;
890f1d2809dSJeff LaBundy 
891f1d2809dSJeff LaBundy 	switch (ch_id) {
892f1d2809dSJeff LaBundy 	case IQS626_CH_ULP_0:
893f1d2809dSJeff LaBundy 		engine = sys_reg->ch_reg_ulp.engine;
894f1d2809dSJeff LaBundy 		break;
895f1d2809dSJeff LaBundy 
896f1d2809dSJeff LaBundy 	case IQS626_CH_TP_2:
897f1d2809dSJeff LaBundy 	case IQS626_CH_TP_3:
898f1d2809dSJeff LaBundy 		engine = sys_reg->tp_grp_reg.engine;
899f1d2809dSJeff LaBundy 		break;
900f1d2809dSJeff LaBundy 
901f1d2809dSJeff LaBundy 	case IQS626_CH_GEN_0:
902f1d2809dSJeff LaBundy 	case IQS626_CH_GEN_1:
903f1d2809dSJeff LaBundy 	case IQS626_CH_GEN_2:
904f1d2809dSJeff LaBundy 		i = ch_id - IQS626_CH_GEN_0;
905f1d2809dSJeff LaBundy 		engine = sys_reg->ch_reg_gen[i].engine;
906f1d2809dSJeff LaBundy 		break;
907f1d2809dSJeff LaBundy 
908f1d2809dSJeff LaBundy 	case IQS626_CH_HALL:
909f1d2809dSJeff LaBundy 		engine = &sys_reg->ch_reg_hall.engine;
910f1d2809dSJeff LaBundy 		break;
911f1d2809dSJeff LaBundy 
912f1d2809dSJeff LaBundy 	default:
913f1d2809dSJeff LaBundy 		return -EINVAL;
914f1d2809dSJeff LaBundy 	}
915f1d2809dSJeff LaBundy 
9164d3d2694SJeff LaBundy 	error = iqs626_parse_ati_target(iqs626, ch_node, ch_id);
9174d3d2694SJeff LaBundy 	if (error)
9184d3d2694SJeff LaBundy 		return error;
9194d3d2694SJeff LaBundy 
9204d3d2694SJeff LaBundy 	error = iqs626_parse_events(iqs626, ch_node, ch_id);
9214d3d2694SJeff LaBundy 	if (error)
9224d3d2694SJeff LaBundy 		return error;
9234d3d2694SJeff LaBundy 
9244d3d2694SJeff LaBundy 	if (!fwnode_property_present(ch_node, "azoteq,ati-exclude"))
9254d3d2694SJeff LaBundy 		sys_reg->redo_ati |= iqs626_channels[ch_id].active;
9264d3d2694SJeff LaBundy 
9274d3d2694SJeff LaBundy 	if (!fwnode_property_present(ch_node, "azoteq,reseed-disable"))
9284d3d2694SJeff LaBundy 		sys_reg->reseed |= iqs626_channels[ch_id].active;
9294d3d2694SJeff LaBundy 
930f1d2809dSJeff LaBundy 	*engine |= IQS626_CHx_ENG_0_MEAS_CAP_SIZE;
931f1d2809dSJeff LaBundy 	if (fwnode_property_present(ch_node, "azoteq,meas-cap-decrease"))
932f1d2809dSJeff LaBundy 		*engine &= ~IQS626_CHx_ENG_0_MEAS_CAP_SIZE;
933f1d2809dSJeff LaBundy 
934f1d2809dSJeff LaBundy 	*engine |= IQS626_CHx_ENG_0_RX_TERM_VSS;
935f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,rx-inactive", &val)) {
936f1d2809dSJeff LaBundy 		switch (val) {
937f1d2809dSJeff LaBundy 		case IQS626_RX_INACTIVE_VSS:
938f1d2809dSJeff LaBundy 			break;
939f1d2809dSJeff LaBundy 
940f1d2809dSJeff LaBundy 		case IQS626_RX_INACTIVE_FLOAT:
941f1d2809dSJeff LaBundy 			*engine &= ~IQS626_CHx_ENG_0_RX_TERM_VSS;
942f1d2809dSJeff LaBundy 			if (ch_id == IQS626_CH_GEN_0 ||
943f1d2809dSJeff LaBundy 			    ch_id == IQS626_CH_GEN_1 ||
944f1d2809dSJeff LaBundy 			    ch_id == IQS626_CH_GEN_2)
945f1d2809dSJeff LaBundy 				*(engine + 4) &= ~IQS626_CHx_ENG_4_RX_TERM_VREG;
946f1d2809dSJeff LaBundy 			break;
947f1d2809dSJeff LaBundy 
948f1d2809dSJeff LaBundy 		case IQS626_RX_INACTIVE_VREG:
949f1d2809dSJeff LaBundy 			if (ch_id == IQS626_CH_GEN_0 ||
950f1d2809dSJeff LaBundy 			    ch_id == IQS626_CH_GEN_1 ||
951f1d2809dSJeff LaBundy 			    ch_id == IQS626_CH_GEN_2) {
952f1d2809dSJeff LaBundy 				*engine &= ~IQS626_CHx_ENG_0_RX_TERM_VSS;
953f1d2809dSJeff LaBundy 				*(engine + 4) |= IQS626_CHx_ENG_4_RX_TERM_VREG;
954f1d2809dSJeff LaBundy 				break;
955f1d2809dSJeff LaBundy 			}
956f1d2809dSJeff LaBundy 			fallthrough;
957f1d2809dSJeff LaBundy 
958f1d2809dSJeff LaBundy 		default:
959f1d2809dSJeff LaBundy 			dev_err(&client->dev,
960f1d2809dSJeff LaBundy 				"Invalid %s channel CRX pin termination: %u\n",
961f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
962f1d2809dSJeff LaBundy 			return -EINVAL;
963f1d2809dSJeff LaBundy 		}
964f1d2809dSJeff LaBundy 	}
965f1d2809dSJeff LaBundy 
966f1d2809dSJeff LaBundy 	*engine &= ~IQS626_CHx_ENG_0_LINEARIZE;
967f1d2809dSJeff LaBundy 	if (fwnode_property_present(ch_node, "azoteq,linearize"))
968f1d2809dSJeff LaBundy 		*engine |= IQS626_CHx_ENG_0_LINEARIZE;
969f1d2809dSJeff LaBundy 
970f1d2809dSJeff LaBundy 	*engine &= ~IQS626_CHx_ENG_0_DUAL_DIR;
971f1d2809dSJeff LaBundy 	if (fwnode_property_present(ch_node, "azoteq,dual-direction"))
972f1d2809dSJeff LaBundy 		*engine |= IQS626_CHx_ENG_0_DUAL_DIR;
973f1d2809dSJeff LaBundy 
974f1d2809dSJeff LaBundy 	*engine &= ~IQS626_CHx_ENG_0_FILT_DISABLE;
975f1d2809dSJeff LaBundy 	if (fwnode_property_present(ch_node, "azoteq,filt-disable"))
976f1d2809dSJeff LaBundy 		*engine |= IQS626_CHx_ENG_0_FILT_DISABLE;
977f1d2809dSJeff LaBundy 
978f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,ati-mode", &val)) {
979f1d2809dSJeff LaBundy 		if (val > IQS626_CHx_ENG_0_ATI_MODE_MAX) {
980f1d2809dSJeff LaBundy 			dev_err(&client->dev,
981f1d2809dSJeff LaBundy 				"Invalid %s channel ATI mode: %u\n",
982f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
983f1d2809dSJeff LaBundy 			return -EINVAL;
984f1d2809dSJeff LaBundy 		}
985f1d2809dSJeff LaBundy 
986f1d2809dSJeff LaBundy 		*engine &= ~IQS626_CHx_ENG_0_ATI_MODE_MASK;
987f1d2809dSJeff LaBundy 		*engine |= val;
988f1d2809dSJeff LaBundy 	}
989f1d2809dSJeff LaBundy 
990f1d2809dSJeff LaBundy 	if (ch_id == IQS626_CH_HALL)
991f1d2809dSJeff LaBundy 		return 0;
992f1d2809dSJeff LaBundy 
993f1d2809dSJeff LaBundy 	*(engine + 1) &= ~IQS626_CHx_ENG_1_CCT_ENABLE;
994f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,cct-increase",
995f1d2809dSJeff LaBundy 				      &val) && val) {
996f1d2809dSJeff LaBundy 		unsigned int orig_val = val--;
997f1d2809dSJeff LaBundy 
998f1d2809dSJeff LaBundy 		/*
999f1d2809dSJeff LaBundy 		 * In the case of the generic channels, the charge cycle time
1000f1d2809dSJeff LaBundy 		 * field doubles in size and straddles two separate registers.
1001f1d2809dSJeff LaBundy 		 */
1002f1d2809dSJeff LaBundy 		if (ch_id == IQS626_CH_GEN_0 ||
1003f1d2809dSJeff LaBundy 		    ch_id == IQS626_CH_GEN_1 ||
1004f1d2809dSJeff LaBundy 		    ch_id == IQS626_CH_GEN_2) {
1005f1d2809dSJeff LaBundy 			*(engine + 4) &= ~IQS626_CHx_ENG_4_CCT_LOW_1;
1006f1d2809dSJeff LaBundy 			if (val & BIT(1))
1007f1d2809dSJeff LaBundy 				*(engine + 4) |= IQS626_CHx_ENG_4_CCT_LOW_1;
1008f1d2809dSJeff LaBundy 
1009f1d2809dSJeff LaBundy 			*(engine + 4) &= ~IQS626_CHx_ENG_4_CCT_LOW_0;
1010f1d2809dSJeff LaBundy 			if (val & BIT(0))
1011f1d2809dSJeff LaBundy 				*(engine + 4) |= IQS626_CHx_ENG_4_CCT_LOW_0;
1012f1d2809dSJeff LaBundy 
1013f1d2809dSJeff LaBundy 			val >>= 2;
1014f1d2809dSJeff LaBundy 		}
1015f1d2809dSJeff LaBundy 
1016f1d2809dSJeff LaBundy 		if (val & ~GENMASK(1, 0)) {
1017f1d2809dSJeff LaBundy 			dev_err(&client->dev,
1018f1d2809dSJeff LaBundy 				"Invalid %s channel charge cycle time: %u\n",
1019f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), orig_val);
1020f1d2809dSJeff LaBundy 			return -EINVAL;
1021f1d2809dSJeff LaBundy 		}
1022f1d2809dSJeff LaBundy 
1023f1d2809dSJeff LaBundy 		*(engine + 1) &= ~IQS626_CHx_ENG_1_CCT_HIGH_1;
1024f1d2809dSJeff LaBundy 		if (val & BIT(1))
1025f1d2809dSJeff LaBundy 			*(engine + 1) |= IQS626_CHx_ENG_1_CCT_HIGH_1;
1026f1d2809dSJeff LaBundy 
1027f1d2809dSJeff LaBundy 		*(engine + 1) &= ~IQS626_CHx_ENG_1_CCT_HIGH_0;
1028f1d2809dSJeff LaBundy 		if (val & BIT(0))
1029f1d2809dSJeff LaBundy 			*(engine + 1) |= IQS626_CHx_ENG_1_CCT_HIGH_0;
1030f1d2809dSJeff LaBundy 
1031f1d2809dSJeff LaBundy 		*(engine + 1) |= IQS626_CHx_ENG_1_CCT_ENABLE;
1032f1d2809dSJeff LaBundy 	}
1033f1d2809dSJeff LaBundy 
1034f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,proj-bias", &val)) {
1035f1d2809dSJeff LaBundy 		if (val > IQS626_CHx_ENG_1_PROJ_BIAS_MAX) {
1036f1d2809dSJeff LaBundy 			dev_err(&client->dev,
1037f1d2809dSJeff LaBundy 				"Invalid %s channel bias current: %u\n",
1038f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
1039f1d2809dSJeff LaBundy 			return -EINVAL;
1040f1d2809dSJeff LaBundy 		}
1041f1d2809dSJeff LaBundy 
1042f1d2809dSJeff LaBundy 		*(engine + 1) &= ~IQS626_CHx_ENG_1_PROJ_BIAS_MASK;
1043f1d2809dSJeff LaBundy 		*(engine + 1) |= (val << IQS626_CHx_ENG_1_PROJ_BIAS_SHIFT);
1044f1d2809dSJeff LaBundy 	}
1045f1d2809dSJeff LaBundy 
1046f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,sense-freq", &val)) {
1047f1d2809dSJeff LaBundy 		if (val > IQS626_CHx_ENG_1_SENSE_FREQ_MAX) {
1048f1d2809dSJeff LaBundy 			dev_err(&client->dev,
1049f1d2809dSJeff LaBundy 				"Invalid %s channel sensing frequency: %u\n",
1050f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
1051f1d2809dSJeff LaBundy 			return -EINVAL;
1052f1d2809dSJeff LaBundy 		}
1053f1d2809dSJeff LaBundy 
1054f1d2809dSJeff LaBundy 		*(engine + 1) &= ~IQS626_CHx_ENG_1_SENSE_FREQ_MASK;
1055f1d2809dSJeff LaBundy 		*(engine + 1) |= (val << IQS626_CHx_ENG_1_SENSE_FREQ_SHIFT);
1056f1d2809dSJeff LaBundy 	}
1057f1d2809dSJeff LaBundy 
1058f1d2809dSJeff LaBundy 	*(engine + 1) &= ~IQS626_CHx_ENG_1_ATI_BAND_TIGHTEN;
1059f1d2809dSJeff LaBundy 	if (fwnode_property_present(ch_node, "azoteq,ati-band-tighten"))
1060f1d2809dSJeff LaBundy 		*(engine + 1) |= IQS626_CHx_ENG_1_ATI_BAND_TIGHTEN;
1061f1d2809dSJeff LaBundy 
1062f1d2809dSJeff LaBundy 	if (ch_id == IQS626_CH_TP_2 || ch_id == IQS626_CH_TP_3)
10634d3d2694SJeff LaBundy 		return iqs626_parse_trackpad(iqs626, ch_node, ch_id);
1064f1d2809dSJeff LaBundy 
1065f1d2809dSJeff LaBundy 	if (ch_id == IQS626_CH_ULP_0) {
1066f1d2809dSJeff LaBundy 		sys_reg->ch_reg_ulp.hyst &= ~IQS626_ULP_PROJ_ENABLE;
1067f1d2809dSJeff LaBundy 		if (fwnode_property_present(ch_node, "azoteq,proj-enable"))
1068f1d2809dSJeff LaBundy 			sys_reg->ch_reg_ulp.hyst |= IQS626_ULP_PROJ_ENABLE;
1069f1d2809dSJeff LaBundy 
1070f1d2809dSJeff LaBundy 		filter = &sys_reg->ch_reg_ulp.filter;
1071f1d2809dSJeff LaBundy 
1072f1d2809dSJeff LaBundy 		rx_enable = &sys_reg->ch_reg_ulp.rx_enable;
1073f1d2809dSJeff LaBundy 		tx_enable = &sys_reg->ch_reg_ulp.tx_enable;
1074f1d2809dSJeff LaBundy 	} else {
1075f1d2809dSJeff LaBundy 		i = ch_id - IQS626_CH_GEN_0;
1076f1d2809dSJeff LaBundy 		filter = &sys_reg->ch_reg_gen[i].filter;
1077f1d2809dSJeff LaBundy 
1078f1d2809dSJeff LaBundy 		rx_enable = &sys_reg->ch_reg_gen[i].rx_enable;
1079f1d2809dSJeff LaBundy 		tx_enable = &sys_reg->ch_reg_gen[i].tx_enable;
1080f1d2809dSJeff LaBundy 	}
1081f1d2809dSJeff LaBundy 
1082f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-np-cnt",
1083f1d2809dSJeff LaBundy 				      &val)) {
1084f1d2809dSJeff LaBundy 		if (val > IQS626_FILT_STR_MAX) {
1085f1d2809dSJeff LaBundy 			dev_err(&client->dev,
1086f1d2809dSJeff LaBundy 				"Invalid %s channel filter strength: %u\n",
1087f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
1088f1d2809dSJeff LaBundy 			return -EINVAL;
1089f1d2809dSJeff LaBundy 		}
1090f1d2809dSJeff LaBundy 
1091f1d2809dSJeff LaBundy 		*filter &= ~IQS626_FILT_STR_NP_CNT_MASK;
1092f1d2809dSJeff LaBundy 		*filter |= (val << IQS626_FILT_STR_NP_CNT_SHIFT);
1093f1d2809dSJeff LaBundy 	}
1094f1d2809dSJeff LaBundy 
1095f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-lp-cnt",
1096f1d2809dSJeff LaBundy 				      &val)) {
1097f1d2809dSJeff LaBundy 		if (val > IQS626_FILT_STR_MAX) {
1098f1d2809dSJeff LaBundy 			dev_err(&client->dev,
1099f1d2809dSJeff LaBundy 				"Invalid %s channel filter strength: %u\n",
1100f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
1101f1d2809dSJeff LaBundy 			return -EINVAL;
1102f1d2809dSJeff LaBundy 		}
1103f1d2809dSJeff LaBundy 
1104f1d2809dSJeff LaBundy 		*filter &= ~IQS626_FILT_STR_LP_CNT_MASK;
1105f1d2809dSJeff LaBundy 		*filter |= (val << IQS626_FILT_STR_LP_CNT_SHIFT);
1106f1d2809dSJeff LaBundy 	}
1107f1d2809dSJeff LaBundy 
1108f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-np-lta",
1109f1d2809dSJeff LaBundy 				      &val)) {
1110f1d2809dSJeff LaBundy 		if (val > IQS626_FILT_STR_MAX) {
1111f1d2809dSJeff LaBundy 			dev_err(&client->dev,
1112f1d2809dSJeff LaBundy 				"Invalid %s channel filter strength: %u\n",
1113f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
1114f1d2809dSJeff LaBundy 			return -EINVAL;
1115f1d2809dSJeff LaBundy 		}
1116f1d2809dSJeff LaBundy 
1117f1d2809dSJeff LaBundy 		*filter &= ~IQS626_FILT_STR_NP_LTA_MASK;
1118f1d2809dSJeff LaBundy 		*filter |= (val << IQS626_FILT_STR_NP_LTA_SHIFT);
1119f1d2809dSJeff LaBundy 	}
1120f1d2809dSJeff LaBundy 
1121f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-lp-lta",
1122f1d2809dSJeff LaBundy 				      &val)) {
1123f1d2809dSJeff LaBundy 		if (val > IQS626_FILT_STR_MAX) {
1124f1d2809dSJeff LaBundy 			dev_err(&client->dev,
1125f1d2809dSJeff LaBundy 				"Invalid %s channel filter strength: %u\n",
1126f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
1127f1d2809dSJeff LaBundy 			return -EINVAL;
1128f1d2809dSJeff LaBundy 		}
1129f1d2809dSJeff LaBundy 
1130f1d2809dSJeff LaBundy 		*filter &= ~IQS626_FILT_STR_LP_LTA_MASK;
1131f1d2809dSJeff LaBundy 		*filter |= val;
1132f1d2809dSJeff LaBundy 	}
1133f1d2809dSJeff LaBundy 
1134f1d2809dSJeff LaBundy 	error = iqs626_parse_pins(iqs626, ch_node, "azoteq,rx-enable",
1135f1d2809dSJeff LaBundy 				  rx_enable);
1136f1d2809dSJeff LaBundy 	if (error)
1137f1d2809dSJeff LaBundy 		return error;
1138f1d2809dSJeff LaBundy 
1139f1d2809dSJeff LaBundy 	error = iqs626_parse_pins(iqs626, ch_node, "azoteq,tx-enable",
1140f1d2809dSJeff LaBundy 				  tx_enable);
1141f1d2809dSJeff LaBundy 	if (error)
1142f1d2809dSJeff LaBundy 		return error;
1143f1d2809dSJeff LaBundy 
1144f1d2809dSJeff LaBundy 	if (ch_id == IQS626_CH_ULP_0)
1145f1d2809dSJeff LaBundy 		return 0;
1146f1d2809dSJeff LaBundy 
1147f1d2809dSJeff LaBundy 	*(engine + 2) &= ~IQS626_CHx_ENG_2_LOCAL_CAP_ENABLE;
1148f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,local-cap-size",
1149f1d2809dSJeff LaBundy 				      &val) && val) {
1150f1d2809dSJeff LaBundy 		unsigned int orig_val = val--;
1151f1d2809dSJeff LaBundy 
1152f1d2809dSJeff LaBundy 		if (val > IQS626_CHx_ENG_2_LOCAL_CAP_MAX) {
1153f1d2809dSJeff LaBundy 			dev_err(&client->dev,
1154f1d2809dSJeff LaBundy 				"Invalid %s channel local cap. size: %u\n",
1155f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), orig_val);
1156f1d2809dSJeff LaBundy 			return -EINVAL;
1157f1d2809dSJeff LaBundy 		}
1158f1d2809dSJeff LaBundy 
1159f1d2809dSJeff LaBundy 		*(engine + 2) &= ~IQS626_CHx_ENG_2_LOCAL_CAP_MASK;
1160f1d2809dSJeff LaBundy 		*(engine + 2) |= (val << IQS626_CHx_ENG_2_LOCAL_CAP_SHIFT);
1161f1d2809dSJeff LaBundy 
1162f1d2809dSJeff LaBundy 		*(engine + 2) |= IQS626_CHx_ENG_2_LOCAL_CAP_ENABLE;
1163f1d2809dSJeff LaBundy 	}
1164f1d2809dSJeff LaBundy 
1165f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,sense-mode", &val)) {
1166f1d2809dSJeff LaBundy 		if (val > IQS626_CHx_ENG_2_SENSE_MODE_MAX) {
1167f1d2809dSJeff LaBundy 			dev_err(&client->dev,
1168f1d2809dSJeff LaBundy 				"Invalid %s channel sensing mode: %u\n",
1169f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
1170f1d2809dSJeff LaBundy 			return -EINVAL;
1171f1d2809dSJeff LaBundy 		}
1172f1d2809dSJeff LaBundy 
1173f1d2809dSJeff LaBundy 		*(engine + 2) &= ~IQS626_CHx_ENG_2_SENSE_MODE_MASK;
1174f1d2809dSJeff LaBundy 		*(engine + 2) |= val;
1175f1d2809dSJeff LaBundy 	}
1176f1d2809dSJeff LaBundy 
1177f1d2809dSJeff LaBundy 	if (!fwnode_property_read_u32(ch_node, "azoteq,tx-freq", &val)) {
1178f1d2809dSJeff LaBundy 		if (val > IQS626_CHx_ENG_3_TX_FREQ_MAX) {
1179f1d2809dSJeff LaBundy 			dev_err(&client->dev,
1180f1d2809dSJeff LaBundy 				"Invalid %s channel excitation frequency: %u\n",
1181f1d2809dSJeff LaBundy 				fwnode_get_name(ch_node), val);
1182f1d2809dSJeff LaBundy 			return -EINVAL;
1183f1d2809dSJeff LaBundy 		}
1184f1d2809dSJeff LaBundy 
1185f1d2809dSJeff LaBundy 		*(engine + 3) &= ~IQS626_CHx_ENG_3_TX_FREQ_MASK;
1186f1d2809dSJeff LaBundy 		*(engine + 3) |= (val << IQS626_CHx_ENG_3_TX_FREQ_SHIFT);
1187f1d2809dSJeff LaBundy 	}
1188f1d2809dSJeff LaBundy 
1189f1d2809dSJeff LaBundy 	*(engine + 3) &= ~IQS626_CHx_ENG_3_INV_LOGIC;
1190f1d2809dSJeff LaBundy 	if (fwnode_property_present(ch_node, "azoteq,invert-enable"))
1191f1d2809dSJeff LaBundy 		*(engine + 3) |= IQS626_CHx_ENG_3_INV_LOGIC;
1192f1d2809dSJeff LaBundy 
1193f1d2809dSJeff LaBundy 	*(engine + 4) &= ~IQS626_CHx_ENG_4_COMP_DISABLE;
1194f1d2809dSJeff LaBundy 	if (fwnode_property_present(ch_node, "azoteq,comp-disable"))
1195f1d2809dSJeff LaBundy 		*(engine + 4) |= IQS626_CHx_ENG_4_COMP_DISABLE;
1196f1d2809dSJeff LaBundy 
1197f1d2809dSJeff LaBundy 	*(engine + 4) &= ~IQS626_CHx_ENG_4_STATIC_ENABLE;
1198f1d2809dSJeff LaBundy 	if (fwnode_property_present(ch_node, "azoteq,static-enable"))
1199f1d2809dSJeff LaBundy 		*(engine + 4) |= IQS626_CHx_ENG_4_STATIC_ENABLE;
1200f1d2809dSJeff LaBundy 
1201f1d2809dSJeff LaBundy 	i = ch_id - IQS626_CH_GEN_0;
1202f1d2809dSJeff LaBundy 	assoc_select = &sys_reg->ch_reg_gen[i].assoc_select;
1203f1d2809dSJeff LaBundy 	assoc_weight = &sys_reg->ch_reg_gen[i].assoc_weight;
1204f1d2809dSJeff LaBundy 
1205f1d2809dSJeff LaBundy 	*assoc_select = 0;
1206f1d2809dSJeff LaBundy 	if (!fwnode_property_present(ch_node, "azoteq,assoc-select"))
1207f1d2809dSJeff LaBundy 		return 0;
1208f1d2809dSJeff LaBundy 
1209f1d2809dSJeff LaBundy 	for (i = 0; i < ARRAY_SIZE(iqs626_channels); i++) {
1210f1d2809dSJeff LaBundy 		if (fwnode_property_match_string(ch_node, "azoteq,assoc-select",
1211f1d2809dSJeff LaBundy 						 iqs626_channels[i].name) < 0)
1212f1d2809dSJeff LaBundy 			continue;
1213f1d2809dSJeff LaBundy 
1214f1d2809dSJeff LaBundy 		*assoc_select |= iqs626_channels[i].active;
1215f1d2809dSJeff LaBundy 	}
1216f1d2809dSJeff LaBundy 
1217f1d2809dSJeff LaBundy 	if (fwnode_property_read_u32(ch_node, "azoteq,assoc-weight", &val))
1218f1d2809dSJeff LaBundy 		return 0;
1219f1d2809dSJeff LaBundy 
1220f1d2809dSJeff LaBundy 	if (val > IQS626_GEN_WEIGHT_MAX) {
1221f1d2809dSJeff LaBundy 		dev_err(&client->dev,
1222f1d2809dSJeff LaBundy 			"Invalid %s channel associated weight: %u\n",
1223f1d2809dSJeff LaBundy 			fwnode_get_name(ch_node), val);
1224f1d2809dSJeff LaBundy 		return -EINVAL;
1225f1d2809dSJeff LaBundy 	}
1226f1d2809dSJeff LaBundy 
1227f1d2809dSJeff LaBundy 	*assoc_weight = val;
1228f1d2809dSJeff LaBundy 
1229f1d2809dSJeff LaBundy 	return 0;
1230f1d2809dSJeff LaBundy }
1231f1d2809dSJeff LaBundy 
iqs626_parse_prop(struct iqs626_private * iqs626)1232f1d2809dSJeff LaBundy static int iqs626_parse_prop(struct iqs626_private *iqs626)
1233f1d2809dSJeff LaBundy {
1234f1d2809dSJeff LaBundy 	struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
1235f1d2809dSJeff LaBundy 	struct i2c_client *client = iqs626->client;
1236f1d2809dSJeff LaBundy 	struct fwnode_handle *ch_node;
1237f1d2809dSJeff LaBundy 	unsigned int val;
1238f1d2809dSJeff LaBundy 	int error, i;
1239f1d2809dSJeff LaBundy 	u16 general;
1240f1d2809dSJeff LaBundy 
1241f1d2809dSJeff LaBundy 	if (!device_property_read_u32(&client->dev, "azoteq,suspend-mode",
1242f1d2809dSJeff LaBundy 				      &val)) {
1243f1d2809dSJeff LaBundy 		if (val > IQS626_SYS_SETTINGS_PWR_MODE_MAX) {
1244f1d2809dSJeff LaBundy 			dev_err(&client->dev, "Invalid suspend mode: %u\n",
1245f1d2809dSJeff LaBundy 				val);
1246f1d2809dSJeff LaBundy 			return -EINVAL;
1247f1d2809dSJeff LaBundy 		}
1248f1d2809dSJeff LaBundy 
1249f1d2809dSJeff LaBundy 		iqs626->suspend_mode = val;
1250f1d2809dSJeff LaBundy 	}
1251f1d2809dSJeff LaBundy 
1252f1d2809dSJeff LaBundy 	error = regmap_raw_read(iqs626->regmap, IQS626_SYS_SETTINGS, sys_reg,
1253f1d2809dSJeff LaBundy 				sizeof(*sys_reg));
1254f1d2809dSJeff LaBundy 	if (error)
1255f1d2809dSJeff LaBundy 		return error;
1256f1d2809dSJeff LaBundy 
1257f1d2809dSJeff LaBundy 	general = be16_to_cpu(sys_reg->general);
1258f1d2809dSJeff LaBundy 	general &= IQS626_SYS_SETTINGS_ULP_UPDATE_MASK;
1259f1d2809dSJeff LaBundy 
1260f1d2809dSJeff LaBundy 	if (device_property_present(&client->dev, "azoteq,clk-div"))
1261f1d2809dSJeff LaBundy 		general |= IQS626_SYS_SETTINGS_CLK_DIV;
1262f1d2809dSJeff LaBundy 
1263f1d2809dSJeff LaBundy 	if (device_property_present(&client->dev, "azoteq,ulp-enable"))
1264f1d2809dSJeff LaBundy 		general |= IQS626_SYS_SETTINGS_ULP_AUTO;
1265f1d2809dSJeff LaBundy 
1266f1d2809dSJeff LaBundy 	if (!device_property_read_u32(&client->dev, "azoteq,ulp-update",
1267f1d2809dSJeff LaBundy 				      &val)) {
1268f1d2809dSJeff LaBundy 		if (val > IQS626_SYS_SETTINGS_ULP_UPDATE_MAX) {
1269f1d2809dSJeff LaBundy 			dev_err(&client->dev, "Invalid update rate: %u\n", val);
1270f1d2809dSJeff LaBundy 			return -EINVAL;
1271f1d2809dSJeff LaBundy 		}
1272f1d2809dSJeff LaBundy 
1273f1d2809dSJeff LaBundy 		general &= ~IQS626_SYS_SETTINGS_ULP_UPDATE_MASK;
1274f1d2809dSJeff LaBundy 		general |= (val << IQS626_SYS_SETTINGS_ULP_UPDATE_SHIFT);
1275f1d2809dSJeff LaBundy 	}
1276f1d2809dSJeff LaBundy 
1277f1d2809dSJeff LaBundy 	sys_reg->misc_a &= ~IQS626_MISC_A_ATI_BAND_DISABLE;
1278f1d2809dSJeff LaBundy 	if (device_property_present(&client->dev, "azoteq,ati-band-disable"))
1279f1d2809dSJeff LaBundy 		sys_reg->misc_a |= IQS626_MISC_A_ATI_BAND_DISABLE;
1280f1d2809dSJeff LaBundy 
1281f1d2809dSJeff LaBundy 	sys_reg->misc_a &= ~IQS626_MISC_A_ATI_LP_ONLY;
1282f1d2809dSJeff LaBundy 	if (device_property_present(&client->dev, "azoteq,ati-lp-only"))
1283f1d2809dSJeff LaBundy 		sys_reg->misc_a |= IQS626_MISC_A_ATI_LP_ONLY;
1284f1d2809dSJeff LaBundy 
1285f1d2809dSJeff LaBundy 	if (!device_property_read_u32(&client->dev, "azoteq,gpio3-select",
1286f1d2809dSJeff LaBundy 				      &val)) {
1287f1d2809dSJeff LaBundy 		if (val > IQS626_MISC_A_GPIO3_SELECT_MAX) {
1288f1d2809dSJeff LaBundy 			dev_err(&client->dev, "Invalid GPIO3 selection: %u\n",
1289f1d2809dSJeff LaBundy 				val);
1290f1d2809dSJeff LaBundy 			return -EINVAL;
1291f1d2809dSJeff LaBundy 		}
1292f1d2809dSJeff LaBundy 
1293f1d2809dSJeff LaBundy 		sys_reg->misc_a &= ~IQS626_MISC_A_GPIO3_SELECT_MASK;
1294f1d2809dSJeff LaBundy 		sys_reg->misc_a |= val;
1295f1d2809dSJeff LaBundy 	}
1296f1d2809dSJeff LaBundy 
1297f1d2809dSJeff LaBundy 	if (!device_property_read_u32(&client->dev, "azoteq,reseed-select",
1298f1d2809dSJeff LaBundy 				      &val)) {
1299f1d2809dSJeff LaBundy 		if (val > IQS626_MISC_B_RESEED_UI_SEL_MAX) {
1300f1d2809dSJeff LaBundy 			dev_err(&client->dev, "Invalid reseed selection: %u\n",
1301f1d2809dSJeff LaBundy 				val);
1302f1d2809dSJeff LaBundy 			return -EINVAL;
1303f1d2809dSJeff LaBundy 		}
1304f1d2809dSJeff LaBundy 
1305f1d2809dSJeff LaBundy 		sys_reg->misc_b &= ~IQS626_MISC_B_RESEED_UI_SEL_MASK;
1306f1d2809dSJeff LaBundy 		sys_reg->misc_b |= (val << IQS626_MISC_B_RESEED_UI_SEL_SHIFT);
1307f1d2809dSJeff LaBundy 	}
1308f1d2809dSJeff LaBundy 
1309f1d2809dSJeff LaBundy 	sys_reg->misc_b &= ~IQS626_MISC_B_THRESH_EXTEND;
1310f1d2809dSJeff LaBundy 	if (device_property_present(&client->dev, "azoteq,thresh-extend"))
1311f1d2809dSJeff LaBundy 		sys_reg->misc_b |= IQS626_MISC_B_THRESH_EXTEND;
1312f1d2809dSJeff LaBundy 
1313f1d2809dSJeff LaBundy 	sys_reg->misc_b &= ~IQS626_MISC_B_TRACKING_UI_ENABLE;
1314f1d2809dSJeff LaBundy 	if (device_property_present(&client->dev, "azoteq,tracking-enable"))
1315f1d2809dSJeff LaBundy 		sys_reg->misc_b |= IQS626_MISC_B_TRACKING_UI_ENABLE;
1316f1d2809dSJeff LaBundy 
1317f1d2809dSJeff LaBundy 	sys_reg->misc_b &= ~IQS626_MISC_B_RESEED_OFFSET;
1318f1d2809dSJeff LaBundy 	if (device_property_present(&client->dev, "azoteq,reseed-offset"))
1319f1d2809dSJeff LaBundy 		sys_reg->misc_b |= IQS626_MISC_B_RESEED_OFFSET;
1320f1d2809dSJeff LaBundy 
1321f1d2809dSJeff LaBundy 	if (!device_property_read_u32(&client->dev, "azoteq,rate-np-ms",
1322f1d2809dSJeff LaBundy 				      &val)) {
1323f1d2809dSJeff LaBundy 		if (val > IQS626_RATE_NP_MS_MAX) {
1324f1d2809dSJeff LaBundy 			dev_err(&client->dev, "Invalid report rate: %u\n", val);
1325f1d2809dSJeff LaBundy 			return -EINVAL;
1326f1d2809dSJeff LaBundy 		}
1327f1d2809dSJeff LaBundy 
1328f1d2809dSJeff LaBundy 		sys_reg->rate_np = val;
1329f1d2809dSJeff LaBundy 	}
1330f1d2809dSJeff LaBundy 
1331f1d2809dSJeff LaBundy 	if (!device_property_read_u32(&client->dev, "azoteq,rate-lp-ms",
1332f1d2809dSJeff LaBundy 				      &val)) {
1333f1d2809dSJeff LaBundy 		if (val > IQS626_RATE_LP_MS_MAX) {
1334f1d2809dSJeff LaBundy 			dev_err(&client->dev, "Invalid report rate: %u\n", val);
1335f1d2809dSJeff LaBundy 			return -EINVAL;
1336f1d2809dSJeff LaBundy 		}
1337f1d2809dSJeff LaBundy 
1338f1d2809dSJeff LaBundy 		sys_reg->rate_lp = val;
1339f1d2809dSJeff LaBundy 	}
1340f1d2809dSJeff LaBundy 
1341f1d2809dSJeff LaBundy 	if (!device_property_read_u32(&client->dev, "azoteq,rate-ulp-ms",
1342f1d2809dSJeff LaBundy 				      &val)) {
1343f1d2809dSJeff LaBundy 		if (val > IQS626_RATE_ULP_MS_MAX) {
1344f1d2809dSJeff LaBundy 			dev_err(&client->dev, "Invalid report rate: %u\n", val);
1345f1d2809dSJeff LaBundy 			return -EINVAL;
1346f1d2809dSJeff LaBundy 		}
1347f1d2809dSJeff LaBundy 
1348f1d2809dSJeff LaBundy 		sys_reg->rate_ulp = val / 16;
1349f1d2809dSJeff LaBundy 	}
1350f1d2809dSJeff LaBundy 
1351f1d2809dSJeff LaBundy 	if (!device_property_read_u32(&client->dev, "azoteq,timeout-pwr-ms",
1352f1d2809dSJeff LaBundy 				      &val)) {
1353f1d2809dSJeff LaBundy 		if (val > IQS626_TIMEOUT_PWR_MS_MAX) {
1354f1d2809dSJeff LaBundy 			dev_err(&client->dev, "Invalid timeout: %u\n", val);
1355f1d2809dSJeff LaBundy 			return -EINVAL;
1356f1d2809dSJeff LaBundy 		}
1357f1d2809dSJeff LaBundy 
1358f1d2809dSJeff LaBundy 		sys_reg->timeout_pwr = val / 512;
1359f1d2809dSJeff LaBundy 	}
1360f1d2809dSJeff LaBundy 
1361f1d2809dSJeff LaBundy 	if (!device_property_read_u32(&client->dev, "azoteq,timeout-lta-ms",
1362f1d2809dSJeff LaBundy 				      &val)) {
1363f1d2809dSJeff LaBundy 		if (val > IQS626_TIMEOUT_LTA_MS_MAX) {
1364f1d2809dSJeff LaBundy 			dev_err(&client->dev, "Invalid timeout: %u\n", val);
1365f1d2809dSJeff LaBundy 			return -EINVAL;
1366f1d2809dSJeff LaBundy 		}
1367f1d2809dSJeff LaBundy 
1368f1d2809dSJeff LaBundy 		sys_reg->timeout_lta = val / 512;
1369f1d2809dSJeff LaBundy 	}
1370f1d2809dSJeff LaBundy 
1371f1d2809dSJeff LaBundy 	sys_reg->event_mask = ~((u8)IQS626_EVENT_MASK_SYS);
1372f1d2809dSJeff LaBundy 	sys_reg->redo_ati = 0;
1373f1d2809dSJeff LaBundy 
1374f1d2809dSJeff LaBundy 	sys_reg->reseed = 0;
1375f1d2809dSJeff LaBundy 	sys_reg->active = 0;
1376f1d2809dSJeff LaBundy 
1377f1d2809dSJeff LaBundy 	for (i = 0; i < ARRAY_SIZE(iqs626_channels); i++) {
1378f1d2809dSJeff LaBundy 		ch_node = device_get_named_child_node(&client->dev,
1379f1d2809dSJeff LaBundy 						      iqs626_channels[i].name);
1380f1d2809dSJeff LaBundy 		if (!ch_node)
1381f1d2809dSJeff LaBundy 			continue;
1382f1d2809dSJeff LaBundy 
1383f1d2809dSJeff LaBundy 		error = iqs626_parse_channel(iqs626, ch_node, i);
13844d3d2694SJeff LaBundy 		fwnode_handle_put(ch_node);
1385f1d2809dSJeff LaBundy 		if (error)
1386f1d2809dSJeff LaBundy 			return error;
1387f1d2809dSJeff LaBundy 
1388f1d2809dSJeff LaBundy 		sys_reg->active |= iqs626_channels[i].active;
1389f1d2809dSJeff LaBundy 	}
1390f1d2809dSJeff LaBundy 
1391f1d2809dSJeff LaBundy 	general |= IQS626_SYS_SETTINGS_EVENT_MODE;
1392f1d2809dSJeff LaBundy 
1393f1d2809dSJeff LaBundy 	/*
1394f1d2809dSJeff LaBundy 	 * Enable streaming during normal-power mode if the trackpad is used to
1395f1d2809dSJeff LaBundy 	 * report raw coordinates instead of gestures. In that case, the device
1396f1d2809dSJeff LaBundy 	 * returns to event mode during low-power mode.
1397f1d2809dSJeff LaBundy 	 */
1398f1d2809dSJeff LaBundy 	if (sys_reg->active & iqs626_channels[IQS626_CH_TP_2].active &&
1399f1d2809dSJeff LaBundy 	    sys_reg->event_mask & IQS626_EVENT_MASK_GESTURE)
1400f1d2809dSJeff LaBundy 		general |= IQS626_SYS_SETTINGS_EVENT_MODE_LP;
1401f1d2809dSJeff LaBundy 
1402f1d2809dSJeff LaBundy 	general |= IQS626_SYS_SETTINGS_REDO_ATI;
1403f1d2809dSJeff LaBundy 	general |= IQS626_SYS_SETTINGS_ACK_RESET;
1404f1d2809dSJeff LaBundy 
1405f1d2809dSJeff LaBundy 	sys_reg->general = cpu_to_be16(general);
1406f1d2809dSJeff LaBundy 
1407f1d2809dSJeff LaBundy 	error = regmap_raw_write(iqs626->regmap, IQS626_SYS_SETTINGS,
1408f1d2809dSJeff LaBundy 				 &iqs626->sys_reg, sizeof(iqs626->sys_reg));
1409f1d2809dSJeff LaBundy 	if (error)
1410f1d2809dSJeff LaBundy 		return error;
1411f1d2809dSJeff LaBundy 
1412f1d2809dSJeff LaBundy 	iqs626_irq_wait();
1413f1d2809dSJeff LaBundy 
1414f1d2809dSJeff LaBundy 	return 0;
1415f1d2809dSJeff LaBundy }
1416f1d2809dSJeff LaBundy 
iqs626_input_init(struct iqs626_private * iqs626)1417f1d2809dSJeff LaBundy static int iqs626_input_init(struct iqs626_private *iqs626)
1418f1d2809dSJeff LaBundy {
1419f1d2809dSJeff LaBundy 	struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
1420f1d2809dSJeff LaBundy 	struct i2c_client *client = iqs626->client;
1421f1d2809dSJeff LaBundy 	int error, i, j;
1422f1d2809dSJeff LaBundy 
1423f1d2809dSJeff LaBundy 	iqs626->keypad = devm_input_allocate_device(&client->dev);
1424f1d2809dSJeff LaBundy 	if (!iqs626->keypad)
1425f1d2809dSJeff LaBundy 		return -ENOMEM;
1426f1d2809dSJeff LaBundy 
1427f1d2809dSJeff LaBundy 	iqs626->keypad->keycodemax = ARRAY_SIZE(iqs626->kp_code);
1428f1d2809dSJeff LaBundy 	iqs626->keypad->keycode = iqs626->kp_code;
1429f1d2809dSJeff LaBundy 	iqs626->keypad->keycodesize = sizeof(**iqs626->kp_code);
1430f1d2809dSJeff LaBundy 
1431f1d2809dSJeff LaBundy 	iqs626->keypad->name = "iqs626a_keypad";
1432f1d2809dSJeff LaBundy 	iqs626->keypad->id.bustype = BUS_I2C;
1433f1d2809dSJeff LaBundy 
1434f1d2809dSJeff LaBundy 	for (i = 0; i < ARRAY_SIZE(iqs626_channels); i++) {
1435f1d2809dSJeff LaBundy 		if (!(sys_reg->active & iqs626_channels[i].active))
1436f1d2809dSJeff LaBundy 			continue;
1437f1d2809dSJeff LaBundy 
1438f1d2809dSJeff LaBundy 		for (j = 0; j < ARRAY_SIZE(iqs626_events); j++) {
1439f1d2809dSJeff LaBundy 			if (!iqs626->kp_type[i][j])
1440f1d2809dSJeff LaBundy 				continue;
1441f1d2809dSJeff LaBundy 
1442f1d2809dSJeff LaBundy 			input_set_capability(iqs626->keypad,
1443f1d2809dSJeff LaBundy 					     iqs626->kp_type[i][j],
1444f1d2809dSJeff LaBundy 					     iqs626->kp_code[i][j]);
1445f1d2809dSJeff LaBundy 		}
1446f1d2809dSJeff LaBundy 	}
1447f1d2809dSJeff LaBundy 
1448f1d2809dSJeff LaBundy 	if (!(sys_reg->active & iqs626_channels[IQS626_CH_TP_2].active))
1449f1d2809dSJeff LaBundy 		return 0;
1450f1d2809dSJeff LaBundy 
1451f1d2809dSJeff LaBundy 	iqs626->trackpad = devm_input_allocate_device(&client->dev);
1452f1d2809dSJeff LaBundy 	if (!iqs626->trackpad)
1453f1d2809dSJeff LaBundy 		return -ENOMEM;
1454f1d2809dSJeff LaBundy 
1455f1d2809dSJeff LaBundy 	iqs626->trackpad->keycodemax = ARRAY_SIZE(iqs626->tp_code);
1456f1d2809dSJeff LaBundy 	iqs626->trackpad->keycode = iqs626->tp_code;
1457f1d2809dSJeff LaBundy 	iqs626->trackpad->keycodesize = sizeof(*iqs626->tp_code);
1458f1d2809dSJeff LaBundy 
1459f1d2809dSJeff LaBundy 	iqs626->trackpad->name = "iqs626a_trackpad";
1460f1d2809dSJeff LaBundy 	iqs626->trackpad->id.bustype = BUS_I2C;
1461f1d2809dSJeff LaBundy 
1462f1d2809dSJeff LaBundy 	/*
1463f1d2809dSJeff LaBundy 	 * Present the trackpad as a traditional pointing device if no gestures
1464f1d2809dSJeff LaBundy 	 * have been mapped to a keycode.
1465f1d2809dSJeff LaBundy 	 */
1466f1d2809dSJeff LaBundy 	if (sys_reg->event_mask & IQS626_EVENT_MASK_GESTURE) {
1467f1d2809dSJeff LaBundy 		u8 tp_mask = iqs626_channels[IQS626_CH_TP_3].active;
1468f1d2809dSJeff LaBundy 
1469f1d2809dSJeff LaBundy 		input_set_capability(iqs626->trackpad, EV_KEY, BTN_TOUCH);
1470f1d2809dSJeff LaBundy 		input_set_abs_params(iqs626->trackpad, ABS_Y, 0, 255, 0, 0);
1471f1d2809dSJeff LaBundy 
1472f1d2809dSJeff LaBundy 		if ((sys_reg->active & tp_mask) == tp_mask)
1473f1d2809dSJeff LaBundy 			input_set_abs_params(iqs626->trackpad,
1474f1d2809dSJeff LaBundy 					     ABS_X, 0, 255, 0, 0);
1475f1d2809dSJeff LaBundy 		else
1476f1d2809dSJeff LaBundy 			input_set_abs_params(iqs626->trackpad,
1477f1d2809dSJeff LaBundy 					     ABS_X, 0, 128, 0, 0);
1478f1d2809dSJeff LaBundy 
1479f1d2809dSJeff LaBundy 		touchscreen_parse_properties(iqs626->trackpad, false,
1480f1d2809dSJeff LaBundy 					     &iqs626->prop);
1481f1d2809dSJeff LaBundy 	} else {
1482f1d2809dSJeff LaBundy 		for (i = 0; i < IQS626_NUM_GESTURES; i++)
1483f1d2809dSJeff LaBundy 			if (iqs626->tp_code[i] != KEY_RESERVED)
1484f1d2809dSJeff LaBundy 				input_set_capability(iqs626->trackpad, EV_KEY,
1485f1d2809dSJeff LaBundy 						     iqs626->tp_code[i]);
1486f1d2809dSJeff LaBundy 	}
1487f1d2809dSJeff LaBundy 
1488f1d2809dSJeff LaBundy 	error = input_register_device(iqs626->trackpad);
1489f1d2809dSJeff LaBundy 	if (error)
1490f1d2809dSJeff LaBundy 		dev_err(&client->dev, "Failed to register trackpad: %d\n",
1491f1d2809dSJeff LaBundy 			error);
1492f1d2809dSJeff LaBundy 
1493f1d2809dSJeff LaBundy 	return error;
1494f1d2809dSJeff LaBundy }
1495f1d2809dSJeff LaBundy 
iqs626_report(struct iqs626_private * iqs626)1496f1d2809dSJeff LaBundy static int iqs626_report(struct iqs626_private *iqs626)
1497f1d2809dSJeff LaBundy {
1498f1d2809dSJeff LaBundy 	struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
1499f1d2809dSJeff LaBundy 	struct i2c_client *client = iqs626->client;
1500f1d2809dSJeff LaBundy 	struct iqs626_flags flags;
1501f1d2809dSJeff LaBundy 	__le16 hall_output;
1502f1d2809dSJeff LaBundy 	int error, i, j;
1503f1d2809dSJeff LaBundy 	u8 state;
1504f1d2809dSJeff LaBundy 	u8 *dir_mask = &flags.states[IQS626_ST_OFFS_DIR];
1505f1d2809dSJeff LaBundy 
1506f1d2809dSJeff LaBundy 	error = regmap_raw_read(iqs626->regmap, IQS626_SYS_FLAGS, &flags,
1507f1d2809dSJeff LaBundy 				sizeof(flags));
1508f1d2809dSJeff LaBundy 	if (error) {
1509f1d2809dSJeff LaBundy 		dev_err(&client->dev, "Failed to read device status: %d\n",
1510f1d2809dSJeff LaBundy 			error);
1511f1d2809dSJeff LaBundy 		return error;
1512f1d2809dSJeff LaBundy 	}
1513f1d2809dSJeff LaBundy 
1514f1d2809dSJeff LaBundy 	/*
1515f1d2809dSJeff LaBundy 	 * The device resets itself if its own watchdog bites, which can happen
1516f1d2809dSJeff LaBundy 	 * in the event of an I2C communication error. In this case, the device
1517f1d2809dSJeff LaBundy 	 * asserts a SHOW_RESET interrupt and all registers must be restored.
1518f1d2809dSJeff LaBundy 	 */
1519f1d2809dSJeff LaBundy 	if (be16_to_cpu(flags.system) & IQS626_SYS_FLAGS_SHOW_RESET) {
1520f1d2809dSJeff LaBundy 		dev_err(&client->dev, "Unexpected device reset\n");
1521f1d2809dSJeff LaBundy 
1522f1d2809dSJeff LaBundy 		error = regmap_raw_write(iqs626->regmap, IQS626_SYS_SETTINGS,
1523f1d2809dSJeff LaBundy 					 sys_reg, sizeof(*sys_reg));
1524f1d2809dSJeff LaBundy 		if (error)
1525f1d2809dSJeff LaBundy 			dev_err(&client->dev,
1526f1d2809dSJeff LaBundy 				"Failed to re-initialize device: %d\n", error);
1527f1d2809dSJeff LaBundy 
1528f1d2809dSJeff LaBundy 		return error;
1529f1d2809dSJeff LaBundy 	}
1530f1d2809dSJeff LaBundy 
1531f1d2809dSJeff LaBundy 	if (be16_to_cpu(flags.system) & IQS626_SYS_FLAGS_IN_ATI)
1532f1d2809dSJeff LaBundy 		return 0;
1533f1d2809dSJeff LaBundy 
1534f1d2809dSJeff LaBundy 	/*
1535f1d2809dSJeff LaBundy 	 * Unlike the ULP or generic channels, the Hall channel does not have a
1536f1d2809dSJeff LaBundy 	 * direction flag. Instead, the direction (i.e. magnet polarity) can be
1537f1d2809dSJeff LaBundy 	 * derived based on the sign of the 2's complement differential output.
1538f1d2809dSJeff LaBundy 	 */
1539f1d2809dSJeff LaBundy 	if (sys_reg->active & iqs626_channels[IQS626_CH_HALL].active) {
1540f1d2809dSJeff LaBundy 		error = regmap_raw_read(iqs626->regmap, IQS626_HALL_OUTPUT,
1541f1d2809dSJeff LaBundy 					&hall_output, sizeof(hall_output));
1542f1d2809dSJeff LaBundy 		if (error) {
1543f1d2809dSJeff LaBundy 			dev_err(&client->dev,
1544f1d2809dSJeff LaBundy 				"Failed to read Hall output: %d\n", error);
1545f1d2809dSJeff LaBundy 			return error;
1546f1d2809dSJeff LaBundy 		}
1547f1d2809dSJeff LaBundy 
1548f1d2809dSJeff LaBundy 		*dir_mask &= ~iqs626_channels[IQS626_CH_HALL].active;
1549f1d2809dSJeff LaBundy 		if (le16_to_cpu(hall_output) < 0x8000)
1550f1d2809dSJeff LaBundy 			*dir_mask |= iqs626_channels[IQS626_CH_HALL].active;
1551f1d2809dSJeff LaBundy 	}
1552f1d2809dSJeff LaBundy 
1553f1d2809dSJeff LaBundy 	for (i = 0; i < ARRAY_SIZE(iqs626_channels); i++) {
1554f1d2809dSJeff LaBundy 		if (!(sys_reg->active & iqs626_channels[i].active))
1555f1d2809dSJeff LaBundy 			continue;
1556f1d2809dSJeff LaBundy 
1557f1d2809dSJeff LaBundy 		for (j = 0; j < ARRAY_SIZE(iqs626_events); j++) {
1558f1d2809dSJeff LaBundy 			if (!iqs626->kp_type[i][j])
1559f1d2809dSJeff LaBundy 				continue;
1560f1d2809dSJeff LaBundy 
1561f1d2809dSJeff LaBundy 			state = flags.states[iqs626_events[j].st_offs];
1562f1d2809dSJeff LaBundy 			state &= iqs626_events[j].dir_up ? *dir_mask
1563f1d2809dSJeff LaBundy 							 : ~(*dir_mask);
1564f1d2809dSJeff LaBundy 			state &= iqs626_channels[i].active;
1565f1d2809dSJeff LaBundy 
1566f1d2809dSJeff LaBundy 			input_event(iqs626->keypad, iqs626->kp_type[i][j],
1567f1d2809dSJeff LaBundy 				    iqs626->kp_code[i][j], !!state);
1568f1d2809dSJeff LaBundy 		}
1569f1d2809dSJeff LaBundy 	}
1570f1d2809dSJeff LaBundy 
1571f1d2809dSJeff LaBundy 	input_sync(iqs626->keypad);
1572f1d2809dSJeff LaBundy 
1573f1d2809dSJeff LaBundy 	/*
1574f1d2809dSJeff LaBundy 	 * The following completion signals that ATI has finished, any initial
1575f1d2809dSJeff LaBundy 	 * switch states have been reported and the keypad can be registered.
1576f1d2809dSJeff LaBundy 	 */
1577f1d2809dSJeff LaBundy 	complete_all(&iqs626->ati_done);
1578f1d2809dSJeff LaBundy 
1579f1d2809dSJeff LaBundy 	if (!(sys_reg->active & iqs626_channels[IQS626_CH_TP_2].active))
1580f1d2809dSJeff LaBundy 		return 0;
1581f1d2809dSJeff LaBundy 
1582f1d2809dSJeff LaBundy 	if (sys_reg->event_mask & IQS626_EVENT_MASK_GESTURE) {
1583f1d2809dSJeff LaBundy 		state = flags.states[IQS626_ST_OFFS_TOUCH];
1584f1d2809dSJeff LaBundy 		state &= iqs626_channels[IQS626_CH_TP_2].active;
1585f1d2809dSJeff LaBundy 
1586f1d2809dSJeff LaBundy 		input_report_key(iqs626->trackpad, BTN_TOUCH, state);
1587f1d2809dSJeff LaBundy 
1588f1d2809dSJeff LaBundy 		if (state)
1589f1d2809dSJeff LaBundy 			touchscreen_report_pos(iqs626->trackpad, &iqs626->prop,
1590f1d2809dSJeff LaBundy 					       flags.trackpad_x,
1591f1d2809dSJeff LaBundy 					       flags.trackpad_y, false);
1592f1d2809dSJeff LaBundy 	} else {
1593f1d2809dSJeff LaBundy 		for (i = 0; i < IQS626_NUM_GESTURES; i++)
1594f1d2809dSJeff LaBundy 			input_report_key(iqs626->trackpad, iqs626->tp_code[i],
1595f1d2809dSJeff LaBundy 					 flags.gesture & BIT(i));
1596f1d2809dSJeff LaBundy 
1597f1d2809dSJeff LaBundy 		if (flags.gesture & GENMASK(IQS626_GESTURE_TAP, 0)) {
1598f1d2809dSJeff LaBundy 			input_sync(iqs626->trackpad);
1599f1d2809dSJeff LaBundy 
1600f1d2809dSJeff LaBundy 			/*
1601f1d2809dSJeff LaBundy 			 * Momentary gestures are followed by a complementary
1602f1d2809dSJeff LaBundy 			 * release cycle so as to emulate a full keystroke.
1603f1d2809dSJeff LaBundy 			 */
1604f1d2809dSJeff LaBundy 			for (i = 0; i < IQS626_GESTURE_HOLD; i++)
1605f1d2809dSJeff LaBundy 				input_report_key(iqs626->trackpad,
1606f1d2809dSJeff LaBundy 						 iqs626->tp_code[i], 0);
1607f1d2809dSJeff LaBundy 		}
1608f1d2809dSJeff LaBundy 	}
1609f1d2809dSJeff LaBundy 
1610f1d2809dSJeff LaBundy 	input_sync(iqs626->trackpad);
1611f1d2809dSJeff LaBundy 
1612f1d2809dSJeff LaBundy 	return 0;
1613f1d2809dSJeff LaBundy }
1614f1d2809dSJeff LaBundy 
iqs626_irq(int irq,void * context)1615f1d2809dSJeff LaBundy static irqreturn_t iqs626_irq(int irq, void *context)
1616f1d2809dSJeff LaBundy {
1617f1d2809dSJeff LaBundy 	struct iqs626_private *iqs626 = context;
1618f1d2809dSJeff LaBundy 
1619f1d2809dSJeff LaBundy 	if (iqs626_report(iqs626))
1620f1d2809dSJeff LaBundy 		return IRQ_NONE;
1621f1d2809dSJeff LaBundy 
1622f1d2809dSJeff LaBundy 	/*
1623f1d2809dSJeff LaBundy 	 * The device does not deassert its interrupt (RDY) pin until shortly
1624f1d2809dSJeff LaBundy 	 * after receiving an I2C stop condition; the following delay ensures
1625f1d2809dSJeff LaBundy 	 * the interrupt handler does not return before this time.
1626f1d2809dSJeff LaBundy 	 */
1627f1d2809dSJeff LaBundy 	iqs626_irq_wait();
1628f1d2809dSJeff LaBundy 
1629f1d2809dSJeff LaBundy 	return IRQ_HANDLED;
1630f1d2809dSJeff LaBundy }
1631f1d2809dSJeff LaBundy 
1632f1d2809dSJeff LaBundy static const struct regmap_config iqs626_regmap_config = {
1633f1d2809dSJeff LaBundy 	.reg_bits = 8,
1634f1d2809dSJeff LaBundy 	.val_bits = 16,
1635f1d2809dSJeff LaBundy 	.max_register = IQS626_MAX_REG,
1636f1d2809dSJeff LaBundy };
1637f1d2809dSJeff LaBundy 
iqs626_probe(struct i2c_client * client)1638f1d2809dSJeff LaBundy static int iqs626_probe(struct i2c_client *client)
1639f1d2809dSJeff LaBundy {
1640f1d2809dSJeff LaBundy 	struct iqs626_ver_info ver_info;
1641f1d2809dSJeff LaBundy 	struct iqs626_private *iqs626;
1642f1d2809dSJeff LaBundy 	int error;
1643f1d2809dSJeff LaBundy 
1644f1d2809dSJeff LaBundy 	iqs626 = devm_kzalloc(&client->dev, sizeof(*iqs626), GFP_KERNEL);
1645f1d2809dSJeff LaBundy 	if (!iqs626)
1646f1d2809dSJeff LaBundy 		return -ENOMEM;
1647f1d2809dSJeff LaBundy 
1648f1d2809dSJeff LaBundy 	i2c_set_clientdata(client, iqs626);
1649f1d2809dSJeff LaBundy 	iqs626->client = client;
1650f1d2809dSJeff LaBundy 
1651f1d2809dSJeff LaBundy 	iqs626->regmap = devm_regmap_init_i2c(client, &iqs626_regmap_config);
1652f1d2809dSJeff LaBundy 	if (IS_ERR(iqs626->regmap)) {
1653f1d2809dSJeff LaBundy 		error = PTR_ERR(iqs626->regmap);
1654f1d2809dSJeff LaBundy 		dev_err(&client->dev, "Failed to initialize register map: %d\n",
1655f1d2809dSJeff LaBundy 			error);
1656f1d2809dSJeff LaBundy 		return error;
1657f1d2809dSJeff LaBundy 	}
1658f1d2809dSJeff LaBundy 
1659f1d2809dSJeff LaBundy 	init_completion(&iqs626->ati_done);
1660f1d2809dSJeff LaBundy 
1661f1d2809dSJeff LaBundy 	error = regmap_raw_read(iqs626->regmap, IQS626_VER_INFO, &ver_info,
1662f1d2809dSJeff LaBundy 				sizeof(ver_info));
1663f1d2809dSJeff LaBundy 	if (error)
1664f1d2809dSJeff LaBundy 		return error;
1665f1d2809dSJeff LaBundy 
1666f1d2809dSJeff LaBundy 	if (ver_info.prod_num != IQS626_VER_INFO_PROD_NUM) {
1667f1d2809dSJeff LaBundy 		dev_err(&client->dev, "Unrecognized product number: 0x%02X\n",
1668f1d2809dSJeff LaBundy 			ver_info.prod_num);
1669f1d2809dSJeff LaBundy 		return -EINVAL;
1670f1d2809dSJeff LaBundy 	}
1671f1d2809dSJeff LaBundy 
1672f1d2809dSJeff LaBundy 	error = iqs626_parse_prop(iqs626);
1673f1d2809dSJeff LaBundy 	if (error)
1674f1d2809dSJeff LaBundy 		return error;
1675f1d2809dSJeff LaBundy 
1676f1d2809dSJeff LaBundy 	error = iqs626_input_init(iqs626);
1677f1d2809dSJeff LaBundy 	if (error)
1678f1d2809dSJeff LaBundy 		return error;
1679f1d2809dSJeff LaBundy 
1680f1d2809dSJeff LaBundy 	error = devm_request_threaded_irq(&client->dev, client->irq,
1681f1d2809dSJeff LaBundy 					  NULL, iqs626_irq, IRQF_ONESHOT,
1682f1d2809dSJeff LaBundy 					  client->name, iqs626);
1683f1d2809dSJeff LaBundy 	if (error) {
1684f1d2809dSJeff LaBundy 		dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
1685f1d2809dSJeff LaBundy 		return error;
1686f1d2809dSJeff LaBundy 	}
1687f1d2809dSJeff LaBundy 
1688f1d2809dSJeff LaBundy 	if (!wait_for_completion_timeout(&iqs626->ati_done,
1689f1d2809dSJeff LaBundy 					 msecs_to_jiffies(2000))) {
1690f1d2809dSJeff LaBundy 		dev_err(&client->dev, "Failed to complete ATI\n");
1691f1d2809dSJeff LaBundy 		return -ETIMEDOUT;
1692f1d2809dSJeff LaBundy 	}
1693f1d2809dSJeff LaBundy 
1694f1d2809dSJeff LaBundy 	/*
1695f1d2809dSJeff LaBundy 	 * The keypad may include one or more switches and is not registered
1696f1d2809dSJeff LaBundy 	 * until ATI is complete and the initial switch states are read.
1697f1d2809dSJeff LaBundy 	 */
1698f1d2809dSJeff LaBundy 	error = input_register_device(iqs626->keypad);
1699f1d2809dSJeff LaBundy 	if (error)
1700f1d2809dSJeff LaBundy 		dev_err(&client->dev, "Failed to register keypad: %d\n", error);
1701f1d2809dSJeff LaBundy 
1702f1d2809dSJeff LaBundy 	return error;
1703f1d2809dSJeff LaBundy }
1704f1d2809dSJeff LaBundy 
iqs626_suspend(struct device * dev)1705238e5de4SJonathan Cameron static int iqs626_suspend(struct device *dev)
1706f1d2809dSJeff LaBundy {
1707f1d2809dSJeff LaBundy 	struct iqs626_private *iqs626 = dev_get_drvdata(dev);
1708f1d2809dSJeff LaBundy 	struct i2c_client *client = iqs626->client;
1709f1d2809dSJeff LaBundy 	unsigned int val;
1710f1d2809dSJeff LaBundy 	int error;
1711f1d2809dSJeff LaBundy 
1712f1d2809dSJeff LaBundy 	if (!iqs626->suspend_mode)
1713f1d2809dSJeff LaBundy 		return 0;
1714f1d2809dSJeff LaBundy 
1715f1d2809dSJeff LaBundy 	disable_irq(client->irq);
1716f1d2809dSJeff LaBundy 
1717f1d2809dSJeff LaBundy 	/*
1718f1d2809dSJeff LaBundy 	 * Automatic power mode switching must be disabled before the device is
1719f1d2809dSJeff LaBundy 	 * forced into any particular power mode. In this case, the device will
1720f1d2809dSJeff LaBundy 	 * transition into normal-power mode.
1721f1d2809dSJeff LaBundy 	 */
1722f1d2809dSJeff LaBundy 	error = regmap_update_bits(iqs626->regmap, IQS626_SYS_SETTINGS,
1723f1d2809dSJeff LaBundy 				   IQS626_SYS_SETTINGS_DIS_AUTO, ~0);
1724f1d2809dSJeff LaBundy 	if (error)
1725f1d2809dSJeff LaBundy 		goto err_irq;
1726f1d2809dSJeff LaBundy 
1727f1d2809dSJeff LaBundy 	/*
1728f1d2809dSJeff LaBundy 	 * The following check ensures the device has completed its transition
1729f1d2809dSJeff LaBundy 	 * into normal-power mode before a manual mode switch is performed.
1730f1d2809dSJeff LaBundy 	 */
1731f1d2809dSJeff LaBundy 	error = regmap_read_poll_timeout(iqs626->regmap, IQS626_SYS_FLAGS, val,
1732f1d2809dSJeff LaBundy 					!(val & IQS626_SYS_FLAGS_PWR_MODE_MASK),
1733f1d2809dSJeff LaBundy 					 IQS626_PWR_MODE_POLL_SLEEP_US,
1734f1d2809dSJeff LaBundy 					 IQS626_PWR_MODE_POLL_TIMEOUT_US);
1735f1d2809dSJeff LaBundy 	if (error)
1736f1d2809dSJeff LaBundy 		goto err_irq;
1737f1d2809dSJeff LaBundy 
1738f1d2809dSJeff LaBundy 	error = regmap_update_bits(iqs626->regmap, IQS626_SYS_SETTINGS,
1739f1d2809dSJeff LaBundy 				   IQS626_SYS_SETTINGS_PWR_MODE_MASK,
1740f1d2809dSJeff LaBundy 				   iqs626->suspend_mode <<
1741f1d2809dSJeff LaBundy 				   IQS626_SYS_SETTINGS_PWR_MODE_SHIFT);
1742f1d2809dSJeff LaBundy 	if (error)
1743f1d2809dSJeff LaBundy 		goto err_irq;
1744f1d2809dSJeff LaBundy 
1745f1d2809dSJeff LaBundy 	/*
1746f1d2809dSJeff LaBundy 	 * This last check ensures the device has completed its transition into
1747f1d2809dSJeff LaBundy 	 * the desired power mode to prevent any spurious interrupts from being
1748f1d2809dSJeff LaBundy 	 * triggered after iqs626_suspend has already returned.
1749f1d2809dSJeff LaBundy 	 */
1750f1d2809dSJeff LaBundy 	error = regmap_read_poll_timeout(iqs626->regmap, IQS626_SYS_FLAGS, val,
1751f1d2809dSJeff LaBundy 					 (val & IQS626_SYS_FLAGS_PWR_MODE_MASK)
1752f1d2809dSJeff LaBundy 					 == (iqs626->suspend_mode <<
1753f1d2809dSJeff LaBundy 					     IQS626_SYS_FLAGS_PWR_MODE_SHIFT),
1754f1d2809dSJeff LaBundy 					 IQS626_PWR_MODE_POLL_SLEEP_US,
1755f1d2809dSJeff LaBundy 					 IQS626_PWR_MODE_POLL_TIMEOUT_US);
1756f1d2809dSJeff LaBundy 
1757f1d2809dSJeff LaBundy err_irq:
1758f1d2809dSJeff LaBundy 	iqs626_irq_wait();
1759f1d2809dSJeff LaBundy 	enable_irq(client->irq);
1760f1d2809dSJeff LaBundy 
1761f1d2809dSJeff LaBundy 	return error;
1762f1d2809dSJeff LaBundy }
1763f1d2809dSJeff LaBundy 
iqs626_resume(struct device * dev)1764238e5de4SJonathan Cameron static int iqs626_resume(struct device *dev)
1765f1d2809dSJeff LaBundy {
1766f1d2809dSJeff LaBundy 	struct iqs626_private *iqs626 = dev_get_drvdata(dev);
1767f1d2809dSJeff LaBundy 	struct i2c_client *client = iqs626->client;
1768f1d2809dSJeff LaBundy 	unsigned int val;
1769f1d2809dSJeff LaBundy 	int error;
1770f1d2809dSJeff LaBundy 
1771f1d2809dSJeff LaBundy 	if (!iqs626->suspend_mode)
1772f1d2809dSJeff LaBundy 		return 0;
1773f1d2809dSJeff LaBundy 
1774f1d2809dSJeff LaBundy 	disable_irq(client->irq);
1775f1d2809dSJeff LaBundy 
1776f1d2809dSJeff LaBundy 	error = regmap_update_bits(iqs626->regmap, IQS626_SYS_SETTINGS,
1777f1d2809dSJeff LaBundy 				   IQS626_SYS_SETTINGS_PWR_MODE_MASK, 0);
1778f1d2809dSJeff LaBundy 	if (error)
1779f1d2809dSJeff LaBundy 		goto err_irq;
1780f1d2809dSJeff LaBundy 
1781f1d2809dSJeff LaBundy 	/*
1782f1d2809dSJeff LaBundy 	 * This check ensures the device has returned to normal-power mode
1783f1d2809dSJeff LaBundy 	 * before automatic power mode switching is re-enabled.
1784f1d2809dSJeff LaBundy 	 */
1785f1d2809dSJeff LaBundy 	error = regmap_read_poll_timeout(iqs626->regmap, IQS626_SYS_FLAGS, val,
1786f1d2809dSJeff LaBundy 					!(val & IQS626_SYS_FLAGS_PWR_MODE_MASK),
1787f1d2809dSJeff LaBundy 					 IQS626_PWR_MODE_POLL_SLEEP_US,
1788f1d2809dSJeff LaBundy 					 IQS626_PWR_MODE_POLL_TIMEOUT_US);
1789f1d2809dSJeff LaBundy 	if (error)
1790f1d2809dSJeff LaBundy 		goto err_irq;
1791f1d2809dSJeff LaBundy 
1792f1d2809dSJeff LaBundy 	error = regmap_update_bits(iqs626->regmap, IQS626_SYS_SETTINGS,
1793f1d2809dSJeff LaBundy 				   IQS626_SYS_SETTINGS_DIS_AUTO, 0);
1794f1d2809dSJeff LaBundy 	if (error)
1795f1d2809dSJeff LaBundy 		goto err_irq;
1796f1d2809dSJeff LaBundy 
1797f1d2809dSJeff LaBundy 	/*
1798f1d2809dSJeff LaBundy 	 * This step reports any events that may have been "swallowed" as a
1799f1d2809dSJeff LaBundy 	 * result of polling PWR_MODE (which automatically acknowledges any
1800f1d2809dSJeff LaBundy 	 * pending interrupts).
1801f1d2809dSJeff LaBundy 	 */
1802f1d2809dSJeff LaBundy 	error = iqs626_report(iqs626);
1803f1d2809dSJeff LaBundy 
1804f1d2809dSJeff LaBundy err_irq:
1805f1d2809dSJeff LaBundy 	iqs626_irq_wait();
1806f1d2809dSJeff LaBundy 	enable_irq(client->irq);
1807f1d2809dSJeff LaBundy 
1808f1d2809dSJeff LaBundy 	return error;
1809f1d2809dSJeff LaBundy }
1810f1d2809dSJeff LaBundy 
1811238e5de4SJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(iqs626_pm, iqs626_suspend, iqs626_resume);
1812f1d2809dSJeff LaBundy 
1813f1d2809dSJeff LaBundy static const struct of_device_id iqs626_of_match[] = {
1814f1d2809dSJeff LaBundy 	{ .compatible = "azoteq,iqs626a" },
1815f1d2809dSJeff LaBundy 	{ }
1816f1d2809dSJeff LaBundy };
1817f1d2809dSJeff LaBundy MODULE_DEVICE_TABLE(of, iqs626_of_match);
1818f1d2809dSJeff LaBundy 
1819f1d2809dSJeff LaBundy static struct i2c_driver iqs626_i2c_driver = {
1820f1d2809dSJeff LaBundy 	.driver = {
1821f1d2809dSJeff LaBundy 		.name = "iqs626a",
1822f1d2809dSJeff LaBundy 		.of_match_table = iqs626_of_match,
1823238e5de4SJonathan Cameron 		.pm = pm_sleep_ptr(&iqs626_pm),
1824f1d2809dSJeff LaBundy 	},
1825d8bde56dSUwe Kleine-König 	.probe = iqs626_probe,
1826f1d2809dSJeff LaBundy };
1827f1d2809dSJeff LaBundy module_i2c_driver(iqs626_i2c_driver);
1828f1d2809dSJeff LaBundy 
1829f1d2809dSJeff LaBundy MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
1830f1d2809dSJeff LaBundy MODULE_DESCRIPTION("Azoteq IQS626A Capacitive Touch Controller");
1831f1d2809dSJeff LaBundy MODULE_LICENSE("GPL");
1832