diff options
Diffstat (limited to 'drivers/mfd/iqs62x.c')
-rw-r--r-- | drivers/mfd/iqs62x.c | 125 |
1 files changed, 69 insertions, 56 deletions
diff --git a/drivers/mfd/iqs62x.c b/drivers/mfd/iqs62x.c index 07c972531715..9b5c389ca2c3 100644 --- a/drivers/mfd/iqs62x.c +++ b/drivers/mfd/iqs62x.c @@ -36,7 +36,6 @@ #define IQS62X_PROD_NUM 0x00 #define IQS62X_SYS_FLAGS 0x10 -#define IQS62X_SYS_FLAGS_IN_ATI BIT(2) #define IQS620_HALL_FLAGS 0x16 #define IQS621_HALL_FLAGS 0x19 @@ -60,6 +59,7 @@ #define IQS62X_SYS_SETTINGS_ACK_RESET BIT(6) #define IQS62X_SYS_SETTINGS_EVENT_MODE BIT(5) #define IQS62X_SYS_SETTINGS_CLK_DIV BIT(4) +#define IQS62X_SYS_SETTINGS_COMM_ATI BIT(3) #define IQS62X_SYS_SETTINGS_REDO_ATI BIT(1) #define IQS62X_PWR_SETTINGS 0xD2 @@ -81,9 +81,7 @@ #define IQS62X_FW_REC_TYPE_MASK 3 #define IQS62X_FW_REC_TYPE_DATA 4 -#define IQS62X_ATI_POLL_SLEEP_US 10000 -#define IQS62X_ATI_POLL_TIMEOUT_US 500000 -#define IQS62X_ATI_STABLE_DELAY_MS 150 +#define IQS62X_FILT_SETTLE_MS 250 struct iqs62x_fw_rec { u8 type; @@ -111,7 +109,6 @@ static int iqs62x_dev_init(struct iqs62x_core *iqs62x) struct iqs62x_fw_blk *fw_blk; unsigned int val; int ret; - u8 clk_div = 1; list_for_each_entry(fw_blk, &iqs62x->fw_blk_head, list) { if (fw_blk->mask) @@ -181,28 +178,32 @@ static int iqs62x_dev_init(struct iqs62x_core *iqs62x) return ret; } - ret = regmap_read(iqs62x->regmap, IQS62X_SYS_SETTINGS, &val); - if (ret) - return ret; - - if (val & IQS62X_SYS_SETTINGS_CLK_DIV) - clk_div = iqs62x->dev_desc->clk_div; - - ret = regmap_write(iqs62x->regmap, IQS62X_SYS_SETTINGS, val | - IQS62X_SYS_SETTINGS_ACK_RESET | - IQS62X_SYS_SETTINGS_EVENT_MODE | - IQS62X_SYS_SETTINGS_REDO_ATI); - if (ret) - return ret; - - ret = regmap_read_poll_timeout(iqs62x->regmap, IQS62X_SYS_FLAGS, val, - !(val & IQS62X_SYS_FLAGS_IN_ATI), - IQS62X_ATI_POLL_SLEEP_US, - IQS62X_ATI_POLL_TIMEOUT_US * clk_div); + /* + * Place the device in streaming mode at first so as not to miss the + * limited number of interrupts that would otherwise occur after ATI + * completes. The device is subsequently placed in event mode by the + * interrupt handler. + * + * In the meantime, mask interrupts during ATI to prevent the device + * from soliciting I2C traffic until the noise-sensitive ATI process + * is complete. + */ + ret = regmap_update_bits(iqs62x->regmap, IQS62X_SYS_SETTINGS, + IQS62X_SYS_SETTINGS_ACK_RESET | + IQS62X_SYS_SETTINGS_EVENT_MODE | + IQS62X_SYS_SETTINGS_COMM_ATI | + IQS62X_SYS_SETTINGS_REDO_ATI, + IQS62X_SYS_SETTINGS_ACK_RESET | + IQS62X_SYS_SETTINGS_REDO_ATI); if (ret) return ret; - msleep(IQS62X_ATI_STABLE_DELAY_MS * clk_div); + /* + * The following delay gives the device time to deassert its RDY output + * in case a communication window was open while the REDO_ATI field was + * written. This prevents an interrupt from being serviced prematurely. + */ + usleep_range(5000, 5100); return 0; } @@ -433,6 +434,11 @@ const struct iqs62x_event_desc iqs62x_events[IQS62X_NUM_EVENTS] = { .mask = BIT(7), .val = BIT(7), }, + [IQS62X_EVENT_SYS_ATI] = { + .reg = IQS62X_EVENT_SYS, + .mask = BIT(2), + .val = BIT(2), + }, }; EXPORT_SYMBOL_GPL(iqs62x_events); @@ -521,12 +527,39 @@ static irqreturn_t iqs62x_irq(int irq, void *context) "Failed to re-initialize device: %d\n", ret); return IRQ_NONE; } + + iqs62x->event_cache |= BIT(IQS62X_EVENT_SYS_RESET); + reinit_completion(&iqs62x->ati_done); + } else if (event_flags & BIT(IQS62X_EVENT_SYS_ATI)) { + iqs62x->event_cache |= BIT(IQS62X_EVENT_SYS_ATI); + reinit_completion(&iqs62x->ati_done); + } else if (!completion_done(&iqs62x->ati_done)) { + ret = regmap_update_bits(iqs62x->regmap, IQS62X_SYS_SETTINGS, + IQS62X_SYS_SETTINGS_EVENT_MODE, 0xFF); + if (ret) { + dev_err(&client->dev, + "Failed to enable event mode: %d\n", ret); + return IRQ_NONE; + } + + msleep(IQS62X_FILT_SETTLE_MS); + complete_all(&iqs62x->ati_done); } - ret = blocking_notifier_call_chain(&iqs62x->nh, event_flags, - &event_data); - if (ret & NOTIFY_STOP_MASK) - return IRQ_NONE; + /* + * Reset and ATI events are not broadcast to the sub-device drivers + * until ATI has completed. Any other events that may have occurred + * during ATI are ignored. + */ + if (completion_done(&iqs62x->ati_done)) { + event_flags |= iqs62x->event_cache; + ret = blocking_notifier_call_chain(&iqs62x->nh, event_flags, + &event_data); + if (ret & NOTIFY_STOP_MASK) + return IRQ_NONE; + + iqs62x->event_cache = 0; + } /* * Once the communication window is closed, a small delay is added to @@ -567,6 +600,12 @@ static void iqs62x_firmware_load(const struct firmware *fw, void *context) goto err_out; } + if (!wait_for_completion_timeout(&iqs62x->ati_done, + msecs_to_jiffies(2000))) { + dev_err(&client->dev, "Failed to complete ATI\n"); + goto err_out; + } + ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, iqs62x->dev_desc->sub_devs, iqs62x->dev_desc->num_sub_devs, @@ -748,22 +787,17 @@ static const struct iqs62x_dev_desc iqs62x_devs[] = { .dev_name = "iqs620at", .sub_devs = iqs620at_sub_devs, .num_sub_devs = ARRAY_SIZE(iqs620at_sub_devs), - .prod_num = IQS620_PROD_NUM, .sw_num = 0x08, .cal_regs = iqs620at_cal_regs, .num_cal_regs = ARRAY_SIZE(iqs620at_cal_regs), - .prox_mask = BIT(0), .sar_mask = BIT(1) | BIT(7), .hall_mask = BIT(2), .hyst_mask = BIT(3), .temp_mask = BIT(4), - .prox_settings = IQS620_PROX_SETTINGS_4, .hall_flags = IQS620_HALL_FLAGS, - - .clk_div = 4, .fw_name = "iqs620a.bin", .event_regs = &iqs620a_event_regs[IQS62X_UI_PROX], }, @@ -771,20 +805,15 @@ static const struct iqs62x_dev_desc iqs62x_devs[] = { .dev_name = "iqs620a", .sub_devs = iqs620a_sub_devs, .num_sub_devs = ARRAY_SIZE(iqs620a_sub_devs), - .prod_num = IQS620_PROD_NUM, .sw_num = 0x08, - .prox_mask = BIT(0), .sar_mask = BIT(1) | BIT(7), .hall_mask = BIT(2), .hyst_mask = BIT(3), .temp_mask = BIT(4), - .prox_settings = IQS620_PROX_SETTINGS_4, .hall_flags = IQS620_HALL_FLAGS, - - .clk_div = 4, .fw_name = "iqs620a.bin", .event_regs = &iqs620a_event_regs[IQS62X_UI_PROX], }, @@ -792,23 +821,18 @@ static const struct iqs62x_dev_desc iqs62x_devs[] = { .dev_name = "iqs621", .sub_devs = iqs621_sub_devs, .num_sub_devs = ARRAY_SIZE(iqs621_sub_devs), - .prod_num = IQS621_PROD_NUM, .sw_num = 0x09, .cal_regs = iqs621_cal_regs, .num_cal_regs = ARRAY_SIZE(iqs621_cal_regs), - .prox_mask = BIT(0), .hall_mask = BIT(1), .als_mask = BIT(2), .hyst_mask = BIT(3), .temp_mask = BIT(4), - .als_flags = IQS621_ALS_FLAGS, .hall_flags = IQS621_HALL_FLAGS, .hyst_shift = 5, - - .clk_div = 2, .fw_name = "iqs621.bin", .event_regs = &iqs621_event_regs[IQS62X_UI_PROX], }, @@ -816,21 +840,16 @@ static const struct iqs62x_dev_desc iqs62x_devs[] = { .dev_name = "iqs622", .sub_devs = iqs622_sub_devs, .num_sub_devs = ARRAY_SIZE(iqs622_sub_devs), - .prod_num = IQS622_PROD_NUM, .sw_num = 0x06, - .prox_mask = BIT(0), .sar_mask = BIT(1), .hall_mask = BIT(2), .als_mask = BIT(3), .ir_mask = BIT(4), - .prox_settings = IQS622_PROX_SETTINGS_4, .als_flags = IQS622_ALS_FLAGS, .hall_flags = IQS622_HALL_FLAGS, - - .clk_div = 2, .fw_name = "iqs622.bin", .event_regs = &iqs622_event_regs[IQS62X_UI_PROX], }, @@ -838,14 +857,10 @@ static const struct iqs62x_dev_desc iqs62x_devs[] = { .dev_name = "iqs624", .sub_devs = iqs624_sub_devs, .num_sub_devs = ARRAY_SIZE(iqs624_sub_devs), - .prod_num = IQS624_PROD_NUM, .sw_num = 0x0B, - .interval = IQS624_INTERVAL_NUM, .interval_div = 3, - - .clk_div = 2, .fw_name = "iqs624.bin", .event_regs = &iqs624_event_regs[IQS62X_UI_PROX], }, @@ -853,14 +868,10 @@ static const struct iqs62x_dev_desc iqs62x_devs[] = { .dev_name = "iqs625", .sub_devs = iqs625_sub_devs, .num_sub_devs = ARRAY_SIZE(iqs625_sub_devs), - .prod_num = IQS625_PROD_NUM, .sw_num = 0x0B, - .interval = IQS625_INTERVAL_NUM, .interval_div = 10, - - .clk_div = 2, .fw_name = "iqs625.bin", .event_regs = &iqs625_event_regs[IQS62X_UI_PROX], }, @@ -890,6 +901,8 @@ static int iqs62x_probe(struct i2c_client *client) BLOCKING_INIT_NOTIFIER_HEAD(&iqs62x->nh); INIT_LIST_HEAD(&iqs62x->fw_blk_head); + + init_completion(&iqs62x->ati_done); init_completion(&iqs62x->fw_done); iqs62x->regmap = devm_regmap_init_i2c(client, &iqs62x_regmap_config); |