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