1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Azoteq IQS620A/621/622/624/625 Multi-Function Sensors
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * These devices rely on application-specific register settings and calibration
8*4882a593Smuzhiyun * data developed in and exported from a suite of GUIs offered by the vendor. A
9*4882a593Smuzhiyun * separate tool converts the GUIs' ASCII-based output into a standard firmware
10*4882a593Smuzhiyun * file parsed by the driver.
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * Link to datasheets and GUIs: https://www.azoteq.com/
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun * Link to conversion tool: https://github.com/jlabundy/iqs62x-h2bin.git
15*4882a593Smuzhiyun */
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include <linux/completion.h>
18*4882a593Smuzhiyun #include <linux/delay.h>
19*4882a593Smuzhiyun #include <linux/device.h>
20*4882a593Smuzhiyun #include <linux/err.h>
21*4882a593Smuzhiyun #include <linux/firmware.h>
22*4882a593Smuzhiyun #include <linux/i2c.h>
23*4882a593Smuzhiyun #include <linux/interrupt.h>
24*4882a593Smuzhiyun #include <linux/kernel.h>
25*4882a593Smuzhiyun #include <linux/list.h>
26*4882a593Smuzhiyun #include <linux/mfd/core.h>
27*4882a593Smuzhiyun #include <linux/mfd/iqs62x.h>
28*4882a593Smuzhiyun #include <linux/module.h>
29*4882a593Smuzhiyun #include <linux/notifier.h>
30*4882a593Smuzhiyun #include <linux/of_device.h>
31*4882a593Smuzhiyun #include <linux/property.h>
32*4882a593Smuzhiyun #include <linux/regmap.h>
33*4882a593Smuzhiyun #include <linux/slab.h>
34*4882a593Smuzhiyun #include <asm/unaligned.h>
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun #define IQS62X_PROD_NUM 0x00
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun #define IQS62X_SYS_FLAGS 0x10
39*4882a593Smuzhiyun #define IQS62X_SYS_FLAGS_IN_ATI BIT(2)
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun #define IQS620_HALL_FLAGS 0x16
42*4882a593Smuzhiyun #define IQS621_HALL_FLAGS 0x19
43*4882a593Smuzhiyun #define IQS622_HALL_FLAGS IQS621_HALL_FLAGS
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun #define IQS624_INTERVAL_NUM 0x18
46*4882a593Smuzhiyun #define IQS625_INTERVAL_NUM 0x12
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun #define IQS622_PROX_SETTINGS_4 0x48
49*4882a593Smuzhiyun #define IQS620_PROX_SETTINGS_4 0x50
50*4882a593Smuzhiyun #define IQS620_PROX_SETTINGS_4_SAR_EN BIT(7)
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun #define IQS621_ALS_CAL_DIV_LUX 0x82
53*4882a593Smuzhiyun #define IQS621_ALS_CAL_DIV_IR 0x83
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun #define IQS620_TEMP_CAL_MULT 0xC2
56*4882a593Smuzhiyun #define IQS620_TEMP_CAL_DIV 0xC3
57*4882a593Smuzhiyun #define IQS620_TEMP_CAL_OFFS 0xC4
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun #define IQS62X_SYS_SETTINGS 0xD0
60*4882a593Smuzhiyun #define IQS62X_SYS_SETTINGS_SOFT_RESET BIT(7)
61*4882a593Smuzhiyun #define IQS62X_SYS_SETTINGS_ACK_RESET BIT(6)
62*4882a593Smuzhiyun #define IQS62X_SYS_SETTINGS_EVENT_MODE BIT(5)
63*4882a593Smuzhiyun #define IQS62X_SYS_SETTINGS_CLK_DIV BIT(4)
64*4882a593Smuzhiyun #define IQS62X_SYS_SETTINGS_REDO_ATI BIT(1)
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun #define IQS62X_PWR_SETTINGS 0xD2
67*4882a593Smuzhiyun #define IQS62X_PWR_SETTINGS_DIS_AUTO BIT(5)
68*4882a593Smuzhiyun #define IQS62X_PWR_SETTINGS_PWR_MODE_MASK (BIT(4) | BIT(3))
69*4882a593Smuzhiyun #define IQS62X_PWR_SETTINGS_PWR_MODE_HALT (BIT(4) | BIT(3))
70*4882a593Smuzhiyun #define IQS62X_PWR_SETTINGS_PWR_MODE_NORM 0
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun #define IQS62X_OTP_CMD 0xF0
73*4882a593Smuzhiyun #define IQS62X_OTP_CMD_FG3 0x13
74*4882a593Smuzhiyun #define IQS62X_OTP_DATA 0xF1
75*4882a593Smuzhiyun #define IQS62X_MAX_REG 0xFF
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun #define IQS62X_HALL_CAL_MASK GENMASK(3, 0)
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun #define IQS62X_FW_REC_TYPE_INFO 0
80*4882a593Smuzhiyun #define IQS62X_FW_REC_TYPE_PROD 1
81*4882a593Smuzhiyun #define IQS62X_FW_REC_TYPE_HALL 2
82*4882a593Smuzhiyun #define IQS62X_FW_REC_TYPE_MASK 3
83*4882a593Smuzhiyun #define IQS62X_FW_REC_TYPE_DATA 4
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun #define IQS62X_ATI_POLL_SLEEP_US 10000
86*4882a593Smuzhiyun #define IQS62X_ATI_POLL_TIMEOUT_US 500000
87*4882a593Smuzhiyun #define IQS62X_ATI_STABLE_DELAY_MS 150
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun struct iqs62x_fw_rec {
90*4882a593Smuzhiyun u8 type;
91*4882a593Smuzhiyun u8 addr;
92*4882a593Smuzhiyun u8 len;
93*4882a593Smuzhiyun u8 data;
94*4882a593Smuzhiyun } __packed;
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun struct iqs62x_fw_blk {
97*4882a593Smuzhiyun struct list_head list;
98*4882a593Smuzhiyun u8 addr;
99*4882a593Smuzhiyun u8 mask;
100*4882a593Smuzhiyun u8 len;
101*4882a593Smuzhiyun u8 data[];
102*4882a593Smuzhiyun };
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun struct iqs62x_info {
105*4882a593Smuzhiyun u8 prod_num;
106*4882a593Smuzhiyun u8 sw_num;
107*4882a593Smuzhiyun u8 hw_num;
108*4882a593Smuzhiyun } __packed;
109*4882a593Smuzhiyun
iqs62x_dev_init(struct iqs62x_core * iqs62x)110*4882a593Smuzhiyun static int iqs62x_dev_init(struct iqs62x_core *iqs62x)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun struct iqs62x_fw_blk *fw_blk;
113*4882a593Smuzhiyun unsigned int val;
114*4882a593Smuzhiyun int ret;
115*4882a593Smuzhiyun u8 clk_div = 1;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun list_for_each_entry(fw_blk, &iqs62x->fw_blk_head, list) {
118*4882a593Smuzhiyun if (fw_blk->mask)
119*4882a593Smuzhiyun ret = regmap_update_bits(iqs62x->regmap, fw_blk->addr,
120*4882a593Smuzhiyun fw_blk->mask, *fw_blk->data);
121*4882a593Smuzhiyun else
122*4882a593Smuzhiyun ret = regmap_raw_write(iqs62x->regmap, fw_blk->addr,
123*4882a593Smuzhiyun fw_blk->data, fw_blk->len);
124*4882a593Smuzhiyun if (ret)
125*4882a593Smuzhiyun return ret;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun switch (iqs62x->dev_desc->prod_num) {
129*4882a593Smuzhiyun case IQS620_PROD_NUM:
130*4882a593Smuzhiyun case IQS622_PROD_NUM:
131*4882a593Smuzhiyun ret = regmap_read(iqs62x->regmap,
132*4882a593Smuzhiyun iqs62x->dev_desc->prox_settings, &val);
133*4882a593Smuzhiyun if (ret)
134*4882a593Smuzhiyun return ret;
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun if (val & IQS620_PROX_SETTINGS_4_SAR_EN)
137*4882a593Smuzhiyun iqs62x->ui_sel = IQS62X_UI_SAR1;
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun fallthrough;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun case IQS621_PROD_NUM:
142*4882a593Smuzhiyun ret = regmap_write(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
143*4882a593Smuzhiyun IQS620_GLBL_EVENT_MASK_PMU |
144*4882a593Smuzhiyun iqs62x->dev_desc->prox_mask |
145*4882a593Smuzhiyun iqs62x->dev_desc->sar_mask |
146*4882a593Smuzhiyun iqs62x->dev_desc->hall_mask |
147*4882a593Smuzhiyun iqs62x->dev_desc->hyst_mask |
148*4882a593Smuzhiyun iqs62x->dev_desc->temp_mask |
149*4882a593Smuzhiyun iqs62x->dev_desc->als_mask |
150*4882a593Smuzhiyun iqs62x->dev_desc->ir_mask);
151*4882a593Smuzhiyun if (ret)
152*4882a593Smuzhiyun return ret;
153*4882a593Smuzhiyun break;
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun default:
156*4882a593Smuzhiyun ret = regmap_write(iqs62x->regmap, IQS624_HALL_UI,
157*4882a593Smuzhiyun IQS624_HALL_UI_WHL_EVENT |
158*4882a593Smuzhiyun IQS624_HALL_UI_INT_EVENT |
159*4882a593Smuzhiyun IQS624_HALL_UI_AUTO_CAL);
160*4882a593Smuzhiyun if (ret)
161*4882a593Smuzhiyun return ret;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun /*
164*4882a593Smuzhiyun * The IQS625 default interval divider is below the minimum
165*4882a593Smuzhiyun * permissible value, and the datasheet mandates that it is
166*4882a593Smuzhiyun * corrected during initialization (unless an updated value
167*4882a593Smuzhiyun * has already been provided by firmware).
168*4882a593Smuzhiyun *
169*4882a593Smuzhiyun * To protect against an unacceptably low user-entered value
170*4882a593Smuzhiyun * stored in the firmware, the same check is extended to the
171*4882a593Smuzhiyun * IQS624 as well.
172*4882a593Smuzhiyun */
173*4882a593Smuzhiyun ret = regmap_read(iqs62x->regmap, IQS624_INTERVAL_DIV, &val);
174*4882a593Smuzhiyun if (ret)
175*4882a593Smuzhiyun return ret;
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun if (val >= iqs62x->dev_desc->interval_div)
178*4882a593Smuzhiyun break;
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun ret = regmap_write(iqs62x->regmap, IQS624_INTERVAL_DIV,
181*4882a593Smuzhiyun iqs62x->dev_desc->interval_div);
182*4882a593Smuzhiyun if (ret)
183*4882a593Smuzhiyun return ret;
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun ret = regmap_read(iqs62x->regmap, IQS62X_SYS_SETTINGS, &val);
187*4882a593Smuzhiyun if (ret)
188*4882a593Smuzhiyun return ret;
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun if (val & IQS62X_SYS_SETTINGS_CLK_DIV)
191*4882a593Smuzhiyun clk_div = iqs62x->dev_desc->clk_div;
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun ret = regmap_write(iqs62x->regmap, IQS62X_SYS_SETTINGS, val |
194*4882a593Smuzhiyun IQS62X_SYS_SETTINGS_ACK_RESET |
195*4882a593Smuzhiyun IQS62X_SYS_SETTINGS_EVENT_MODE |
196*4882a593Smuzhiyun IQS62X_SYS_SETTINGS_REDO_ATI);
197*4882a593Smuzhiyun if (ret)
198*4882a593Smuzhiyun return ret;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun ret = regmap_read_poll_timeout(iqs62x->regmap, IQS62X_SYS_FLAGS, val,
201*4882a593Smuzhiyun !(val & IQS62X_SYS_FLAGS_IN_ATI),
202*4882a593Smuzhiyun IQS62X_ATI_POLL_SLEEP_US,
203*4882a593Smuzhiyun IQS62X_ATI_POLL_TIMEOUT_US * clk_div);
204*4882a593Smuzhiyun if (ret)
205*4882a593Smuzhiyun return ret;
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun msleep(IQS62X_ATI_STABLE_DELAY_MS * clk_div);
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun return 0;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun
iqs62x_firmware_parse(struct iqs62x_core * iqs62x,const struct firmware * fw)212*4882a593Smuzhiyun static int iqs62x_firmware_parse(struct iqs62x_core *iqs62x,
213*4882a593Smuzhiyun const struct firmware *fw)
214*4882a593Smuzhiyun {
215*4882a593Smuzhiyun struct i2c_client *client = iqs62x->client;
216*4882a593Smuzhiyun struct iqs62x_fw_rec *fw_rec;
217*4882a593Smuzhiyun struct iqs62x_fw_blk *fw_blk;
218*4882a593Smuzhiyun unsigned int val;
219*4882a593Smuzhiyun size_t pos = 0;
220*4882a593Smuzhiyun int ret = 0;
221*4882a593Smuzhiyun u8 mask, len, *data;
222*4882a593Smuzhiyun u8 hall_cal_index = 0;
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun while (pos < fw->size) {
225*4882a593Smuzhiyun if (pos + sizeof(*fw_rec) > fw->size) {
226*4882a593Smuzhiyun ret = -EINVAL;
227*4882a593Smuzhiyun break;
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun fw_rec = (struct iqs62x_fw_rec *)(fw->data + pos);
230*4882a593Smuzhiyun pos += sizeof(*fw_rec);
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun if (pos + fw_rec->len - 1 > fw->size) {
233*4882a593Smuzhiyun ret = -EINVAL;
234*4882a593Smuzhiyun break;
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun pos += fw_rec->len - 1;
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun switch (fw_rec->type) {
239*4882a593Smuzhiyun case IQS62X_FW_REC_TYPE_INFO:
240*4882a593Smuzhiyun continue;
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun case IQS62X_FW_REC_TYPE_PROD:
243*4882a593Smuzhiyun if (fw_rec->data == iqs62x->dev_desc->prod_num)
244*4882a593Smuzhiyun continue;
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun dev_err(&client->dev,
247*4882a593Smuzhiyun "Incompatible product number: 0x%02X\n",
248*4882a593Smuzhiyun fw_rec->data);
249*4882a593Smuzhiyun ret = -EINVAL;
250*4882a593Smuzhiyun break;
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun case IQS62X_FW_REC_TYPE_HALL:
253*4882a593Smuzhiyun if (!hall_cal_index) {
254*4882a593Smuzhiyun ret = regmap_write(iqs62x->regmap,
255*4882a593Smuzhiyun IQS62X_OTP_CMD,
256*4882a593Smuzhiyun IQS62X_OTP_CMD_FG3);
257*4882a593Smuzhiyun if (ret)
258*4882a593Smuzhiyun break;
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun ret = regmap_read(iqs62x->regmap,
261*4882a593Smuzhiyun IQS62X_OTP_DATA, &val);
262*4882a593Smuzhiyun if (ret)
263*4882a593Smuzhiyun break;
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun hall_cal_index = val & IQS62X_HALL_CAL_MASK;
266*4882a593Smuzhiyun if (!hall_cal_index) {
267*4882a593Smuzhiyun dev_err(&client->dev,
268*4882a593Smuzhiyun "Uncalibrated device\n");
269*4882a593Smuzhiyun ret = -ENODATA;
270*4882a593Smuzhiyun break;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun if (hall_cal_index > fw_rec->len) {
275*4882a593Smuzhiyun ret = -EINVAL;
276*4882a593Smuzhiyun break;
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun mask = 0;
280*4882a593Smuzhiyun data = &fw_rec->data + hall_cal_index - 1;
281*4882a593Smuzhiyun len = sizeof(*data);
282*4882a593Smuzhiyun break;
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun case IQS62X_FW_REC_TYPE_MASK:
285*4882a593Smuzhiyun if (fw_rec->len < (sizeof(mask) + sizeof(*data))) {
286*4882a593Smuzhiyun ret = -EINVAL;
287*4882a593Smuzhiyun break;
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun mask = fw_rec->data;
291*4882a593Smuzhiyun data = &fw_rec->data + sizeof(mask);
292*4882a593Smuzhiyun len = sizeof(*data);
293*4882a593Smuzhiyun break;
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun case IQS62X_FW_REC_TYPE_DATA:
296*4882a593Smuzhiyun mask = 0;
297*4882a593Smuzhiyun data = &fw_rec->data;
298*4882a593Smuzhiyun len = fw_rec->len;
299*4882a593Smuzhiyun break;
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun default:
302*4882a593Smuzhiyun dev_err(&client->dev,
303*4882a593Smuzhiyun "Unrecognized record type: 0x%02X\n",
304*4882a593Smuzhiyun fw_rec->type);
305*4882a593Smuzhiyun ret = -EINVAL;
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun if (ret)
309*4882a593Smuzhiyun break;
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun fw_blk = devm_kzalloc(&client->dev,
312*4882a593Smuzhiyun struct_size(fw_blk, data, len),
313*4882a593Smuzhiyun GFP_KERNEL);
314*4882a593Smuzhiyun if (!fw_blk) {
315*4882a593Smuzhiyun ret = -ENOMEM;
316*4882a593Smuzhiyun break;
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun fw_blk->addr = fw_rec->addr;
320*4882a593Smuzhiyun fw_blk->mask = mask;
321*4882a593Smuzhiyun fw_blk->len = len;
322*4882a593Smuzhiyun memcpy(fw_blk->data, data, len);
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun list_add(&fw_blk->list, &iqs62x->fw_blk_head);
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun release_firmware(fw);
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun return ret;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun const struct iqs62x_event_desc iqs62x_events[IQS62X_NUM_EVENTS] = {
333*4882a593Smuzhiyun [IQS62X_EVENT_PROX_CH0_T] = {
334*4882a593Smuzhiyun .reg = IQS62X_EVENT_PROX,
335*4882a593Smuzhiyun .mask = BIT(4),
336*4882a593Smuzhiyun .val = BIT(4),
337*4882a593Smuzhiyun },
338*4882a593Smuzhiyun [IQS62X_EVENT_PROX_CH0_P] = {
339*4882a593Smuzhiyun .reg = IQS62X_EVENT_PROX,
340*4882a593Smuzhiyun .mask = BIT(0),
341*4882a593Smuzhiyun .val = BIT(0),
342*4882a593Smuzhiyun },
343*4882a593Smuzhiyun [IQS62X_EVENT_PROX_CH1_T] = {
344*4882a593Smuzhiyun .reg = IQS62X_EVENT_PROX,
345*4882a593Smuzhiyun .mask = BIT(5),
346*4882a593Smuzhiyun .val = BIT(5),
347*4882a593Smuzhiyun },
348*4882a593Smuzhiyun [IQS62X_EVENT_PROX_CH1_P] = {
349*4882a593Smuzhiyun .reg = IQS62X_EVENT_PROX,
350*4882a593Smuzhiyun .mask = BIT(1),
351*4882a593Smuzhiyun .val = BIT(1),
352*4882a593Smuzhiyun },
353*4882a593Smuzhiyun [IQS62X_EVENT_PROX_CH2_T] = {
354*4882a593Smuzhiyun .reg = IQS62X_EVENT_PROX,
355*4882a593Smuzhiyun .mask = BIT(6),
356*4882a593Smuzhiyun .val = BIT(6),
357*4882a593Smuzhiyun },
358*4882a593Smuzhiyun [IQS62X_EVENT_PROX_CH2_P] = {
359*4882a593Smuzhiyun .reg = IQS62X_EVENT_PROX,
360*4882a593Smuzhiyun .mask = BIT(2),
361*4882a593Smuzhiyun .val = BIT(2),
362*4882a593Smuzhiyun },
363*4882a593Smuzhiyun [IQS62X_EVENT_HYST_POS_T] = {
364*4882a593Smuzhiyun .reg = IQS62X_EVENT_HYST,
365*4882a593Smuzhiyun .mask = BIT(6) | BIT(7),
366*4882a593Smuzhiyun .val = BIT(6),
367*4882a593Smuzhiyun },
368*4882a593Smuzhiyun [IQS62X_EVENT_HYST_POS_P] = {
369*4882a593Smuzhiyun .reg = IQS62X_EVENT_HYST,
370*4882a593Smuzhiyun .mask = BIT(5) | BIT(7),
371*4882a593Smuzhiyun .val = BIT(5),
372*4882a593Smuzhiyun },
373*4882a593Smuzhiyun [IQS62X_EVENT_HYST_NEG_T] = {
374*4882a593Smuzhiyun .reg = IQS62X_EVENT_HYST,
375*4882a593Smuzhiyun .mask = BIT(6) | BIT(7),
376*4882a593Smuzhiyun .val = BIT(6) | BIT(7),
377*4882a593Smuzhiyun },
378*4882a593Smuzhiyun [IQS62X_EVENT_HYST_NEG_P] = {
379*4882a593Smuzhiyun .reg = IQS62X_EVENT_HYST,
380*4882a593Smuzhiyun .mask = BIT(5) | BIT(7),
381*4882a593Smuzhiyun .val = BIT(5) | BIT(7),
382*4882a593Smuzhiyun },
383*4882a593Smuzhiyun [IQS62X_EVENT_SAR1_ACT] = {
384*4882a593Smuzhiyun .reg = IQS62X_EVENT_HYST,
385*4882a593Smuzhiyun .mask = BIT(4),
386*4882a593Smuzhiyun .val = BIT(4),
387*4882a593Smuzhiyun },
388*4882a593Smuzhiyun [IQS62X_EVENT_SAR1_QRD] = {
389*4882a593Smuzhiyun .reg = IQS62X_EVENT_HYST,
390*4882a593Smuzhiyun .mask = BIT(2),
391*4882a593Smuzhiyun .val = BIT(2),
392*4882a593Smuzhiyun },
393*4882a593Smuzhiyun [IQS62X_EVENT_SAR1_MOVE] = {
394*4882a593Smuzhiyun .reg = IQS62X_EVENT_HYST,
395*4882a593Smuzhiyun .mask = BIT(1),
396*4882a593Smuzhiyun .val = BIT(1),
397*4882a593Smuzhiyun },
398*4882a593Smuzhiyun [IQS62X_EVENT_SAR1_HALT] = {
399*4882a593Smuzhiyun .reg = IQS62X_EVENT_HYST,
400*4882a593Smuzhiyun .mask = BIT(0),
401*4882a593Smuzhiyun .val = BIT(0),
402*4882a593Smuzhiyun },
403*4882a593Smuzhiyun [IQS62X_EVENT_WHEEL_UP] = {
404*4882a593Smuzhiyun .reg = IQS62X_EVENT_WHEEL,
405*4882a593Smuzhiyun .mask = BIT(7) | BIT(6),
406*4882a593Smuzhiyun .val = BIT(7),
407*4882a593Smuzhiyun },
408*4882a593Smuzhiyun [IQS62X_EVENT_WHEEL_DN] = {
409*4882a593Smuzhiyun .reg = IQS62X_EVENT_WHEEL,
410*4882a593Smuzhiyun .mask = BIT(7) | BIT(6),
411*4882a593Smuzhiyun .val = BIT(7) | BIT(6),
412*4882a593Smuzhiyun },
413*4882a593Smuzhiyun [IQS62X_EVENT_HALL_N_T] = {
414*4882a593Smuzhiyun .reg = IQS62X_EVENT_HALL,
415*4882a593Smuzhiyun .mask = BIT(2) | BIT(0),
416*4882a593Smuzhiyun .val = BIT(2),
417*4882a593Smuzhiyun },
418*4882a593Smuzhiyun [IQS62X_EVENT_HALL_N_P] = {
419*4882a593Smuzhiyun .reg = IQS62X_EVENT_HALL,
420*4882a593Smuzhiyun .mask = BIT(1) | BIT(0),
421*4882a593Smuzhiyun .val = BIT(1),
422*4882a593Smuzhiyun },
423*4882a593Smuzhiyun [IQS62X_EVENT_HALL_S_T] = {
424*4882a593Smuzhiyun .reg = IQS62X_EVENT_HALL,
425*4882a593Smuzhiyun .mask = BIT(2) | BIT(0),
426*4882a593Smuzhiyun .val = BIT(2) | BIT(0),
427*4882a593Smuzhiyun },
428*4882a593Smuzhiyun [IQS62X_EVENT_HALL_S_P] = {
429*4882a593Smuzhiyun .reg = IQS62X_EVENT_HALL,
430*4882a593Smuzhiyun .mask = BIT(1) | BIT(0),
431*4882a593Smuzhiyun .val = BIT(1) | BIT(0),
432*4882a593Smuzhiyun },
433*4882a593Smuzhiyun [IQS62X_EVENT_SYS_RESET] = {
434*4882a593Smuzhiyun .reg = IQS62X_EVENT_SYS,
435*4882a593Smuzhiyun .mask = BIT(7),
436*4882a593Smuzhiyun .val = BIT(7),
437*4882a593Smuzhiyun },
438*4882a593Smuzhiyun };
439*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(iqs62x_events);
440*4882a593Smuzhiyun
iqs62x_irq(int irq,void * context)441*4882a593Smuzhiyun static irqreturn_t iqs62x_irq(int irq, void *context)
442*4882a593Smuzhiyun {
443*4882a593Smuzhiyun struct iqs62x_core *iqs62x = context;
444*4882a593Smuzhiyun struct i2c_client *client = iqs62x->client;
445*4882a593Smuzhiyun struct iqs62x_event_data event_data;
446*4882a593Smuzhiyun struct iqs62x_event_desc event_desc;
447*4882a593Smuzhiyun enum iqs62x_event_reg event_reg;
448*4882a593Smuzhiyun unsigned long event_flags = 0;
449*4882a593Smuzhiyun int ret, i, j;
450*4882a593Smuzhiyun u8 event_map[IQS62X_EVENT_SIZE];
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun /*
453*4882a593Smuzhiyun * The device asserts the RDY output to signal the beginning of a
454*4882a593Smuzhiyun * communication window, which is closed by an I2C stop condition.
455*4882a593Smuzhiyun * As such, all interrupt status is captured in a single read and
456*4882a593Smuzhiyun * broadcast to any interested sub-device drivers.
457*4882a593Smuzhiyun */
458*4882a593Smuzhiyun ret = regmap_raw_read(iqs62x->regmap, IQS62X_SYS_FLAGS, event_map,
459*4882a593Smuzhiyun sizeof(event_map));
460*4882a593Smuzhiyun if (ret) {
461*4882a593Smuzhiyun dev_err(&client->dev, "Failed to read device status: %d\n",
462*4882a593Smuzhiyun ret);
463*4882a593Smuzhiyun return IRQ_NONE;
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun for (i = 0; i < sizeof(event_map); i++) {
467*4882a593Smuzhiyun event_reg = iqs62x->dev_desc->event_regs[iqs62x->ui_sel][i];
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun switch (event_reg) {
470*4882a593Smuzhiyun case IQS62X_EVENT_UI_LO:
471*4882a593Smuzhiyun event_data.ui_data = get_unaligned_le16(&event_map[i]);
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun fallthrough;
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun case IQS62X_EVENT_UI_HI:
476*4882a593Smuzhiyun case IQS62X_EVENT_NONE:
477*4882a593Smuzhiyun continue;
478*4882a593Smuzhiyun
479*4882a593Smuzhiyun case IQS62X_EVENT_ALS:
480*4882a593Smuzhiyun event_data.als_flags = event_map[i];
481*4882a593Smuzhiyun continue;
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun case IQS62X_EVENT_IR:
484*4882a593Smuzhiyun event_data.ir_flags = event_map[i];
485*4882a593Smuzhiyun continue;
486*4882a593Smuzhiyun
487*4882a593Smuzhiyun case IQS62X_EVENT_INTER:
488*4882a593Smuzhiyun event_data.interval = event_map[i];
489*4882a593Smuzhiyun continue;
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun case IQS62X_EVENT_HYST:
492*4882a593Smuzhiyun event_map[i] <<= iqs62x->dev_desc->hyst_shift;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun fallthrough;
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun case IQS62X_EVENT_WHEEL:
497*4882a593Smuzhiyun case IQS62X_EVENT_HALL:
498*4882a593Smuzhiyun case IQS62X_EVENT_PROX:
499*4882a593Smuzhiyun case IQS62X_EVENT_SYS:
500*4882a593Smuzhiyun break;
501*4882a593Smuzhiyun }
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun for (j = 0; j < IQS62X_NUM_EVENTS; j++) {
504*4882a593Smuzhiyun event_desc = iqs62x_events[j];
505*4882a593Smuzhiyun
506*4882a593Smuzhiyun if (event_desc.reg != event_reg)
507*4882a593Smuzhiyun continue;
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun if ((event_map[i] & event_desc.mask) == event_desc.val)
510*4882a593Smuzhiyun event_flags |= BIT(j);
511*4882a593Smuzhiyun }
512*4882a593Smuzhiyun }
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun /*
515*4882a593Smuzhiyun * The device resets itself in response to the I2C master stalling
516*4882a593Smuzhiyun * communication past a fixed timeout. In this case, all registers
517*4882a593Smuzhiyun * are restored and any interested sub-device drivers are notified.
518*4882a593Smuzhiyun */
519*4882a593Smuzhiyun if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) {
520*4882a593Smuzhiyun dev_err(&client->dev, "Unexpected device reset\n");
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun ret = iqs62x_dev_init(iqs62x);
523*4882a593Smuzhiyun if (ret) {
524*4882a593Smuzhiyun dev_err(&client->dev,
525*4882a593Smuzhiyun "Failed to re-initialize device: %d\n", ret);
526*4882a593Smuzhiyun return IRQ_NONE;
527*4882a593Smuzhiyun }
528*4882a593Smuzhiyun }
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun ret = blocking_notifier_call_chain(&iqs62x->nh, event_flags,
531*4882a593Smuzhiyun &event_data);
532*4882a593Smuzhiyun if (ret & NOTIFY_STOP_MASK)
533*4882a593Smuzhiyun return IRQ_NONE;
534*4882a593Smuzhiyun
535*4882a593Smuzhiyun /*
536*4882a593Smuzhiyun * Once the communication window is closed, a small delay is added to
537*4882a593Smuzhiyun * ensure the device's RDY output has been deasserted by the time the
538*4882a593Smuzhiyun * interrupt handler returns.
539*4882a593Smuzhiyun */
540*4882a593Smuzhiyun usleep_range(50, 100);
541*4882a593Smuzhiyun
542*4882a593Smuzhiyun return IRQ_HANDLED;
543*4882a593Smuzhiyun }
544*4882a593Smuzhiyun
iqs62x_firmware_load(const struct firmware * fw,void * context)545*4882a593Smuzhiyun static void iqs62x_firmware_load(const struct firmware *fw, void *context)
546*4882a593Smuzhiyun {
547*4882a593Smuzhiyun struct iqs62x_core *iqs62x = context;
548*4882a593Smuzhiyun struct i2c_client *client = iqs62x->client;
549*4882a593Smuzhiyun int ret;
550*4882a593Smuzhiyun
551*4882a593Smuzhiyun if (fw) {
552*4882a593Smuzhiyun ret = iqs62x_firmware_parse(iqs62x, fw);
553*4882a593Smuzhiyun if (ret) {
554*4882a593Smuzhiyun dev_err(&client->dev, "Failed to parse firmware: %d\n",
555*4882a593Smuzhiyun ret);
556*4882a593Smuzhiyun goto err_out;
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun }
559*4882a593Smuzhiyun
560*4882a593Smuzhiyun ret = iqs62x_dev_init(iqs62x);
561*4882a593Smuzhiyun if (ret) {
562*4882a593Smuzhiyun dev_err(&client->dev, "Failed to initialize device: %d\n", ret);
563*4882a593Smuzhiyun goto err_out;
564*4882a593Smuzhiyun }
565*4882a593Smuzhiyun
566*4882a593Smuzhiyun ret = devm_request_threaded_irq(&client->dev, client->irq,
567*4882a593Smuzhiyun NULL, iqs62x_irq, IRQF_ONESHOT,
568*4882a593Smuzhiyun client->name, iqs62x);
569*4882a593Smuzhiyun if (ret) {
570*4882a593Smuzhiyun dev_err(&client->dev, "Failed to request IRQ: %d\n", ret);
571*4882a593Smuzhiyun goto err_out;
572*4882a593Smuzhiyun }
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
575*4882a593Smuzhiyun iqs62x->dev_desc->sub_devs,
576*4882a593Smuzhiyun iqs62x->dev_desc->num_sub_devs,
577*4882a593Smuzhiyun NULL, 0, NULL);
578*4882a593Smuzhiyun if (ret)
579*4882a593Smuzhiyun dev_err(&client->dev, "Failed to add sub-devices: %d\n", ret);
580*4882a593Smuzhiyun
581*4882a593Smuzhiyun err_out:
582*4882a593Smuzhiyun complete_all(&iqs62x->fw_done);
583*4882a593Smuzhiyun }
584*4882a593Smuzhiyun
585*4882a593Smuzhiyun static const struct mfd_cell iqs620at_sub_devs[] = {
586*4882a593Smuzhiyun {
587*4882a593Smuzhiyun .name = "iqs62x-keys",
588*4882a593Smuzhiyun .of_compatible = "azoteq,iqs620a-keys",
589*4882a593Smuzhiyun },
590*4882a593Smuzhiyun {
591*4882a593Smuzhiyun .name = "iqs620a-pwm",
592*4882a593Smuzhiyun .of_compatible = "azoteq,iqs620a-pwm",
593*4882a593Smuzhiyun },
594*4882a593Smuzhiyun { .name = "iqs620at-temp", },
595*4882a593Smuzhiyun };
596*4882a593Smuzhiyun
597*4882a593Smuzhiyun static const struct mfd_cell iqs620a_sub_devs[] = {
598*4882a593Smuzhiyun {
599*4882a593Smuzhiyun .name = "iqs62x-keys",
600*4882a593Smuzhiyun .of_compatible = "azoteq,iqs620a-keys",
601*4882a593Smuzhiyun },
602*4882a593Smuzhiyun {
603*4882a593Smuzhiyun .name = "iqs620a-pwm",
604*4882a593Smuzhiyun .of_compatible = "azoteq,iqs620a-pwm",
605*4882a593Smuzhiyun },
606*4882a593Smuzhiyun };
607*4882a593Smuzhiyun
608*4882a593Smuzhiyun static const struct mfd_cell iqs621_sub_devs[] = {
609*4882a593Smuzhiyun {
610*4882a593Smuzhiyun .name = "iqs62x-keys",
611*4882a593Smuzhiyun .of_compatible = "azoteq,iqs621-keys",
612*4882a593Smuzhiyun },
613*4882a593Smuzhiyun { .name = "iqs621-als", },
614*4882a593Smuzhiyun };
615*4882a593Smuzhiyun
616*4882a593Smuzhiyun static const struct mfd_cell iqs622_sub_devs[] = {
617*4882a593Smuzhiyun {
618*4882a593Smuzhiyun .name = "iqs62x-keys",
619*4882a593Smuzhiyun .of_compatible = "azoteq,iqs622-keys",
620*4882a593Smuzhiyun },
621*4882a593Smuzhiyun { .name = "iqs621-als", },
622*4882a593Smuzhiyun };
623*4882a593Smuzhiyun
624*4882a593Smuzhiyun static const struct mfd_cell iqs624_sub_devs[] = {
625*4882a593Smuzhiyun {
626*4882a593Smuzhiyun .name = "iqs62x-keys",
627*4882a593Smuzhiyun .of_compatible = "azoteq,iqs624-keys",
628*4882a593Smuzhiyun },
629*4882a593Smuzhiyun { .name = "iqs624-pos", },
630*4882a593Smuzhiyun };
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun static const struct mfd_cell iqs625_sub_devs[] = {
633*4882a593Smuzhiyun {
634*4882a593Smuzhiyun .name = "iqs62x-keys",
635*4882a593Smuzhiyun .of_compatible = "azoteq,iqs625-keys",
636*4882a593Smuzhiyun },
637*4882a593Smuzhiyun { .name = "iqs624-pos", },
638*4882a593Smuzhiyun };
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun static const u8 iqs620at_cal_regs[] = {
641*4882a593Smuzhiyun IQS620_TEMP_CAL_MULT,
642*4882a593Smuzhiyun IQS620_TEMP_CAL_DIV,
643*4882a593Smuzhiyun IQS620_TEMP_CAL_OFFS,
644*4882a593Smuzhiyun };
645*4882a593Smuzhiyun
646*4882a593Smuzhiyun static const u8 iqs621_cal_regs[] = {
647*4882a593Smuzhiyun IQS621_ALS_CAL_DIV_LUX,
648*4882a593Smuzhiyun IQS621_ALS_CAL_DIV_IR,
649*4882a593Smuzhiyun };
650*4882a593Smuzhiyun
651*4882a593Smuzhiyun static const enum iqs62x_event_reg iqs620a_event_regs[][IQS62X_EVENT_SIZE] = {
652*4882a593Smuzhiyun [IQS62X_UI_PROX] = {
653*4882a593Smuzhiyun IQS62X_EVENT_SYS, /* 0x10 */
654*4882a593Smuzhiyun IQS62X_EVENT_NONE,
655*4882a593Smuzhiyun IQS62X_EVENT_PROX, /* 0x12 */
656*4882a593Smuzhiyun IQS62X_EVENT_HYST, /* 0x13 */
657*4882a593Smuzhiyun IQS62X_EVENT_NONE,
658*4882a593Smuzhiyun IQS62X_EVENT_NONE,
659*4882a593Smuzhiyun IQS62X_EVENT_HALL, /* 0x16 */
660*4882a593Smuzhiyun IQS62X_EVENT_NONE,
661*4882a593Smuzhiyun IQS62X_EVENT_NONE,
662*4882a593Smuzhiyun IQS62X_EVENT_NONE,
663*4882a593Smuzhiyun },
664*4882a593Smuzhiyun [IQS62X_UI_SAR1] = {
665*4882a593Smuzhiyun IQS62X_EVENT_SYS, /* 0x10 */
666*4882a593Smuzhiyun IQS62X_EVENT_NONE,
667*4882a593Smuzhiyun IQS62X_EVENT_NONE,
668*4882a593Smuzhiyun IQS62X_EVENT_HYST, /* 0x13 */
669*4882a593Smuzhiyun IQS62X_EVENT_NONE,
670*4882a593Smuzhiyun IQS62X_EVENT_NONE,
671*4882a593Smuzhiyun IQS62X_EVENT_HALL, /* 0x16 */
672*4882a593Smuzhiyun IQS62X_EVENT_NONE,
673*4882a593Smuzhiyun IQS62X_EVENT_NONE,
674*4882a593Smuzhiyun IQS62X_EVENT_NONE,
675*4882a593Smuzhiyun },
676*4882a593Smuzhiyun };
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun static const enum iqs62x_event_reg iqs621_event_regs[][IQS62X_EVENT_SIZE] = {
679*4882a593Smuzhiyun [IQS62X_UI_PROX] = {
680*4882a593Smuzhiyun IQS62X_EVENT_SYS, /* 0x10 */
681*4882a593Smuzhiyun IQS62X_EVENT_NONE,
682*4882a593Smuzhiyun IQS62X_EVENT_PROX, /* 0x12 */
683*4882a593Smuzhiyun IQS62X_EVENT_HYST, /* 0x13 */
684*4882a593Smuzhiyun IQS62X_EVENT_NONE,
685*4882a593Smuzhiyun IQS62X_EVENT_NONE,
686*4882a593Smuzhiyun IQS62X_EVENT_ALS, /* 0x16 */
687*4882a593Smuzhiyun IQS62X_EVENT_UI_LO, /* 0x17 */
688*4882a593Smuzhiyun IQS62X_EVENT_UI_HI, /* 0x18 */
689*4882a593Smuzhiyun IQS62X_EVENT_HALL, /* 0x19 */
690*4882a593Smuzhiyun },
691*4882a593Smuzhiyun };
692*4882a593Smuzhiyun
693*4882a593Smuzhiyun static const enum iqs62x_event_reg iqs622_event_regs[][IQS62X_EVENT_SIZE] = {
694*4882a593Smuzhiyun [IQS62X_UI_PROX] = {
695*4882a593Smuzhiyun IQS62X_EVENT_SYS, /* 0x10 */
696*4882a593Smuzhiyun IQS62X_EVENT_NONE,
697*4882a593Smuzhiyun IQS62X_EVENT_PROX, /* 0x12 */
698*4882a593Smuzhiyun IQS62X_EVENT_NONE,
699*4882a593Smuzhiyun IQS62X_EVENT_ALS, /* 0x14 */
700*4882a593Smuzhiyun IQS62X_EVENT_NONE,
701*4882a593Smuzhiyun IQS62X_EVENT_IR, /* 0x16 */
702*4882a593Smuzhiyun IQS62X_EVENT_UI_LO, /* 0x17 */
703*4882a593Smuzhiyun IQS62X_EVENT_UI_HI, /* 0x18 */
704*4882a593Smuzhiyun IQS62X_EVENT_HALL, /* 0x19 */
705*4882a593Smuzhiyun },
706*4882a593Smuzhiyun [IQS62X_UI_SAR1] = {
707*4882a593Smuzhiyun IQS62X_EVENT_SYS, /* 0x10 */
708*4882a593Smuzhiyun IQS62X_EVENT_NONE,
709*4882a593Smuzhiyun IQS62X_EVENT_NONE,
710*4882a593Smuzhiyun IQS62X_EVENT_HYST, /* 0x13 */
711*4882a593Smuzhiyun IQS62X_EVENT_ALS, /* 0x14 */
712*4882a593Smuzhiyun IQS62X_EVENT_NONE,
713*4882a593Smuzhiyun IQS62X_EVENT_IR, /* 0x16 */
714*4882a593Smuzhiyun IQS62X_EVENT_UI_LO, /* 0x17 */
715*4882a593Smuzhiyun IQS62X_EVENT_UI_HI, /* 0x18 */
716*4882a593Smuzhiyun IQS62X_EVENT_HALL, /* 0x19 */
717*4882a593Smuzhiyun },
718*4882a593Smuzhiyun };
719*4882a593Smuzhiyun
720*4882a593Smuzhiyun static const enum iqs62x_event_reg iqs624_event_regs[][IQS62X_EVENT_SIZE] = {
721*4882a593Smuzhiyun [IQS62X_UI_PROX] = {
722*4882a593Smuzhiyun IQS62X_EVENT_SYS, /* 0x10 */
723*4882a593Smuzhiyun IQS62X_EVENT_NONE,
724*4882a593Smuzhiyun IQS62X_EVENT_PROX, /* 0x12 */
725*4882a593Smuzhiyun IQS62X_EVENT_NONE,
726*4882a593Smuzhiyun IQS62X_EVENT_WHEEL, /* 0x14 */
727*4882a593Smuzhiyun IQS62X_EVENT_NONE,
728*4882a593Smuzhiyun IQS62X_EVENT_UI_LO, /* 0x16 */
729*4882a593Smuzhiyun IQS62X_EVENT_UI_HI, /* 0x17 */
730*4882a593Smuzhiyun IQS62X_EVENT_INTER, /* 0x18 */
731*4882a593Smuzhiyun IQS62X_EVENT_NONE,
732*4882a593Smuzhiyun },
733*4882a593Smuzhiyun };
734*4882a593Smuzhiyun
735*4882a593Smuzhiyun static const enum iqs62x_event_reg iqs625_event_regs[][IQS62X_EVENT_SIZE] = {
736*4882a593Smuzhiyun [IQS62X_UI_PROX] = {
737*4882a593Smuzhiyun IQS62X_EVENT_SYS, /* 0x10 */
738*4882a593Smuzhiyun IQS62X_EVENT_PROX, /* 0x11 */
739*4882a593Smuzhiyun IQS62X_EVENT_INTER, /* 0x12 */
740*4882a593Smuzhiyun IQS62X_EVENT_NONE,
741*4882a593Smuzhiyun IQS62X_EVENT_NONE,
742*4882a593Smuzhiyun IQS62X_EVENT_NONE,
743*4882a593Smuzhiyun IQS62X_EVENT_NONE,
744*4882a593Smuzhiyun IQS62X_EVENT_NONE,
745*4882a593Smuzhiyun IQS62X_EVENT_NONE,
746*4882a593Smuzhiyun IQS62X_EVENT_NONE,
747*4882a593Smuzhiyun },
748*4882a593Smuzhiyun };
749*4882a593Smuzhiyun
750*4882a593Smuzhiyun static const struct iqs62x_dev_desc iqs62x_devs[] = {
751*4882a593Smuzhiyun {
752*4882a593Smuzhiyun .dev_name = "iqs620at",
753*4882a593Smuzhiyun .sub_devs = iqs620at_sub_devs,
754*4882a593Smuzhiyun .num_sub_devs = ARRAY_SIZE(iqs620at_sub_devs),
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun .prod_num = IQS620_PROD_NUM,
757*4882a593Smuzhiyun .sw_num = 0x08,
758*4882a593Smuzhiyun .cal_regs = iqs620at_cal_regs,
759*4882a593Smuzhiyun .num_cal_regs = ARRAY_SIZE(iqs620at_cal_regs),
760*4882a593Smuzhiyun
761*4882a593Smuzhiyun .prox_mask = BIT(0),
762*4882a593Smuzhiyun .sar_mask = BIT(1) | BIT(7),
763*4882a593Smuzhiyun .hall_mask = BIT(2),
764*4882a593Smuzhiyun .hyst_mask = BIT(3),
765*4882a593Smuzhiyun .temp_mask = BIT(4),
766*4882a593Smuzhiyun
767*4882a593Smuzhiyun .prox_settings = IQS620_PROX_SETTINGS_4,
768*4882a593Smuzhiyun .hall_flags = IQS620_HALL_FLAGS,
769*4882a593Smuzhiyun
770*4882a593Smuzhiyun .clk_div = 4,
771*4882a593Smuzhiyun .fw_name = "iqs620a.bin",
772*4882a593Smuzhiyun .event_regs = &iqs620a_event_regs[IQS62X_UI_PROX],
773*4882a593Smuzhiyun },
774*4882a593Smuzhiyun {
775*4882a593Smuzhiyun .dev_name = "iqs620a",
776*4882a593Smuzhiyun .sub_devs = iqs620a_sub_devs,
777*4882a593Smuzhiyun .num_sub_devs = ARRAY_SIZE(iqs620a_sub_devs),
778*4882a593Smuzhiyun
779*4882a593Smuzhiyun .prod_num = IQS620_PROD_NUM,
780*4882a593Smuzhiyun .sw_num = 0x08,
781*4882a593Smuzhiyun
782*4882a593Smuzhiyun .prox_mask = BIT(0),
783*4882a593Smuzhiyun .sar_mask = BIT(1) | BIT(7),
784*4882a593Smuzhiyun .hall_mask = BIT(2),
785*4882a593Smuzhiyun .hyst_mask = BIT(3),
786*4882a593Smuzhiyun .temp_mask = BIT(4),
787*4882a593Smuzhiyun
788*4882a593Smuzhiyun .prox_settings = IQS620_PROX_SETTINGS_4,
789*4882a593Smuzhiyun .hall_flags = IQS620_HALL_FLAGS,
790*4882a593Smuzhiyun
791*4882a593Smuzhiyun .clk_div = 4,
792*4882a593Smuzhiyun .fw_name = "iqs620a.bin",
793*4882a593Smuzhiyun .event_regs = &iqs620a_event_regs[IQS62X_UI_PROX],
794*4882a593Smuzhiyun },
795*4882a593Smuzhiyun {
796*4882a593Smuzhiyun .dev_name = "iqs621",
797*4882a593Smuzhiyun .sub_devs = iqs621_sub_devs,
798*4882a593Smuzhiyun .num_sub_devs = ARRAY_SIZE(iqs621_sub_devs),
799*4882a593Smuzhiyun
800*4882a593Smuzhiyun .prod_num = IQS621_PROD_NUM,
801*4882a593Smuzhiyun .sw_num = 0x09,
802*4882a593Smuzhiyun .cal_regs = iqs621_cal_regs,
803*4882a593Smuzhiyun .num_cal_regs = ARRAY_SIZE(iqs621_cal_regs),
804*4882a593Smuzhiyun
805*4882a593Smuzhiyun .prox_mask = BIT(0),
806*4882a593Smuzhiyun .hall_mask = BIT(1),
807*4882a593Smuzhiyun .als_mask = BIT(2),
808*4882a593Smuzhiyun .hyst_mask = BIT(3),
809*4882a593Smuzhiyun .temp_mask = BIT(4),
810*4882a593Smuzhiyun
811*4882a593Smuzhiyun .als_flags = IQS621_ALS_FLAGS,
812*4882a593Smuzhiyun .hall_flags = IQS621_HALL_FLAGS,
813*4882a593Smuzhiyun .hyst_shift = 5,
814*4882a593Smuzhiyun
815*4882a593Smuzhiyun .clk_div = 2,
816*4882a593Smuzhiyun .fw_name = "iqs621.bin",
817*4882a593Smuzhiyun .event_regs = &iqs621_event_regs[IQS62X_UI_PROX],
818*4882a593Smuzhiyun },
819*4882a593Smuzhiyun {
820*4882a593Smuzhiyun .dev_name = "iqs622",
821*4882a593Smuzhiyun .sub_devs = iqs622_sub_devs,
822*4882a593Smuzhiyun .num_sub_devs = ARRAY_SIZE(iqs622_sub_devs),
823*4882a593Smuzhiyun
824*4882a593Smuzhiyun .prod_num = IQS622_PROD_NUM,
825*4882a593Smuzhiyun .sw_num = 0x06,
826*4882a593Smuzhiyun
827*4882a593Smuzhiyun .prox_mask = BIT(0),
828*4882a593Smuzhiyun .sar_mask = BIT(1),
829*4882a593Smuzhiyun .hall_mask = BIT(2),
830*4882a593Smuzhiyun .als_mask = BIT(3),
831*4882a593Smuzhiyun .ir_mask = BIT(4),
832*4882a593Smuzhiyun
833*4882a593Smuzhiyun .prox_settings = IQS622_PROX_SETTINGS_4,
834*4882a593Smuzhiyun .als_flags = IQS622_ALS_FLAGS,
835*4882a593Smuzhiyun .hall_flags = IQS622_HALL_FLAGS,
836*4882a593Smuzhiyun
837*4882a593Smuzhiyun .clk_div = 2,
838*4882a593Smuzhiyun .fw_name = "iqs622.bin",
839*4882a593Smuzhiyun .event_regs = &iqs622_event_regs[IQS62X_UI_PROX],
840*4882a593Smuzhiyun },
841*4882a593Smuzhiyun {
842*4882a593Smuzhiyun .dev_name = "iqs624",
843*4882a593Smuzhiyun .sub_devs = iqs624_sub_devs,
844*4882a593Smuzhiyun .num_sub_devs = ARRAY_SIZE(iqs624_sub_devs),
845*4882a593Smuzhiyun
846*4882a593Smuzhiyun .prod_num = IQS624_PROD_NUM,
847*4882a593Smuzhiyun .sw_num = 0x0B,
848*4882a593Smuzhiyun
849*4882a593Smuzhiyun .interval = IQS624_INTERVAL_NUM,
850*4882a593Smuzhiyun .interval_div = 3,
851*4882a593Smuzhiyun
852*4882a593Smuzhiyun .clk_div = 2,
853*4882a593Smuzhiyun .fw_name = "iqs624.bin",
854*4882a593Smuzhiyun .event_regs = &iqs624_event_regs[IQS62X_UI_PROX],
855*4882a593Smuzhiyun },
856*4882a593Smuzhiyun {
857*4882a593Smuzhiyun .dev_name = "iqs625",
858*4882a593Smuzhiyun .sub_devs = iqs625_sub_devs,
859*4882a593Smuzhiyun .num_sub_devs = ARRAY_SIZE(iqs625_sub_devs),
860*4882a593Smuzhiyun
861*4882a593Smuzhiyun .prod_num = IQS625_PROD_NUM,
862*4882a593Smuzhiyun .sw_num = 0x0B,
863*4882a593Smuzhiyun
864*4882a593Smuzhiyun .interval = IQS625_INTERVAL_NUM,
865*4882a593Smuzhiyun .interval_div = 10,
866*4882a593Smuzhiyun
867*4882a593Smuzhiyun .clk_div = 2,
868*4882a593Smuzhiyun .fw_name = "iqs625.bin",
869*4882a593Smuzhiyun .event_regs = &iqs625_event_regs[IQS62X_UI_PROX],
870*4882a593Smuzhiyun },
871*4882a593Smuzhiyun };
872*4882a593Smuzhiyun
873*4882a593Smuzhiyun static const struct regmap_config iqs62x_map_config = {
874*4882a593Smuzhiyun .reg_bits = 8,
875*4882a593Smuzhiyun .val_bits = 8,
876*4882a593Smuzhiyun .max_register = IQS62X_MAX_REG,
877*4882a593Smuzhiyun };
878*4882a593Smuzhiyun
iqs62x_probe(struct i2c_client * client)879*4882a593Smuzhiyun static int iqs62x_probe(struct i2c_client *client)
880*4882a593Smuzhiyun {
881*4882a593Smuzhiyun struct iqs62x_core *iqs62x;
882*4882a593Smuzhiyun struct iqs62x_info info;
883*4882a593Smuzhiyun unsigned int val;
884*4882a593Smuzhiyun int ret, i, j;
885*4882a593Smuzhiyun u8 sw_num = 0;
886*4882a593Smuzhiyun const char *fw_name = NULL;
887*4882a593Smuzhiyun
888*4882a593Smuzhiyun iqs62x = devm_kzalloc(&client->dev, sizeof(*iqs62x), GFP_KERNEL);
889*4882a593Smuzhiyun if (!iqs62x)
890*4882a593Smuzhiyun return -ENOMEM;
891*4882a593Smuzhiyun
892*4882a593Smuzhiyun i2c_set_clientdata(client, iqs62x);
893*4882a593Smuzhiyun iqs62x->client = client;
894*4882a593Smuzhiyun
895*4882a593Smuzhiyun BLOCKING_INIT_NOTIFIER_HEAD(&iqs62x->nh);
896*4882a593Smuzhiyun INIT_LIST_HEAD(&iqs62x->fw_blk_head);
897*4882a593Smuzhiyun init_completion(&iqs62x->fw_done);
898*4882a593Smuzhiyun
899*4882a593Smuzhiyun iqs62x->regmap = devm_regmap_init_i2c(client, &iqs62x_map_config);
900*4882a593Smuzhiyun if (IS_ERR(iqs62x->regmap)) {
901*4882a593Smuzhiyun ret = PTR_ERR(iqs62x->regmap);
902*4882a593Smuzhiyun dev_err(&client->dev, "Failed to initialize register map: %d\n",
903*4882a593Smuzhiyun ret);
904*4882a593Smuzhiyun return ret;
905*4882a593Smuzhiyun }
906*4882a593Smuzhiyun
907*4882a593Smuzhiyun ret = regmap_raw_read(iqs62x->regmap, IQS62X_PROD_NUM, &info,
908*4882a593Smuzhiyun sizeof(info));
909*4882a593Smuzhiyun if (ret)
910*4882a593Smuzhiyun return ret;
911*4882a593Smuzhiyun
912*4882a593Smuzhiyun /*
913*4882a593Smuzhiyun * The following sequence validates the device's product and software
914*4882a593Smuzhiyun * numbers. It then determines if the device is factory-calibrated by
915*4882a593Smuzhiyun * checking for nonzero values in the device's designated calibration
916*4882a593Smuzhiyun * registers (if applicable). Depending on the device, the absence of
917*4882a593Smuzhiyun * calibration data indicates a reduced feature set or invalid device.
918*4882a593Smuzhiyun *
919*4882a593Smuzhiyun * For devices given in both calibrated and uncalibrated versions, the
920*4882a593Smuzhiyun * calibrated version (e.g. IQS620AT) appears first in the iqs62x_devs
921*4882a593Smuzhiyun * array. The uncalibrated version (e.g. IQS620A) appears next and has
922*4882a593Smuzhiyun * the same product and software numbers, but no calibration registers
923*4882a593Smuzhiyun * are specified.
924*4882a593Smuzhiyun */
925*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(iqs62x_devs); i++) {
926*4882a593Smuzhiyun if (info.prod_num != iqs62x_devs[i].prod_num)
927*4882a593Smuzhiyun continue;
928*4882a593Smuzhiyun
929*4882a593Smuzhiyun iqs62x->dev_desc = &iqs62x_devs[i];
930*4882a593Smuzhiyun
931*4882a593Smuzhiyun if (info.sw_num < iqs62x->dev_desc->sw_num)
932*4882a593Smuzhiyun continue;
933*4882a593Smuzhiyun
934*4882a593Smuzhiyun sw_num = info.sw_num;
935*4882a593Smuzhiyun
936*4882a593Smuzhiyun /*
937*4882a593Smuzhiyun * Read each of the device's designated calibration registers,
938*4882a593Smuzhiyun * if any, and exit from the inner loop early if any are equal
939*4882a593Smuzhiyun * to zero (indicating the device is uncalibrated). This could
940*4882a593Smuzhiyun * be acceptable depending on the device (e.g. IQS620A instead
941*4882a593Smuzhiyun * of IQS620AT).
942*4882a593Smuzhiyun */
943*4882a593Smuzhiyun for (j = 0; j < iqs62x->dev_desc->num_cal_regs; j++) {
944*4882a593Smuzhiyun ret = regmap_read(iqs62x->regmap,
945*4882a593Smuzhiyun iqs62x->dev_desc->cal_regs[j], &val);
946*4882a593Smuzhiyun if (ret)
947*4882a593Smuzhiyun return ret;
948*4882a593Smuzhiyun
949*4882a593Smuzhiyun if (!val)
950*4882a593Smuzhiyun break;
951*4882a593Smuzhiyun }
952*4882a593Smuzhiyun
953*4882a593Smuzhiyun /*
954*4882a593Smuzhiyun * If the number of nonzero values read from the device equals
955*4882a593Smuzhiyun * the number of designated calibration registers (which could
956*4882a593Smuzhiyun * be zero), exit from the outer loop early to signal that the
957*4882a593Smuzhiyun * device's product and software numbers match a known device,
958*4882a593Smuzhiyun * and the device is calibrated (if applicable).
959*4882a593Smuzhiyun */
960*4882a593Smuzhiyun if (j == iqs62x->dev_desc->num_cal_regs)
961*4882a593Smuzhiyun break;
962*4882a593Smuzhiyun }
963*4882a593Smuzhiyun
964*4882a593Smuzhiyun if (!iqs62x->dev_desc) {
965*4882a593Smuzhiyun dev_err(&client->dev, "Unrecognized product number: 0x%02X\n",
966*4882a593Smuzhiyun info.prod_num);
967*4882a593Smuzhiyun return -EINVAL;
968*4882a593Smuzhiyun }
969*4882a593Smuzhiyun
970*4882a593Smuzhiyun if (!sw_num) {
971*4882a593Smuzhiyun dev_err(&client->dev, "Unrecognized software number: 0x%02X\n",
972*4882a593Smuzhiyun info.sw_num);
973*4882a593Smuzhiyun return -EINVAL;
974*4882a593Smuzhiyun }
975*4882a593Smuzhiyun
976*4882a593Smuzhiyun if (i == ARRAY_SIZE(iqs62x_devs)) {
977*4882a593Smuzhiyun dev_err(&client->dev, "Uncalibrated device\n");
978*4882a593Smuzhiyun return -ENODATA;
979*4882a593Smuzhiyun }
980*4882a593Smuzhiyun
981*4882a593Smuzhiyun device_property_read_string(&client->dev, "firmware-name", &fw_name);
982*4882a593Smuzhiyun
983*4882a593Smuzhiyun ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
984*4882a593Smuzhiyun fw_name ? : iqs62x->dev_desc->fw_name,
985*4882a593Smuzhiyun &client->dev, GFP_KERNEL, iqs62x,
986*4882a593Smuzhiyun iqs62x_firmware_load);
987*4882a593Smuzhiyun if (ret)
988*4882a593Smuzhiyun dev_err(&client->dev, "Failed to request firmware: %d\n", ret);
989*4882a593Smuzhiyun
990*4882a593Smuzhiyun return ret;
991*4882a593Smuzhiyun }
992*4882a593Smuzhiyun
iqs62x_remove(struct i2c_client * client)993*4882a593Smuzhiyun static int iqs62x_remove(struct i2c_client *client)
994*4882a593Smuzhiyun {
995*4882a593Smuzhiyun struct iqs62x_core *iqs62x = i2c_get_clientdata(client);
996*4882a593Smuzhiyun
997*4882a593Smuzhiyun wait_for_completion(&iqs62x->fw_done);
998*4882a593Smuzhiyun
999*4882a593Smuzhiyun return 0;
1000*4882a593Smuzhiyun }
1001*4882a593Smuzhiyun
iqs62x_suspend(struct device * dev)1002*4882a593Smuzhiyun static int __maybe_unused iqs62x_suspend(struct device *dev)
1003*4882a593Smuzhiyun {
1004*4882a593Smuzhiyun struct iqs62x_core *iqs62x = dev_get_drvdata(dev);
1005*4882a593Smuzhiyun int ret;
1006*4882a593Smuzhiyun
1007*4882a593Smuzhiyun wait_for_completion(&iqs62x->fw_done);
1008*4882a593Smuzhiyun
1009*4882a593Smuzhiyun /*
1010*4882a593Smuzhiyun * As per the datasheet, automatic mode switching must be disabled
1011*4882a593Smuzhiyun * before the device is placed in or taken out of halt mode.
1012*4882a593Smuzhiyun */
1013*4882a593Smuzhiyun ret = regmap_update_bits(iqs62x->regmap, IQS62X_PWR_SETTINGS,
1014*4882a593Smuzhiyun IQS62X_PWR_SETTINGS_DIS_AUTO, 0xFF);
1015*4882a593Smuzhiyun if (ret)
1016*4882a593Smuzhiyun return ret;
1017*4882a593Smuzhiyun
1018*4882a593Smuzhiyun return regmap_update_bits(iqs62x->regmap, IQS62X_PWR_SETTINGS,
1019*4882a593Smuzhiyun IQS62X_PWR_SETTINGS_PWR_MODE_MASK,
1020*4882a593Smuzhiyun IQS62X_PWR_SETTINGS_PWR_MODE_HALT);
1021*4882a593Smuzhiyun }
1022*4882a593Smuzhiyun
iqs62x_resume(struct device * dev)1023*4882a593Smuzhiyun static int __maybe_unused iqs62x_resume(struct device *dev)
1024*4882a593Smuzhiyun {
1025*4882a593Smuzhiyun struct iqs62x_core *iqs62x = dev_get_drvdata(dev);
1026*4882a593Smuzhiyun int ret;
1027*4882a593Smuzhiyun
1028*4882a593Smuzhiyun ret = regmap_update_bits(iqs62x->regmap, IQS62X_PWR_SETTINGS,
1029*4882a593Smuzhiyun IQS62X_PWR_SETTINGS_PWR_MODE_MASK,
1030*4882a593Smuzhiyun IQS62X_PWR_SETTINGS_PWR_MODE_NORM);
1031*4882a593Smuzhiyun if (ret)
1032*4882a593Smuzhiyun return ret;
1033*4882a593Smuzhiyun
1034*4882a593Smuzhiyun return regmap_update_bits(iqs62x->regmap, IQS62X_PWR_SETTINGS,
1035*4882a593Smuzhiyun IQS62X_PWR_SETTINGS_DIS_AUTO, 0);
1036*4882a593Smuzhiyun }
1037*4882a593Smuzhiyun
1038*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(iqs62x_pm, iqs62x_suspend, iqs62x_resume);
1039*4882a593Smuzhiyun
1040*4882a593Smuzhiyun static const struct of_device_id iqs62x_of_match[] = {
1041*4882a593Smuzhiyun { .compatible = "azoteq,iqs620a" },
1042*4882a593Smuzhiyun { .compatible = "azoteq,iqs621" },
1043*4882a593Smuzhiyun { .compatible = "azoteq,iqs622" },
1044*4882a593Smuzhiyun { .compatible = "azoteq,iqs624" },
1045*4882a593Smuzhiyun { .compatible = "azoteq,iqs625" },
1046*4882a593Smuzhiyun { }
1047*4882a593Smuzhiyun };
1048*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, iqs62x_of_match);
1049*4882a593Smuzhiyun
1050*4882a593Smuzhiyun static struct i2c_driver iqs62x_i2c_driver = {
1051*4882a593Smuzhiyun .driver = {
1052*4882a593Smuzhiyun .name = "iqs62x",
1053*4882a593Smuzhiyun .of_match_table = iqs62x_of_match,
1054*4882a593Smuzhiyun .pm = &iqs62x_pm,
1055*4882a593Smuzhiyun },
1056*4882a593Smuzhiyun .probe_new = iqs62x_probe,
1057*4882a593Smuzhiyun .remove = iqs62x_remove,
1058*4882a593Smuzhiyun };
1059*4882a593Smuzhiyun module_i2c_driver(iqs62x_i2c_driver);
1060*4882a593Smuzhiyun
1061*4882a593Smuzhiyun MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
1062*4882a593Smuzhiyun MODULE_DESCRIPTION("Azoteq IQS620A/621/622/624/625 Multi-Function Sensors");
1063*4882a593Smuzhiyun MODULE_LICENSE("GPL");
1064