162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Azoteq IQS620A/621/622/624/625 Multi-Function Sensors 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * These devices rely on application-specific register settings and calibration 862306a36Sopenharmony_ci * data developed in and exported from a suite of GUIs offered by the vendor. A 962306a36Sopenharmony_ci * separate tool converts the GUIs' ASCII-based output into a standard firmware 1062306a36Sopenharmony_ci * file parsed by the driver. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Link to datasheets and GUIs: https://www.azoteq.com/ 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Link to conversion tool: https://github.com/jlabundy/iqs62x-h2bin.git 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/completion.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/device.h> 2062306a36Sopenharmony_ci#include <linux/err.h> 2162306a36Sopenharmony_ci#include <linux/firmware.h> 2262306a36Sopenharmony_ci#include <linux/i2c.h> 2362306a36Sopenharmony_ci#include <linux/interrupt.h> 2462306a36Sopenharmony_ci#include <linux/kernel.h> 2562306a36Sopenharmony_ci#include <linux/list.h> 2662306a36Sopenharmony_ci#include <linux/mfd/core.h> 2762306a36Sopenharmony_ci#include <linux/mfd/iqs62x.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/notifier.h> 3062306a36Sopenharmony_ci#include <linux/of.h> 3162306a36Sopenharmony_ci#include <linux/property.h> 3262306a36Sopenharmony_ci#include <linux/regmap.h> 3362306a36Sopenharmony_ci#include <linux/slab.h> 3462306a36Sopenharmony_ci#include <asm/unaligned.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define IQS62X_PROD_NUM 0x00 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define IQS62X_SYS_FLAGS 0x10 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define IQS620_HALL_FLAGS 0x16 4162306a36Sopenharmony_ci#define IQS621_HALL_FLAGS 0x19 4262306a36Sopenharmony_ci#define IQS622_HALL_FLAGS IQS621_HALL_FLAGS 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define IQS624_INTERVAL_NUM 0x18 4562306a36Sopenharmony_ci#define IQS625_INTERVAL_NUM 0x12 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define IQS622_PROX_SETTINGS_4 0x48 4862306a36Sopenharmony_ci#define IQS620_PROX_SETTINGS_4 0x50 4962306a36Sopenharmony_ci#define IQS620_PROX_SETTINGS_4_SAR_EN BIT(7) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define IQS621_ALS_CAL_DIV_LUX 0x82 5262306a36Sopenharmony_ci#define IQS621_ALS_CAL_DIV_IR 0x83 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define IQS620_TEMP_CAL_MULT 0xC2 5562306a36Sopenharmony_ci#define IQS620_TEMP_CAL_DIV 0xC3 5662306a36Sopenharmony_ci#define IQS620_TEMP_CAL_OFFS 0xC4 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define IQS62X_SYS_SETTINGS 0xD0 5962306a36Sopenharmony_ci#define IQS62X_SYS_SETTINGS_ACK_RESET BIT(6) 6062306a36Sopenharmony_ci#define IQS62X_SYS_SETTINGS_EVENT_MODE BIT(5) 6162306a36Sopenharmony_ci#define IQS62X_SYS_SETTINGS_CLK_DIV BIT(4) 6262306a36Sopenharmony_ci#define IQS62X_SYS_SETTINGS_COMM_ATI BIT(3) 6362306a36Sopenharmony_ci#define IQS62X_SYS_SETTINGS_REDO_ATI BIT(1) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define IQS62X_PWR_SETTINGS 0xD2 6662306a36Sopenharmony_ci#define IQS62X_PWR_SETTINGS_DIS_AUTO BIT(5) 6762306a36Sopenharmony_ci#define IQS62X_PWR_SETTINGS_PWR_MODE_MASK (BIT(4) | BIT(3)) 6862306a36Sopenharmony_ci#define IQS62X_PWR_SETTINGS_PWR_MODE_HALT (BIT(4) | BIT(3)) 6962306a36Sopenharmony_ci#define IQS62X_PWR_SETTINGS_PWR_MODE_NORM 0 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define IQS62X_OTP_CMD 0xF0 7262306a36Sopenharmony_ci#define IQS62X_OTP_CMD_FG3 0x13 7362306a36Sopenharmony_ci#define IQS62X_OTP_DATA 0xF1 7462306a36Sopenharmony_ci#define IQS62X_MAX_REG 0xFF 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define IQS62X_HALL_CAL_MASK GENMASK(3, 0) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define IQS62X_FW_REC_TYPE_INFO 0 7962306a36Sopenharmony_ci#define IQS62X_FW_REC_TYPE_PROD 1 8062306a36Sopenharmony_ci#define IQS62X_FW_REC_TYPE_HALL 2 8162306a36Sopenharmony_ci#define IQS62X_FW_REC_TYPE_MASK 3 8262306a36Sopenharmony_ci#define IQS62X_FW_REC_TYPE_DATA 4 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define IQS62X_ATI_STARTUP_MS 350 8562306a36Sopenharmony_ci#define IQS62X_FILT_SETTLE_MS 250 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistruct iqs62x_fw_rec { 8862306a36Sopenharmony_ci u8 type; 8962306a36Sopenharmony_ci u8 addr; 9062306a36Sopenharmony_ci u8 len; 9162306a36Sopenharmony_ci u8 data; 9262306a36Sopenharmony_ci} __packed; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistruct iqs62x_fw_blk { 9562306a36Sopenharmony_ci struct list_head list; 9662306a36Sopenharmony_ci u8 addr; 9762306a36Sopenharmony_ci u8 mask; 9862306a36Sopenharmony_ci u8 len; 9962306a36Sopenharmony_ci u8 data[]; 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct iqs62x_info { 10362306a36Sopenharmony_ci u8 prod_num; 10462306a36Sopenharmony_ci u8 sw_num; 10562306a36Sopenharmony_ci u8 hw_num; 10662306a36Sopenharmony_ci} __packed; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int iqs62x_dev_init(struct iqs62x_core *iqs62x) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct iqs62x_fw_blk *fw_blk; 11162306a36Sopenharmony_ci unsigned int val; 11262306a36Sopenharmony_ci int ret; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci list_for_each_entry(fw_blk, &iqs62x->fw_blk_head, list) { 11562306a36Sopenharmony_ci /* 11662306a36Sopenharmony_ci * In case ATI is in progress, wait for it to complete before 11762306a36Sopenharmony_ci * lowering the core clock frequency. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci if (fw_blk->addr == IQS62X_SYS_SETTINGS && 12062306a36Sopenharmony_ci *fw_blk->data & IQS62X_SYS_SETTINGS_CLK_DIV) 12162306a36Sopenharmony_ci msleep(IQS62X_ATI_STARTUP_MS); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (fw_blk->mask) 12462306a36Sopenharmony_ci ret = regmap_update_bits(iqs62x->regmap, fw_blk->addr, 12562306a36Sopenharmony_ci fw_blk->mask, *fw_blk->data); 12662306a36Sopenharmony_ci else 12762306a36Sopenharmony_ci ret = regmap_raw_write(iqs62x->regmap, fw_blk->addr, 12862306a36Sopenharmony_ci fw_blk->data, fw_blk->len); 12962306a36Sopenharmony_ci if (ret) 13062306a36Sopenharmony_ci return ret; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci switch (iqs62x->dev_desc->prod_num) { 13462306a36Sopenharmony_ci case IQS620_PROD_NUM: 13562306a36Sopenharmony_ci case IQS622_PROD_NUM: 13662306a36Sopenharmony_ci ret = regmap_read(iqs62x->regmap, 13762306a36Sopenharmony_ci iqs62x->dev_desc->prox_settings, &val); 13862306a36Sopenharmony_ci if (ret) 13962306a36Sopenharmony_ci return ret; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (val & IQS620_PROX_SETTINGS_4_SAR_EN) 14262306a36Sopenharmony_ci iqs62x->ui_sel = IQS62X_UI_SAR1; 14362306a36Sopenharmony_ci fallthrough; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci case IQS621_PROD_NUM: 14662306a36Sopenharmony_ci ret = regmap_write(iqs62x->regmap, IQS620_GLBL_EVENT_MASK, 14762306a36Sopenharmony_ci IQS620_GLBL_EVENT_MASK_PMU | 14862306a36Sopenharmony_ci iqs62x->dev_desc->prox_mask | 14962306a36Sopenharmony_ci iqs62x->dev_desc->sar_mask | 15062306a36Sopenharmony_ci iqs62x->dev_desc->hall_mask | 15162306a36Sopenharmony_ci iqs62x->dev_desc->hyst_mask | 15262306a36Sopenharmony_ci iqs62x->dev_desc->temp_mask | 15362306a36Sopenharmony_ci iqs62x->dev_desc->als_mask | 15462306a36Sopenharmony_ci iqs62x->dev_desc->ir_mask); 15562306a36Sopenharmony_ci if (ret) 15662306a36Sopenharmony_ci return ret; 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci default: 16062306a36Sopenharmony_ci ret = regmap_write(iqs62x->regmap, IQS624_HALL_UI, 16162306a36Sopenharmony_ci IQS624_HALL_UI_WHL_EVENT | 16262306a36Sopenharmony_ci IQS624_HALL_UI_INT_EVENT | 16362306a36Sopenharmony_ci IQS624_HALL_UI_AUTO_CAL); 16462306a36Sopenharmony_ci if (ret) 16562306a36Sopenharmony_ci return ret; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* 16862306a36Sopenharmony_ci * The IQS625 default interval divider is below the minimum 16962306a36Sopenharmony_ci * permissible value, and the datasheet mandates that it is 17062306a36Sopenharmony_ci * corrected during initialization (unless an updated value 17162306a36Sopenharmony_ci * has already been provided by firmware). 17262306a36Sopenharmony_ci * 17362306a36Sopenharmony_ci * To protect against an unacceptably low user-entered value 17462306a36Sopenharmony_ci * stored in the firmware, the same check is extended to the 17562306a36Sopenharmony_ci * IQS624 as well. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci ret = regmap_read(iqs62x->regmap, IQS624_INTERVAL_DIV, &val); 17862306a36Sopenharmony_ci if (ret) 17962306a36Sopenharmony_ci return ret; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (val >= iqs62x->dev_desc->interval_div) 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci ret = regmap_write(iqs62x->regmap, IQS624_INTERVAL_DIV, 18562306a36Sopenharmony_ci iqs62x->dev_desc->interval_div); 18662306a36Sopenharmony_ci if (ret) 18762306a36Sopenharmony_ci return ret; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* 19162306a36Sopenharmony_ci * Place the device in streaming mode at first so as not to miss the 19262306a36Sopenharmony_ci * limited number of interrupts that would otherwise occur after ATI 19362306a36Sopenharmony_ci * completes. The device is subsequently placed in event mode by the 19462306a36Sopenharmony_ci * interrupt handler. 19562306a36Sopenharmony_ci * 19662306a36Sopenharmony_ci * In the meantime, mask interrupts during ATI to prevent the device 19762306a36Sopenharmony_ci * from soliciting I2C traffic until the noise-sensitive ATI process 19862306a36Sopenharmony_ci * is complete. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci ret = regmap_update_bits(iqs62x->regmap, IQS62X_SYS_SETTINGS, 20162306a36Sopenharmony_ci IQS62X_SYS_SETTINGS_ACK_RESET | 20262306a36Sopenharmony_ci IQS62X_SYS_SETTINGS_EVENT_MODE | 20362306a36Sopenharmony_ci IQS62X_SYS_SETTINGS_COMM_ATI | 20462306a36Sopenharmony_ci IQS62X_SYS_SETTINGS_REDO_ATI, 20562306a36Sopenharmony_ci IQS62X_SYS_SETTINGS_ACK_RESET | 20662306a36Sopenharmony_ci IQS62X_SYS_SETTINGS_REDO_ATI); 20762306a36Sopenharmony_ci if (ret) 20862306a36Sopenharmony_ci return ret; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * The following delay gives the device time to deassert its RDY output 21262306a36Sopenharmony_ci * in case a communication window was open while the REDO_ATI field was 21362306a36Sopenharmony_ci * written. This prevents an interrupt from being serviced prematurely. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci usleep_range(5000, 5100); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int iqs62x_firmware_parse(struct iqs62x_core *iqs62x, 22162306a36Sopenharmony_ci const struct firmware *fw) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct i2c_client *client = iqs62x->client; 22462306a36Sopenharmony_ci struct iqs62x_fw_rec *fw_rec; 22562306a36Sopenharmony_ci struct iqs62x_fw_blk *fw_blk; 22662306a36Sopenharmony_ci unsigned int val; 22762306a36Sopenharmony_ci size_t pos = 0; 22862306a36Sopenharmony_ci int ret = 0; 22962306a36Sopenharmony_ci u8 mask, len, *data; 23062306a36Sopenharmony_ci u8 hall_cal_index = 0; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci while (pos < fw->size) { 23362306a36Sopenharmony_ci if (pos + sizeof(*fw_rec) > fw->size) { 23462306a36Sopenharmony_ci ret = -EINVAL; 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci fw_rec = (struct iqs62x_fw_rec *)(fw->data + pos); 23862306a36Sopenharmony_ci pos += sizeof(*fw_rec); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (pos + fw_rec->len - 1 > fw->size) { 24162306a36Sopenharmony_ci ret = -EINVAL; 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci pos += fw_rec->len - 1; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci switch (fw_rec->type) { 24762306a36Sopenharmony_ci case IQS62X_FW_REC_TYPE_INFO: 24862306a36Sopenharmony_ci continue; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci case IQS62X_FW_REC_TYPE_PROD: 25162306a36Sopenharmony_ci if (fw_rec->data == iqs62x->dev_desc->prod_num) 25262306a36Sopenharmony_ci continue; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci dev_err(&client->dev, 25562306a36Sopenharmony_ci "Incompatible product number: 0x%02X\n", 25662306a36Sopenharmony_ci fw_rec->data); 25762306a36Sopenharmony_ci ret = -EINVAL; 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci case IQS62X_FW_REC_TYPE_HALL: 26162306a36Sopenharmony_ci if (!hall_cal_index) { 26262306a36Sopenharmony_ci ret = regmap_write(iqs62x->regmap, 26362306a36Sopenharmony_ci IQS62X_OTP_CMD, 26462306a36Sopenharmony_ci IQS62X_OTP_CMD_FG3); 26562306a36Sopenharmony_ci if (ret) 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci ret = regmap_read(iqs62x->regmap, 26962306a36Sopenharmony_ci IQS62X_OTP_DATA, &val); 27062306a36Sopenharmony_ci if (ret) 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci hall_cal_index = val & IQS62X_HALL_CAL_MASK; 27462306a36Sopenharmony_ci if (!hall_cal_index) { 27562306a36Sopenharmony_ci dev_err(&client->dev, 27662306a36Sopenharmony_ci "Uncalibrated device\n"); 27762306a36Sopenharmony_ci ret = -ENODATA; 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (hall_cal_index > fw_rec->len) { 28362306a36Sopenharmony_ci ret = -EINVAL; 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci mask = 0; 28862306a36Sopenharmony_ci data = &fw_rec->data + hall_cal_index - 1; 28962306a36Sopenharmony_ci len = sizeof(*data); 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci case IQS62X_FW_REC_TYPE_MASK: 29362306a36Sopenharmony_ci if (fw_rec->len < (sizeof(mask) + sizeof(*data))) { 29462306a36Sopenharmony_ci ret = -EINVAL; 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci mask = fw_rec->data; 29962306a36Sopenharmony_ci data = &fw_rec->data + sizeof(mask); 30062306a36Sopenharmony_ci len = sizeof(*data); 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci case IQS62X_FW_REC_TYPE_DATA: 30462306a36Sopenharmony_ci mask = 0; 30562306a36Sopenharmony_ci data = &fw_rec->data; 30662306a36Sopenharmony_ci len = fw_rec->len; 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci default: 31062306a36Sopenharmony_ci dev_err(&client->dev, 31162306a36Sopenharmony_ci "Unrecognized record type: 0x%02X\n", 31262306a36Sopenharmony_ci fw_rec->type); 31362306a36Sopenharmony_ci ret = -EINVAL; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (ret) 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci fw_blk = devm_kzalloc(&client->dev, 32062306a36Sopenharmony_ci struct_size(fw_blk, data, len), 32162306a36Sopenharmony_ci GFP_KERNEL); 32262306a36Sopenharmony_ci if (!fw_blk) { 32362306a36Sopenharmony_ci ret = -ENOMEM; 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci fw_blk->addr = fw_rec->addr; 32862306a36Sopenharmony_ci fw_blk->mask = mask; 32962306a36Sopenharmony_ci fw_blk->len = len; 33062306a36Sopenharmony_ci memcpy(fw_blk->data, data, len); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci list_add(&fw_blk->list, &iqs62x->fw_blk_head); 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci release_firmware(fw); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return ret; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ciconst struct iqs62x_event_desc iqs62x_events[IQS62X_NUM_EVENTS] = { 34162306a36Sopenharmony_ci [IQS62X_EVENT_PROX_CH0_T] = { 34262306a36Sopenharmony_ci .reg = IQS62X_EVENT_PROX, 34362306a36Sopenharmony_ci .mask = BIT(4), 34462306a36Sopenharmony_ci .val = BIT(4), 34562306a36Sopenharmony_ci }, 34662306a36Sopenharmony_ci [IQS62X_EVENT_PROX_CH0_P] = { 34762306a36Sopenharmony_ci .reg = IQS62X_EVENT_PROX, 34862306a36Sopenharmony_ci .mask = BIT(0), 34962306a36Sopenharmony_ci .val = BIT(0), 35062306a36Sopenharmony_ci }, 35162306a36Sopenharmony_ci [IQS62X_EVENT_PROX_CH1_T] = { 35262306a36Sopenharmony_ci .reg = IQS62X_EVENT_PROX, 35362306a36Sopenharmony_ci .mask = BIT(5), 35462306a36Sopenharmony_ci .val = BIT(5), 35562306a36Sopenharmony_ci }, 35662306a36Sopenharmony_ci [IQS62X_EVENT_PROX_CH1_P] = { 35762306a36Sopenharmony_ci .reg = IQS62X_EVENT_PROX, 35862306a36Sopenharmony_ci .mask = BIT(1), 35962306a36Sopenharmony_ci .val = BIT(1), 36062306a36Sopenharmony_ci }, 36162306a36Sopenharmony_ci [IQS62X_EVENT_PROX_CH2_T] = { 36262306a36Sopenharmony_ci .reg = IQS62X_EVENT_PROX, 36362306a36Sopenharmony_ci .mask = BIT(6), 36462306a36Sopenharmony_ci .val = BIT(6), 36562306a36Sopenharmony_ci }, 36662306a36Sopenharmony_ci [IQS62X_EVENT_PROX_CH2_P] = { 36762306a36Sopenharmony_ci .reg = IQS62X_EVENT_PROX, 36862306a36Sopenharmony_ci .mask = BIT(2), 36962306a36Sopenharmony_ci .val = BIT(2), 37062306a36Sopenharmony_ci }, 37162306a36Sopenharmony_ci [IQS62X_EVENT_HYST_POS_T] = { 37262306a36Sopenharmony_ci .reg = IQS62X_EVENT_HYST, 37362306a36Sopenharmony_ci .mask = BIT(6) | BIT(7), 37462306a36Sopenharmony_ci .val = BIT(6), 37562306a36Sopenharmony_ci }, 37662306a36Sopenharmony_ci [IQS62X_EVENT_HYST_POS_P] = { 37762306a36Sopenharmony_ci .reg = IQS62X_EVENT_HYST, 37862306a36Sopenharmony_ci .mask = BIT(5) | BIT(7), 37962306a36Sopenharmony_ci .val = BIT(5), 38062306a36Sopenharmony_ci }, 38162306a36Sopenharmony_ci [IQS62X_EVENT_HYST_NEG_T] = { 38262306a36Sopenharmony_ci .reg = IQS62X_EVENT_HYST, 38362306a36Sopenharmony_ci .mask = BIT(6) | BIT(7), 38462306a36Sopenharmony_ci .val = BIT(6) | BIT(7), 38562306a36Sopenharmony_ci }, 38662306a36Sopenharmony_ci [IQS62X_EVENT_HYST_NEG_P] = { 38762306a36Sopenharmony_ci .reg = IQS62X_EVENT_HYST, 38862306a36Sopenharmony_ci .mask = BIT(5) | BIT(7), 38962306a36Sopenharmony_ci .val = BIT(5) | BIT(7), 39062306a36Sopenharmony_ci }, 39162306a36Sopenharmony_ci [IQS62X_EVENT_SAR1_ACT] = { 39262306a36Sopenharmony_ci .reg = IQS62X_EVENT_HYST, 39362306a36Sopenharmony_ci .mask = BIT(4), 39462306a36Sopenharmony_ci .val = BIT(4), 39562306a36Sopenharmony_ci }, 39662306a36Sopenharmony_ci [IQS62X_EVENT_SAR1_QRD] = { 39762306a36Sopenharmony_ci .reg = IQS62X_EVENT_HYST, 39862306a36Sopenharmony_ci .mask = BIT(2), 39962306a36Sopenharmony_ci .val = BIT(2), 40062306a36Sopenharmony_ci }, 40162306a36Sopenharmony_ci [IQS62X_EVENT_SAR1_MOVE] = { 40262306a36Sopenharmony_ci .reg = IQS62X_EVENT_HYST, 40362306a36Sopenharmony_ci .mask = BIT(1), 40462306a36Sopenharmony_ci .val = BIT(1), 40562306a36Sopenharmony_ci }, 40662306a36Sopenharmony_ci [IQS62X_EVENT_SAR1_HALT] = { 40762306a36Sopenharmony_ci .reg = IQS62X_EVENT_HYST, 40862306a36Sopenharmony_ci .mask = BIT(0), 40962306a36Sopenharmony_ci .val = BIT(0), 41062306a36Sopenharmony_ci }, 41162306a36Sopenharmony_ci [IQS62X_EVENT_WHEEL_UP] = { 41262306a36Sopenharmony_ci .reg = IQS62X_EVENT_WHEEL, 41362306a36Sopenharmony_ci .mask = BIT(7) | BIT(6), 41462306a36Sopenharmony_ci .val = BIT(7), 41562306a36Sopenharmony_ci }, 41662306a36Sopenharmony_ci [IQS62X_EVENT_WHEEL_DN] = { 41762306a36Sopenharmony_ci .reg = IQS62X_EVENT_WHEEL, 41862306a36Sopenharmony_ci .mask = BIT(7) | BIT(6), 41962306a36Sopenharmony_ci .val = BIT(7) | BIT(6), 42062306a36Sopenharmony_ci }, 42162306a36Sopenharmony_ci [IQS62X_EVENT_HALL_N_T] = { 42262306a36Sopenharmony_ci .reg = IQS62X_EVENT_HALL, 42362306a36Sopenharmony_ci .mask = BIT(2) | BIT(0), 42462306a36Sopenharmony_ci .val = BIT(2), 42562306a36Sopenharmony_ci }, 42662306a36Sopenharmony_ci [IQS62X_EVENT_HALL_N_P] = { 42762306a36Sopenharmony_ci .reg = IQS62X_EVENT_HALL, 42862306a36Sopenharmony_ci .mask = BIT(1) | BIT(0), 42962306a36Sopenharmony_ci .val = BIT(1), 43062306a36Sopenharmony_ci }, 43162306a36Sopenharmony_ci [IQS62X_EVENT_HALL_S_T] = { 43262306a36Sopenharmony_ci .reg = IQS62X_EVENT_HALL, 43362306a36Sopenharmony_ci .mask = BIT(2) | BIT(0), 43462306a36Sopenharmony_ci .val = BIT(2) | BIT(0), 43562306a36Sopenharmony_ci }, 43662306a36Sopenharmony_ci [IQS62X_EVENT_HALL_S_P] = { 43762306a36Sopenharmony_ci .reg = IQS62X_EVENT_HALL, 43862306a36Sopenharmony_ci .mask = BIT(1) | BIT(0), 43962306a36Sopenharmony_ci .val = BIT(1) | BIT(0), 44062306a36Sopenharmony_ci }, 44162306a36Sopenharmony_ci [IQS62X_EVENT_SYS_RESET] = { 44262306a36Sopenharmony_ci .reg = IQS62X_EVENT_SYS, 44362306a36Sopenharmony_ci .mask = BIT(7), 44462306a36Sopenharmony_ci .val = BIT(7), 44562306a36Sopenharmony_ci }, 44662306a36Sopenharmony_ci [IQS62X_EVENT_SYS_ATI] = { 44762306a36Sopenharmony_ci .reg = IQS62X_EVENT_SYS, 44862306a36Sopenharmony_ci .mask = BIT(2), 44962306a36Sopenharmony_ci .val = BIT(2), 45062306a36Sopenharmony_ci }, 45162306a36Sopenharmony_ci}; 45262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iqs62x_events); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic irqreturn_t iqs62x_irq(int irq, void *context) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct iqs62x_core *iqs62x = context; 45762306a36Sopenharmony_ci struct i2c_client *client = iqs62x->client; 45862306a36Sopenharmony_ci struct iqs62x_event_data event_data; 45962306a36Sopenharmony_ci struct iqs62x_event_desc event_desc; 46062306a36Sopenharmony_ci enum iqs62x_event_reg event_reg; 46162306a36Sopenharmony_ci unsigned long event_flags = 0; 46262306a36Sopenharmony_ci int ret, i, j; 46362306a36Sopenharmony_ci u8 event_map[IQS62X_EVENT_SIZE]; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* 46662306a36Sopenharmony_ci * The device asserts the RDY output to signal the beginning of a 46762306a36Sopenharmony_ci * communication window, which is closed by an I2C stop condition. 46862306a36Sopenharmony_ci * As such, all interrupt status is captured in a single read and 46962306a36Sopenharmony_ci * broadcast to any interested sub-device drivers. 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_ci ret = regmap_raw_read(iqs62x->regmap, IQS62X_SYS_FLAGS, event_map, 47262306a36Sopenharmony_ci sizeof(event_map)); 47362306a36Sopenharmony_ci if (ret) { 47462306a36Sopenharmony_ci dev_err(&client->dev, "Failed to read device status: %d\n", 47562306a36Sopenharmony_ci ret); 47662306a36Sopenharmony_ci return IRQ_NONE; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci for (i = 0; i < sizeof(event_map); i++) { 48062306a36Sopenharmony_ci event_reg = iqs62x->dev_desc->event_regs[iqs62x->ui_sel][i]; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci switch (event_reg) { 48362306a36Sopenharmony_ci case IQS62X_EVENT_UI_LO: 48462306a36Sopenharmony_ci event_data.ui_data = get_unaligned_le16(&event_map[i]); 48562306a36Sopenharmony_ci fallthrough; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci case IQS62X_EVENT_UI_HI: 48862306a36Sopenharmony_ci case IQS62X_EVENT_NONE: 48962306a36Sopenharmony_ci continue; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci case IQS62X_EVENT_ALS: 49262306a36Sopenharmony_ci event_data.als_flags = event_map[i]; 49362306a36Sopenharmony_ci continue; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci case IQS62X_EVENT_IR: 49662306a36Sopenharmony_ci event_data.ir_flags = event_map[i]; 49762306a36Sopenharmony_ci continue; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci case IQS62X_EVENT_INTER: 50062306a36Sopenharmony_ci event_data.interval = event_map[i]; 50162306a36Sopenharmony_ci continue; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci case IQS62X_EVENT_HYST: 50462306a36Sopenharmony_ci event_map[i] <<= iqs62x->dev_desc->hyst_shift; 50562306a36Sopenharmony_ci fallthrough; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci case IQS62X_EVENT_WHEEL: 50862306a36Sopenharmony_ci case IQS62X_EVENT_HALL: 50962306a36Sopenharmony_ci case IQS62X_EVENT_PROX: 51062306a36Sopenharmony_ci case IQS62X_EVENT_SYS: 51162306a36Sopenharmony_ci break; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci for (j = 0; j < IQS62X_NUM_EVENTS; j++) { 51562306a36Sopenharmony_ci event_desc = iqs62x_events[j]; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (event_desc.reg != event_reg) 51862306a36Sopenharmony_ci continue; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if ((event_map[i] & event_desc.mask) == event_desc.val) 52162306a36Sopenharmony_ci event_flags |= BIT(j); 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* 52662306a36Sopenharmony_ci * The device resets itself in response to the I2C master stalling 52762306a36Sopenharmony_ci * communication past a fixed timeout. In this case, all registers 52862306a36Sopenharmony_ci * are restored and any interested sub-device drivers are notified. 52962306a36Sopenharmony_ci */ 53062306a36Sopenharmony_ci if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) { 53162306a36Sopenharmony_ci dev_err(&client->dev, "Unexpected device reset\n"); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci ret = iqs62x_dev_init(iqs62x); 53462306a36Sopenharmony_ci if (ret) { 53562306a36Sopenharmony_ci dev_err(&client->dev, 53662306a36Sopenharmony_ci "Failed to re-initialize device: %d\n", ret); 53762306a36Sopenharmony_ci return IRQ_NONE; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci iqs62x->event_cache |= BIT(IQS62X_EVENT_SYS_RESET); 54162306a36Sopenharmony_ci reinit_completion(&iqs62x->ati_done); 54262306a36Sopenharmony_ci } else if (event_flags & BIT(IQS62X_EVENT_SYS_ATI)) { 54362306a36Sopenharmony_ci iqs62x->event_cache |= BIT(IQS62X_EVENT_SYS_ATI); 54462306a36Sopenharmony_ci reinit_completion(&iqs62x->ati_done); 54562306a36Sopenharmony_ci } else if (!completion_done(&iqs62x->ati_done)) { 54662306a36Sopenharmony_ci ret = regmap_update_bits(iqs62x->regmap, IQS62X_SYS_SETTINGS, 54762306a36Sopenharmony_ci IQS62X_SYS_SETTINGS_EVENT_MODE, 0xFF); 54862306a36Sopenharmony_ci if (ret) { 54962306a36Sopenharmony_ci dev_err(&client->dev, 55062306a36Sopenharmony_ci "Failed to enable event mode: %d\n", ret); 55162306a36Sopenharmony_ci return IRQ_NONE; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci msleep(IQS62X_FILT_SETTLE_MS); 55562306a36Sopenharmony_ci complete_all(&iqs62x->ati_done); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* 55962306a36Sopenharmony_ci * Reset and ATI events are not broadcast to the sub-device drivers 56062306a36Sopenharmony_ci * until ATI has completed. Any other events that may have occurred 56162306a36Sopenharmony_ci * during ATI are ignored. 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_ci if (completion_done(&iqs62x->ati_done)) { 56462306a36Sopenharmony_ci event_flags |= iqs62x->event_cache; 56562306a36Sopenharmony_ci ret = blocking_notifier_call_chain(&iqs62x->nh, event_flags, 56662306a36Sopenharmony_ci &event_data); 56762306a36Sopenharmony_ci if (ret & NOTIFY_STOP_MASK) 56862306a36Sopenharmony_ci return IRQ_NONE; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci iqs62x->event_cache = 0; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* 57462306a36Sopenharmony_ci * Once the communication window is closed, a small delay is added to 57562306a36Sopenharmony_ci * ensure the device's RDY output has been deasserted by the time the 57662306a36Sopenharmony_ci * interrupt handler returns. 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_ci usleep_range(150, 200); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return IRQ_HANDLED; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic void iqs62x_firmware_load(const struct firmware *fw, void *context) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct iqs62x_core *iqs62x = context; 58662306a36Sopenharmony_ci struct i2c_client *client = iqs62x->client; 58762306a36Sopenharmony_ci int ret; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (fw) { 59062306a36Sopenharmony_ci ret = iqs62x_firmware_parse(iqs62x, fw); 59162306a36Sopenharmony_ci if (ret) { 59262306a36Sopenharmony_ci dev_err(&client->dev, "Failed to parse firmware: %d\n", 59362306a36Sopenharmony_ci ret); 59462306a36Sopenharmony_ci goto err_out; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci ret = iqs62x_dev_init(iqs62x); 59962306a36Sopenharmony_ci if (ret) { 60062306a36Sopenharmony_ci dev_err(&client->dev, "Failed to initialize device: %d\n", ret); 60162306a36Sopenharmony_ci goto err_out; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci ret = devm_request_threaded_irq(&client->dev, client->irq, 60562306a36Sopenharmony_ci NULL, iqs62x_irq, IRQF_ONESHOT, 60662306a36Sopenharmony_ci client->name, iqs62x); 60762306a36Sopenharmony_ci if (ret) { 60862306a36Sopenharmony_ci dev_err(&client->dev, "Failed to request IRQ: %d\n", ret); 60962306a36Sopenharmony_ci goto err_out; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (!wait_for_completion_timeout(&iqs62x->ati_done, 61362306a36Sopenharmony_ci msecs_to_jiffies(2000))) { 61462306a36Sopenharmony_ci dev_err(&client->dev, "Failed to complete ATI\n"); 61562306a36Sopenharmony_ci goto err_out; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, 61962306a36Sopenharmony_ci iqs62x->dev_desc->sub_devs, 62062306a36Sopenharmony_ci iqs62x->dev_desc->num_sub_devs, 62162306a36Sopenharmony_ci NULL, 0, NULL); 62262306a36Sopenharmony_ci if (ret) 62362306a36Sopenharmony_ci dev_err(&client->dev, "Failed to add sub-devices: %d\n", ret); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cierr_out: 62662306a36Sopenharmony_ci complete_all(&iqs62x->fw_done); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic const struct mfd_cell iqs620at_sub_devs[] = { 63062306a36Sopenharmony_ci { 63162306a36Sopenharmony_ci .name = "iqs62x-keys", 63262306a36Sopenharmony_ci .of_compatible = "azoteq,iqs620a-keys", 63362306a36Sopenharmony_ci }, 63462306a36Sopenharmony_ci { 63562306a36Sopenharmony_ci .name = "iqs620a-pwm", 63662306a36Sopenharmony_ci .of_compatible = "azoteq,iqs620a-pwm", 63762306a36Sopenharmony_ci }, 63862306a36Sopenharmony_ci { .name = "iqs620at-temp", }, 63962306a36Sopenharmony_ci}; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic const struct mfd_cell iqs620a_sub_devs[] = { 64262306a36Sopenharmony_ci { 64362306a36Sopenharmony_ci .name = "iqs62x-keys", 64462306a36Sopenharmony_ci .of_compatible = "azoteq,iqs620a-keys", 64562306a36Sopenharmony_ci }, 64662306a36Sopenharmony_ci { 64762306a36Sopenharmony_ci .name = "iqs620a-pwm", 64862306a36Sopenharmony_ci .of_compatible = "azoteq,iqs620a-pwm", 64962306a36Sopenharmony_ci }, 65062306a36Sopenharmony_ci}; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic const struct mfd_cell iqs621_sub_devs[] = { 65362306a36Sopenharmony_ci { 65462306a36Sopenharmony_ci .name = "iqs62x-keys", 65562306a36Sopenharmony_ci .of_compatible = "azoteq,iqs621-keys", 65662306a36Sopenharmony_ci }, 65762306a36Sopenharmony_ci { .name = "iqs621-als", }, 65862306a36Sopenharmony_ci}; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic const struct mfd_cell iqs622_sub_devs[] = { 66162306a36Sopenharmony_ci { 66262306a36Sopenharmony_ci .name = "iqs62x-keys", 66362306a36Sopenharmony_ci .of_compatible = "azoteq,iqs622-keys", 66462306a36Sopenharmony_ci }, 66562306a36Sopenharmony_ci { .name = "iqs621-als", }, 66662306a36Sopenharmony_ci}; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic const struct mfd_cell iqs624_sub_devs[] = { 66962306a36Sopenharmony_ci { 67062306a36Sopenharmony_ci .name = "iqs62x-keys", 67162306a36Sopenharmony_ci .of_compatible = "azoteq,iqs624-keys", 67262306a36Sopenharmony_ci }, 67362306a36Sopenharmony_ci { .name = "iqs624-pos", }, 67462306a36Sopenharmony_ci}; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic const struct mfd_cell iqs625_sub_devs[] = { 67762306a36Sopenharmony_ci { 67862306a36Sopenharmony_ci .name = "iqs62x-keys", 67962306a36Sopenharmony_ci .of_compatible = "azoteq,iqs625-keys", 68062306a36Sopenharmony_ci }, 68162306a36Sopenharmony_ci { .name = "iqs624-pos", }, 68262306a36Sopenharmony_ci}; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic const u8 iqs620at_cal_regs[] = { 68562306a36Sopenharmony_ci IQS620_TEMP_CAL_MULT, 68662306a36Sopenharmony_ci IQS620_TEMP_CAL_DIV, 68762306a36Sopenharmony_ci IQS620_TEMP_CAL_OFFS, 68862306a36Sopenharmony_ci}; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic const u8 iqs621_cal_regs[] = { 69162306a36Sopenharmony_ci IQS621_ALS_CAL_DIV_LUX, 69262306a36Sopenharmony_ci IQS621_ALS_CAL_DIV_IR, 69362306a36Sopenharmony_ci}; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic const enum iqs62x_event_reg iqs620a_event_regs[][IQS62X_EVENT_SIZE] = { 69662306a36Sopenharmony_ci [IQS62X_UI_PROX] = { 69762306a36Sopenharmony_ci IQS62X_EVENT_SYS, /* 0x10 */ 69862306a36Sopenharmony_ci IQS62X_EVENT_NONE, 69962306a36Sopenharmony_ci IQS62X_EVENT_PROX, /* 0x12 */ 70062306a36Sopenharmony_ci IQS62X_EVENT_HYST, /* 0x13 */ 70162306a36Sopenharmony_ci IQS62X_EVENT_NONE, 70262306a36Sopenharmony_ci IQS62X_EVENT_NONE, 70362306a36Sopenharmony_ci IQS62X_EVENT_HALL, /* 0x16 */ 70462306a36Sopenharmony_ci IQS62X_EVENT_NONE, 70562306a36Sopenharmony_ci IQS62X_EVENT_NONE, 70662306a36Sopenharmony_ci IQS62X_EVENT_NONE, 70762306a36Sopenharmony_ci }, 70862306a36Sopenharmony_ci [IQS62X_UI_SAR1] = { 70962306a36Sopenharmony_ci IQS62X_EVENT_SYS, /* 0x10 */ 71062306a36Sopenharmony_ci IQS62X_EVENT_NONE, 71162306a36Sopenharmony_ci IQS62X_EVENT_NONE, 71262306a36Sopenharmony_ci IQS62X_EVENT_HYST, /* 0x13 */ 71362306a36Sopenharmony_ci IQS62X_EVENT_NONE, 71462306a36Sopenharmony_ci IQS62X_EVENT_NONE, 71562306a36Sopenharmony_ci IQS62X_EVENT_HALL, /* 0x16 */ 71662306a36Sopenharmony_ci IQS62X_EVENT_NONE, 71762306a36Sopenharmony_ci IQS62X_EVENT_NONE, 71862306a36Sopenharmony_ci IQS62X_EVENT_NONE, 71962306a36Sopenharmony_ci }, 72062306a36Sopenharmony_ci}; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic const enum iqs62x_event_reg iqs621_event_regs[][IQS62X_EVENT_SIZE] = { 72362306a36Sopenharmony_ci [IQS62X_UI_PROX] = { 72462306a36Sopenharmony_ci IQS62X_EVENT_SYS, /* 0x10 */ 72562306a36Sopenharmony_ci IQS62X_EVENT_NONE, 72662306a36Sopenharmony_ci IQS62X_EVENT_PROX, /* 0x12 */ 72762306a36Sopenharmony_ci IQS62X_EVENT_HYST, /* 0x13 */ 72862306a36Sopenharmony_ci IQS62X_EVENT_NONE, 72962306a36Sopenharmony_ci IQS62X_EVENT_NONE, 73062306a36Sopenharmony_ci IQS62X_EVENT_ALS, /* 0x16 */ 73162306a36Sopenharmony_ci IQS62X_EVENT_UI_LO, /* 0x17 */ 73262306a36Sopenharmony_ci IQS62X_EVENT_UI_HI, /* 0x18 */ 73362306a36Sopenharmony_ci IQS62X_EVENT_HALL, /* 0x19 */ 73462306a36Sopenharmony_ci }, 73562306a36Sopenharmony_ci}; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic const enum iqs62x_event_reg iqs622_event_regs[][IQS62X_EVENT_SIZE] = { 73862306a36Sopenharmony_ci [IQS62X_UI_PROX] = { 73962306a36Sopenharmony_ci IQS62X_EVENT_SYS, /* 0x10 */ 74062306a36Sopenharmony_ci IQS62X_EVENT_NONE, 74162306a36Sopenharmony_ci IQS62X_EVENT_PROX, /* 0x12 */ 74262306a36Sopenharmony_ci IQS62X_EVENT_NONE, 74362306a36Sopenharmony_ci IQS62X_EVENT_ALS, /* 0x14 */ 74462306a36Sopenharmony_ci IQS62X_EVENT_NONE, 74562306a36Sopenharmony_ci IQS62X_EVENT_IR, /* 0x16 */ 74662306a36Sopenharmony_ci IQS62X_EVENT_UI_LO, /* 0x17 */ 74762306a36Sopenharmony_ci IQS62X_EVENT_UI_HI, /* 0x18 */ 74862306a36Sopenharmony_ci IQS62X_EVENT_HALL, /* 0x19 */ 74962306a36Sopenharmony_ci }, 75062306a36Sopenharmony_ci [IQS62X_UI_SAR1] = { 75162306a36Sopenharmony_ci IQS62X_EVENT_SYS, /* 0x10 */ 75262306a36Sopenharmony_ci IQS62X_EVENT_NONE, 75362306a36Sopenharmony_ci IQS62X_EVENT_NONE, 75462306a36Sopenharmony_ci IQS62X_EVENT_HYST, /* 0x13 */ 75562306a36Sopenharmony_ci IQS62X_EVENT_ALS, /* 0x14 */ 75662306a36Sopenharmony_ci IQS62X_EVENT_NONE, 75762306a36Sopenharmony_ci IQS62X_EVENT_IR, /* 0x16 */ 75862306a36Sopenharmony_ci IQS62X_EVENT_UI_LO, /* 0x17 */ 75962306a36Sopenharmony_ci IQS62X_EVENT_UI_HI, /* 0x18 */ 76062306a36Sopenharmony_ci IQS62X_EVENT_HALL, /* 0x19 */ 76162306a36Sopenharmony_ci }, 76262306a36Sopenharmony_ci}; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cistatic const enum iqs62x_event_reg iqs624_event_regs[][IQS62X_EVENT_SIZE] = { 76562306a36Sopenharmony_ci [IQS62X_UI_PROX] = { 76662306a36Sopenharmony_ci IQS62X_EVENT_SYS, /* 0x10 */ 76762306a36Sopenharmony_ci IQS62X_EVENT_NONE, 76862306a36Sopenharmony_ci IQS62X_EVENT_PROX, /* 0x12 */ 76962306a36Sopenharmony_ci IQS62X_EVENT_NONE, 77062306a36Sopenharmony_ci IQS62X_EVENT_WHEEL, /* 0x14 */ 77162306a36Sopenharmony_ci IQS62X_EVENT_NONE, 77262306a36Sopenharmony_ci IQS62X_EVENT_UI_LO, /* 0x16 */ 77362306a36Sopenharmony_ci IQS62X_EVENT_UI_HI, /* 0x17 */ 77462306a36Sopenharmony_ci IQS62X_EVENT_INTER, /* 0x18 */ 77562306a36Sopenharmony_ci IQS62X_EVENT_NONE, 77662306a36Sopenharmony_ci }, 77762306a36Sopenharmony_ci}; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic const enum iqs62x_event_reg iqs625_event_regs[][IQS62X_EVENT_SIZE] = { 78062306a36Sopenharmony_ci [IQS62X_UI_PROX] = { 78162306a36Sopenharmony_ci IQS62X_EVENT_SYS, /* 0x10 */ 78262306a36Sopenharmony_ci IQS62X_EVENT_PROX, /* 0x11 */ 78362306a36Sopenharmony_ci IQS62X_EVENT_INTER, /* 0x12 */ 78462306a36Sopenharmony_ci IQS62X_EVENT_NONE, 78562306a36Sopenharmony_ci IQS62X_EVENT_NONE, 78662306a36Sopenharmony_ci IQS62X_EVENT_NONE, 78762306a36Sopenharmony_ci IQS62X_EVENT_NONE, 78862306a36Sopenharmony_ci IQS62X_EVENT_NONE, 78962306a36Sopenharmony_ci IQS62X_EVENT_NONE, 79062306a36Sopenharmony_ci IQS62X_EVENT_NONE, 79162306a36Sopenharmony_ci }, 79262306a36Sopenharmony_ci}; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic const struct iqs62x_dev_desc iqs62x_devs[] = { 79562306a36Sopenharmony_ci { 79662306a36Sopenharmony_ci .dev_name = "iqs620at", 79762306a36Sopenharmony_ci .sub_devs = iqs620at_sub_devs, 79862306a36Sopenharmony_ci .num_sub_devs = ARRAY_SIZE(iqs620at_sub_devs), 79962306a36Sopenharmony_ci .prod_num = IQS620_PROD_NUM, 80062306a36Sopenharmony_ci .sw_num = 0x08, 80162306a36Sopenharmony_ci .cal_regs = iqs620at_cal_regs, 80262306a36Sopenharmony_ci .num_cal_regs = ARRAY_SIZE(iqs620at_cal_regs), 80362306a36Sopenharmony_ci .prox_mask = BIT(0), 80462306a36Sopenharmony_ci .sar_mask = BIT(1) | BIT(7), 80562306a36Sopenharmony_ci .hall_mask = BIT(2), 80662306a36Sopenharmony_ci .hyst_mask = BIT(3), 80762306a36Sopenharmony_ci .temp_mask = BIT(4), 80862306a36Sopenharmony_ci .prox_settings = IQS620_PROX_SETTINGS_4, 80962306a36Sopenharmony_ci .hall_flags = IQS620_HALL_FLAGS, 81062306a36Sopenharmony_ci .fw_name = "iqs620a.bin", 81162306a36Sopenharmony_ci .event_regs = &iqs620a_event_regs[IQS62X_UI_PROX], 81262306a36Sopenharmony_ci }, 81362306a36Sopenharmony_ci { 81462306a36Sopenharmony_ci .dev_name = "iqs620a", 81562306a36Sopenharmony_ci .sub_devs = iqs620a_sub_devs, 81662306a36Sopenharmony_ci .num_sub_devs = ARRAY_SIZE(iqs620a_sub_devs), 81762306a36Sopenharmony_ci .prod_num = IQS620_PROD_NUM, 81862306a36Sopenharmony_ci .sw_num = 0x08, 81962306a36Sopenharmony_ci .prox_mask = BIT(0), 82062306a36Sopenharmony_ci .sar_mask = BIT(1) | BIT(7), 82162306a36Sopenharmony_ci .hall_mask = BIT(2), 82262306a36Sopenharmony_ci .hyst_mask = BIT(3), 82362306a36Sopenharmony_ci .temp_mask = BIT(4), 82462306a36Sopenharmony_ci .prox_settings = IQS620_PROX_SETTINGS_4, 82562306a36Sopenharmony_ci .hall_flags = IQS620_HALL_FLAGS, 82662306a36Sopenharmony_ci .fw_name = "iqs620a.bin", 82762306a36Sopenharmony_ci .event_regs = &iqs620a_event_regs[IQS62X_UI_PROX], 82862306a36Sopenharmony_ci }, 82962306a36Sopenharmony_ci { 83062306a36Sopenharmony_ci .dev_name = "iqs621", 83162306a36Sopenharmony_ci .sub_devs = iqs621_sub_devs, 83262306a36Sopenharmony_ci .num_sub_devs = ARRAY_SIZE(iqs621_sub_devs), 83362306a36Sopenharmony_ci .prod_num = IQS621_PROD_NUM, 83462306a36Sopenharmony_ci .sw_num = 0x09, 83562306a36Sopenharmony_ci .cal_regs = iqs621_cal_regs, 83662306a36Sopenharmony_ci .num_cal_regs = ARRAY_SIZE(iqs621_cal_regs), 83762306a36Sopenharmony_ci .prox_mask = BIT(0), 83862306a36Sopenharmony_ci .hall_mask = BIT(1), 83962306a36Sopenharmony_ci .als_mask = BIT(2), 84062306a36Sopenharmony_ci .hyst_mask = BIT(3), 84162306a36Sopenharmony_ci .temp_mask = BIT(4), 84262306a36Sopenharmony_ci .als_flags = IQS621_ALS_FLAGS, 84362306a36Sopenharmony_ci .hall_flags = IQS621_HALL_FLAGS, 84462306a36Sopenharmony_ci .hyst_shift = 5, 84562306a36Sopenharmony_ci .fw_name = "iqs621.bin", 84662306a36Sopenharmony_ci .event_regs = &iqs621_event_regs[IQS62X_UI_PROX], 84762306a36Sopenharmony_ci }, 84862306a36Sopenharmony_ci { 84962306a36Sopenharmony_ci .dev_name = "iqs622", 85062306a36Sopenharmony_ci .sub_devs = iqs622_sub_devs, 85162306a36Sopenharmony_ci .num_sub_devs = ARRAY_SIZE(iqs622_sub_devs), 85262306a36Sopenharmony_ci .prod_num = IQS622_PROD_NUM, 85362306a36Sopenharmony_ci .sw_num = 0x06, 85462306a36Sopenharmony_ci .prox_mask = BIT(0), 85562306a36Sopenharmony_ci .sar_mask = BIT(1), 85662306a36Sopenharmony_ci .hall_mask = BIT(2), 85762306a36Sopenharmony_ci .als_mask = BIT(3), 85862306a36Sopenharmony_ci .ir_mask = BIT(4), 85962306a36Sopenharmony_ci .prox_settings = IQS622_PROX_SETTINGS_4, 86062306a36Sopenharmony_ci .als_flags = IQS622_ALS_FLAGS, 86162306a36Sopenharmony_ci .hall_flags = IQS622_HALL_FLAGS, 86262306a36Sopenharmony_ci .fw_name = "iqs622.bin", 86362306a36Sopenharmony_ci .event_regs = &iqs622_event_regs[IQS62X_UI_PROX], 86462306a36Sopenharmony_ci }, 86562306a36Sopenharmony_ci { 86662306a36Sopenharmony_ci .dev_name = "iqs624", 86762306a36Sopenharmony_ci .sub_devs = iqs624_sub_devs, 86862306a36Sopenharmony_ci .num_sub_devs = ARRAY_SIZE(iqs624_sub_devs), 86962306a36Sopenharmony_ci .prod_num = IQS624_PROD_NUM, 87062306a36Sopenharmony_ci .sw_num = 0x0B, 87162306a36Sopenharmony_ci .interval = IQS624_INTERVAL_NUM, 87262306a36Sopenharmony_ci .interval_div = 3, 87362306a36Sopenharmony_ci .fw_name = "iqs624.bin", 87462306a36Sopenharmony_ci .event_regs = &iqs624_event_regs[IQS62X_UI_PROX], 87562306a36Sopenharmony_ci }, 87662306a36Sopenharmony_ci { 87762306a36Sopenharmony_ci .dev_name = "iqs625", 87862306a36Sopenharmony_ci .sub_devs = iqs625_sub_devs, 87962306a36Sopenharmony_ci .num_sub_devs = ARRAY_SIZE(iqs625_sub_devs), 88062306a36Sopenharmony_ci .prod_num = IQS625_PROD_NUM, 88162306a36Sopenharmony_ci .sw_num = 0x0B, 88262306a36Sopenharmony_ci .interval = IQS625_INTERVAL_NUM, 88362306a36Sopenharmony_ci .interval_div = 10, 88462306a36Sopenharmony_ci .fw_name = "iqs625.bin", 88562306a36Sopenharmony_ci .event_regs = &iqs625_event_regs[IQS62X_UI_PROX], 88662306a36Sopenharmony_ci }, 88762306a36Sopenharmony_ci}; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cistatic const struct regmap_config iqs62x_regmap_config = { 89062306a36Sopenharmony_ci .reg_bits = 8, 89162306a36Sopenharmony_ci .val_bits = 8, 89262306a36Sopenharmony_ci .max_register = IQS62X_MAX_REG, 89362306a36Sopenharmony_ci}; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic int iqs62x_probe(struct i2c_client *client) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct iqs62x_core *iqs62x; 89862306a36Sopenharmony_ci struct iqs62x_info info; 89962306a36Sopenharmony_ci unsigned int val; 90062306a36Sopenharmony_ci int ret, i, j; 90162306a36Sopenharmony_ci const char *fw_name = NULL; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci iqs62x = devm_kzalloc(&client->dev, sizeof(*iqs62x), GFP_KERNEL); 90462306a36Sopenharmony_ci if (!iqs62x) 90562306a36Sopenharmony_ci return -ENOMEM; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci i2c_set_clientdata(client, iqs62x); 90862306a36Sopenharmony_ci iqs62x->client = client; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci BLOCKING_INIT_NOTIFIER_HEAD(&iqs62x->nh); 91162306a36Sopenharmony_ci INIT_LIST_HEAD(&iqs62x->fw_blk_head); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci init_completion(&iqs62x->ati_done); 91462306a36Sopenharmony_ci init_completion(&iqs62x->fw_done); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci iqs62x->regmap = devm_regmap_init_i2c(client, &iqs62x_regmap_config); 91762306a36Sopenharmony_ci if (IS_ERR(iqs62x->regmap)) { 91862306a36Sopenharmony_ci ret = PTR_ERR(iqs62x->regmap); 91962306a36Sopenharmony_ci dev_err(&client->dev, "Failed to initialize register map: %d\n", 92062306a36Sopenharmony_ci ret); 92162306a36Sopenharmony_ci return ret; 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci ret = regmap_raw_read(iqs62x->regmap, IQS62X_PROD_NUM, &info, 92562306a36Sopenharmony_ci sizeof(info)); 92662306a36Sopenharmony_ci if (ret) 92762306a36Sopenharmony_ci return ret; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci /* 93062306a36Sopenharmony_ci * The following sequence validates the device's product and software 93162306a36Sopenharmony_ci * numbers. It then determines if the device is factory-calibrated by 93262306a36Sopenharmony_ci * checking for nonzero values in the device's designated calibration 93362306a36Sopenharmony_ci * registers (if applicable). Depending on the device, the absence of 93462306a36Sopenharmony_ci * calibration data indicates a reduced feature set or invalid device. 93562306a36Sopenharmony_ci * 93662306a36Sopenharmony_ci * For devices given in both calibrated and uncalibrated versions, the 93762306a36Sopenharmony_ci * calibrated version (e.g. IQS620AT) appears first in the iqs62x_devs 93862306a36Sopenharmony_ci * array. The uncalibrated version (e.g. IQS620A) appears next and has 93962306a36Sopenharmony_ci * the same product and software numbers, but no calibration registers 94062306a36Sopenharmony_ci * are specified. 94162306a36Sopenharmony_ci */ 94262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(iqs62x_devs); i++) { 94362306a36Sopenharmony_ci if (info.prod_num != iqs62x_devs[i].prod_num) 94462306a36Sopenharmony_ci continue; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci iqs62x->dev_desc = &iqs62x_devs[i]; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (info.sw_num < iqs62x->dev_desc->sw_num) 94962306a36Sopenharmony_ci continue; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci iqs62x->sw_num = info.sw_num; 95262306a36Sopenharmony_ci iqs62x->hw_num = info.hw_num; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci /* 95562306a36Sopenharmony_ci * Read each of the device's designated calibration registers, 95662306a36Sopenharmony_ci * if any, and exit from the inner loop early if any are equal 95762306a36Sopenharmony_ci * to zero (indicating the device is uncalibrated). This could 95862306a36Sopenharmony_ci * be acceptable depending on the device (e.g. IQS620A instead 95962306a36Sopenharmony_ci * of IQS620AT). 96062306a36Sopenharmony_ci */ 96162306a36Sopenharmony_ci for (j = 0; j < iqs62x->dev_desc->num_cal_regs; j++) { 96262306a36Sopenharmony_ci ret = regmap_read(iqs62x->regmap, 96362306a36Sopenharmony_ci iqs62x->dev_desc->cal_regs[j], &val); 96462306a36Sopenharmony_ci if (ret) 96562306a36Sopenharmony_ci return ret; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (!val) 96862306a36Sopenharmony_ci break; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* 97262306a36Sopenharmony_ci * If the number of nonzero values read from the device equals 97362306a36Sopenharmony_ci * the number of designated calibration registers (which could 97462306a36Sopenharmony_ci * be zero), exit from the outer loop early to signal that the 97562306a36Sopenharmony_ci * device's product and software numbers match a known device, 97662306a36Sopenharmony_ci * and the device is calibrated (if applicable). 97762306a36Sopenharmony_ci */ 97862306a36Sopenharmony_ci if (j == iqs62x->dev_desc->num_cal_regs) 97962306a36Sopenharmony_ci break; 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (!iqs62x->dev_desc) { 98362306a36Sopenharmony_ci dev_err(&client->dev, "Unrecognized product number: 0x%02X\n", 98462306a36Sopenharmony_ci info.prod_num); 98562306a36Sopenharmony_ci return -EINVAL; 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci if (!iqs62x->sw_num) { 98962306a36Sopenharmony_ci dev_err(&client->dev, "Unrecognized software number: 0x%02X\n", 99062306a36Sopenharmony_ci info.sw_num); 99162306a36Sopenharmony_ci return -EINVAL; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (i == ARRAY_SIZE(iqs62x_devs)) { 99562306a36Sopenharmony_ci dev_err(&client->dev, "Uncalibrated device\n"); 99662306a36Sopenharmony_ci return -ENODATA; 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci device_property_read_string(&client->dev, "firmware-name", &fw_name); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, 100262306a36Sopenharmony_ci fw_name ? : iqs62x->dev_desc->fw_name, 100362306a36Sopenharmony_ci &client->dev, GFP_KERNEL, iqs62x, 100462306a36Sopenharmony_ci iqs62x_firmware_load); 100562306a36Sopenharmony_ci if (ret) 100662306a36Sopenharmony_ci dev_err(&client->dev, "Failed to request firmware: %d\n", ret); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci return ret; 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic void iqs62x_remove(struct i2c_client *client) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci struct iqs62x_core *iqs62x = i2c_get_clientdata(client); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci wait_for_completion(&iqs62x->fw_done); 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_cistatic int __maybe_unused iqs62x_suspend(struct device *dev) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci struct iqs62x_core *iqs62x = dev_get_drvdata(dev); 102162306a36Sopenharmony_ci int ret; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci wait_for_completion(&iqs62x->fw_done); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci /* 102662306a36Sopenharmony_ci * As per the datasheet, automatic mode switching must be disabled 102762306a36Sopenharmony_ci * before the device is placed in or taken out of halt mode. 102862306a36Sopenharmony_ci */ 102962306a36Sopenharmony_ci ret = regmap_update_bits(iqs62x->regmap, IQS62X_PWR_SETTINGS, 103062306a36Sopenharmony_ci IQS62X_PWR_SETTINGS_DIS_AUTO, 0xFF); 103162306a36Sopenharmony_ci if (ret) 103262306a36Sopenharmony_ci return ret; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci return regmap_update_bits(iqs62x->regmap, IQS62X_PWR_SETTINGS, 103562306a36Sopenharmony_ci IQS62X_PWR_SETTINGS_PWR_MODE_MASK, 103662306a36Sopenharmony_ci IQS62X_PWR_SETTINGS_PWR_MODE_HALT); 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_cistatic int __maybe_unused iqs62x_resume(struct device *dev) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci struct iqs62x_core *iqs62x = dev_get_drvdata(dev); 104262306a36Sopenharmony_ci int ret; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci ret = regmap_update_bits(iqs62x->regmap, IQS62X_PWR_SETTINGS, 104562306a36Sopenharmony_ci IQS62X_PWR_SETTINGS_PWR_MODE_MASK, 104662306a36Sopenharmony_ci IQS62X_PWR_SETTINGS_PWR_MODE_NORM); 104762306a36Sopenharmony_ci if (ret) 104862306a36Sopenharmony_ci return ret; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci return regmap_update_bits(iqs62x->regmap, IQS62X_PWR_SETTINGS, 105162306a36Sopenharmony_ci IQS62X_PWR_SETTINGS_DIS_AUTO, 0); 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(iqs62x_pm, iqs62x_suspend, iqs62x_resume); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_cistatic const struct of_device_id iqs62x_of_match[] = { 105762306a36Sopenharmony_ci { .compatible = "azoteq,iqs620a" }, 105862306a36Sopenharmony_ci { .compatible = "azoteq,iqs621" }, 105962306a36Sopenharmony_ci { .compatible = "azoteq,iqs622" }, 106062306a36Sopenharmony_ci { .compatible = "azoteq,iqs624" }, 106162306a36Sopenharmony_ci { .compatible = "azoteq,iqs625" }, 106262306a36Sopenharmony_ci { } 106362306a36Sopenharmony_ci}; 106462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, iqs62x_of_match); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic struct i2c_driver iqs62x_i2c_driver = { 106762306a36Sopenharmony_ci .driver = { 106862306a36Sopenharmony_ci .name = "iqs62x", 106962306a36Sopenharmony_ci .of_match_table = iqs62x_of_match, 107062306a36Sopenharmony_ci .pm = &iqs62x_pm, 107162306a36Sopenharmony_ci }, 107262306a36Sopenharmony_ci .probe = iqs62x_probe, 107362306a36Sopenharmony_ci .remove = iqs62x_remove, 107462306a36Sopenharmony_ci}; 107562306a36Sopenharmony_cimodule_i2c_driver(iqs62x_i2c_driver); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ciMODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>"); 107862306a36Sopenharmony_ciMODULE_DESCRIPTION("Azoteq IQS620A/621/622/624/625 Multi-Function Sensors"); 107962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1080