Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mfd: Add support for Azoteq IQS620A/621/622/624/625

This patch adds core support for the Azoteq IQS620A, IQS621, IQS622,
IQS624 and IQS625 multi-function sensors.

Signed-off-by: Jeff LaBundy <jeff@labundy.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>

authored by

Jeff LaBundy and committed by
Lee Jones
4d9cf7df d8a3c488

+1216
+13
drivers/mfd/Kconfig
··· 642 642 AT90LS8535 microcontroller flashed with a special iPAQ 643 643 firmware using the custom protocol implemented in this driver. 644 644 645 + config MFD_IQS62X 646 + tristate "Azoteq IQS620A/621/622/624/625 core support" 647 + depends on I2C 648 + select MFD_CORE 649 + select REGMAP_I2C 650 + help 651 + Say Y here if you want to build core support for the Azoteq IQS620A, 652 + IQS621, IQS622, IQS624 and IQS625 multi-function sensors. Additional 653 + options must be selected to enable device-specific functions. 654 + 655 + To compile this driver as a module, choose M here: the module will 656 + be called iqs62x. 657 + 645 658 config MFD_JANZ_CMODIO 646 659 tristate "Janz CMOD-IO PCI MODULbus Carrier Board" 647 660 select MFD_CORE
+1
drivers/mfd/Makefile
··· 226 226 obj-$(CONFIG_MFD_AS3722) += as3722.o 227 227 obj-$(CONFIG_MFD_STW481X) += stw481x.o 228 228 obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o 229 + obj-$(CONFIG_MFD_IQS62X) += iqs62x.o 229 230 obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o 230 231 obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o 231 232 obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o
+1063
drivers/mfd/iqs62x.c
··· 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 + 139 + /* fall through */ 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 + 473 + /* fall through */ 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 + 494 + /* fall through */ 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");
+139
include/linux/mfd/iqs62x.h
··· 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 + 8 + #ifndef __LINUX_MFD_IQS62X_H 9 + #define __LINUX_MFD_IQS62X_H 10 + 11 + #define IQS620_PROD_NUM 0x41 12 + #define IQS621_PROD_NUM 0x46 13 + #define IQS622_PROD_NUM 0x42 14 + #define IQS624_PROD_NUM 0x43 15 + #define IQS625_PROD_NUM 0x4E 16 + 17 + #define IQS621_ALS_FLAGS 0x16 18 + #define IQS622_ALS_FLAGS 0x14 19 + 20 + #define IQS624_HALL_UI 0x70 21 + #define IQS624_HALL_UI_WHL_EVENT BIT(4) 22 + #define IQS624_HALL_UI_INT_EVENT BIT(3) 23 + #define IQS624_HALL_UI_AUTO_CAL BIT(2) 24 + 25 + #define IQS624_INTERVAL_DIV 0x7D 26 + 27 + #define IQS620_GLBL_EVENT_MASK 0xD7 28 + #define IQS620_GLBL_EVENT_MASK_PMU BIT(6) 29 + 30 + #define IQS62X_NUM_KEYS 16 31 + #define IQS62X_NUM_EVENTS (IQS62X_NUM_KEYS + 5) 32 + 33 + #define IQS62X_EVENT_SIZE 10 34 + 35 + enum iqs62x_ui_sel { 36 + IQS62X_UI_PROX, 37 + IQS62X_UI_SAR1, 38 + }; 39 + 40 + enum iqs62x_event_reg { 41 + IQS62X_EVENT_NONE, 42 + IQS62X_EVENT_SYS, 43 + IQS62X_EVENT_PROX, 44 + IQS62X_EVENT_HYST, 45 + IQS62X_EVENT_HALL, 46 + IQS62X_EVENT_ALS, 47 + IQS62X_EVENT_IR, 48 + IQS62X_EVENT_WHEEL, 49 + IQS62X_EVENT_INTER, 50 + IQS62X_EVENT_UI_LO, 51 + IQS62X_EVENT_UI_HI, 52 + }; 53 + 54 + enum iqs62x_event_flag { 55 + /* keys */ 56 + IQS62X_EVENT_PROX_CH0_T, 57 + IQS62X_EVENT_PROX_CH0_P, 58 + IQS62X_EVENT_PROX_CH1_T, 59 + IQS62X_EVENT_PROX_CH1_P, 60 + IQS62X_EVENT_PROX_CH2_T, 61 + IQS62X_EVENT_PROX_CH2_P, 62 + IQS62X_EVENT_HYST_POS_T, 63 + IQS62X_EVENT_HYST_POS_P, 64 + IQS62X_EVENT_HYST_NEG_T, 65 + IQS62X_EVENT_HYST_NEG_P, 66 + IQS62X_EVENT_SAR1_ACT, 67 + IQS62X_EVENT_SAR1_QRD, 68 + IQS62X_EVENT_SAR1_MOVE, 69 + IQS62X_EVENT_SAR1_HALT, 70 + IQS62X_EVENT_WHEEL_UP, 71 + IQS62X_EVENT_WHEEL_DN, 72 + 73 + /* switches */ 74 + IQS62X_EVENT_HALL_N_T, 75 + IQS62X_EVENT_HALL_N_P, 76 + IQS62X_EVENT_HALL_S_T, 77 + IQS62X_EVENT_HALL_S_P, 78 + 79 + /* everything else */ 80 + IQS62X_EVENT_SYS_RESET, 81 + }; 82 + 83 + struct iqs62x_event_data { 84 + u16 ui_data; 85 + u8 als_flags; 86 + u8 ir_flags; 87 + u8 interval; 88 + }; 89 + 90 + struct iqs62x_event_desc { 91 + enum iqs62x_event_reg reg; 92 + u8 mask; 93 + u8 val; 94 + }; 95 + 96 + struct iqs62x_dev_desc { 97 + const char *dev_name; 98 + const struct mfd_cell *sub_devs; 99 + int num_sub_devs; 100 + 101 + u8 prod_num; 102 + u8 sw_num; 103 + const u8 *cal_regs; 104 + int num_cal_regs; 105 + 106 + u8 prox_mask; 107 + u8 sar_mask; 108 + u8 hall_mask; 109 + u8 hyst_mask; 110 + u8 temp_mask; 111 + u8 als_mask; 112 + u8 ir_mask; 113 + 114 + u8 prox_settings; 115 + u8 als_flags; 116 + u8 hall_flags; 117 + u8 hyst_shift; 118 + 119 + u8 interval; 120 + u8 interval_div; 121 + 122 + u8 clk_div; 123 + const char *fw_name; 124 + const enum iqs62x_event_reg (*event_regs)[IQS62X_EVENT_SIZE]; 125 + }; 126 + 127 + struct iqs62x_core { 128 + const struct iqs62x_dev_desc *dev_desc; 129 + struct i2c_client *client; 130 + struct regmap *regmap; 131 + struct blocking_notifier_head nh; 132 + struct list_head fw_blk_head; 133 + struct completion fw_done; 134 + enum iqs62x_ui_sel ui_sel; 135 + }; 136 + 137 + extern const struct iqs62x_event_desc iqs62x_events[IQS62X_NUM_EVENTS]; 138 + 139 + #endif /* __LINUX_MFD_IQS62X_H */