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