diff options
author | Jeff LaBundy | 2024-01-01 14:02:45 -0600 |
---|---|---|
committer | Dmitry Torokhov | 2024-01-01 13:24:03 -0800 |
commit | 992bbc9e9ab9abe5bc1ea9a1a8d61331b28f848e (patch) | |
tree | d4513ad7a5322bee086913b4840d712f582d6423 /drivers/input | |
parent | 56c083e3f5723c68055ab5f97d21e55aab7bd8ef (diff) |
Input: iqs269a - add support for OTP variants
This patch adds support for each available OTP variant of the device.
The OTP configuration cannot be read over I2C, so it is derived from
a compatible string instead.
Early revisions of the D0 order code require their OTP-enabled func-
tionality to be manually restored following a soft reset; this patch
accommodates this erratum as well.
Signed-off-by: Jeff LaBundy <jeff@labundy.com>
Link: https://lore.kernel.org/r/ZZMaZbdk6iAKUjlm@nixie71
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/misc/iqs269a.c | 92 |
1 files changed, 89 insertions, 3 deletions
diff --git a/drivers/input/misc/iqs269a.c b/drivers/input/misc/iqs269a.c index 0d0b5cdc7830..cd14ff9f57cf 100644 --- a/drivers/input/misc/iqs269a.c +++ b/drivers/input/misc/iqs269a.c @@ -102,6 +102,11 @@ #define IQS269_MISC_B_TRACKING_UI_ENABLE BIT(4) #define IQS269_MISC_B_FILT_STR_SLIDER GENMASK(1, 0) +#define IQS269_TOUCH_HOLD_SLIDER_SEL 0x89 +#define IQS269_TOUCH_HOLD_DEFAULT 0x14 +#define IQS269_TOUCH_HOLD_MS_MIN 256 +#define IQS269_TOUCH_HOLD_MS_MAX 65280 + #define IQS269_TIMEOUT_TAP_MS_MAX 4080 #define IQS269_TIMEOUT_SWIPE_MS_MAX 4080 #define IQS269_THRESH_SWIPE_MAX 255 @@ -151,6 +156,10 @@ #define IQS269_MAX_REG 0xFF +#define IQS269_OTP_OPTION_DEFAULT 0x00 +#define IQS269_OTP_OPTION_TWS 0xD0 +#define IQS269_OTP_OPTION_HOLD BIT(7) + #define IQS269_NUM_CH 8 #define IQS269_NUM_SL 2 @@ -315,6 +324,7 @@ struct iqs269_private { struct input_dev *slider[IQS269_NUM_SL]; unsigned int keycode[ARRAY_SIZE(iqs269_events) * IQS269_NUM_CH]; unsigned int sl_code[IQS269_NUM_SL][IQS269_NUM_GESTURES]; + unsigned int otp_option; unsigned int ch_num; bool hall_enable; bool ati_current; @@ -325,6 +335,14 @@ static enum iqs269_slider_id iqs269_slider_type(struct iqs269_private *iqs269, { int i; + /* + * Slider 1 is unavailable if the touch-and-hold option is enabled via + * OTP. In that case, the channel selection register is repurposed for + * the touch-and-hold timer ceiling. + */ + if (slider_num && (iqs269->otp_option & IQS269_OTP_OPTION_HOLD)) + return IQS269_SLIDER_NONE; + if (!iqs269->sys_reg.slider_select[slider_num]) return IQS269_SLIDER_NONE; @@ -565,7 +583,8 @@ static int iqs269_parse_chan(struct iqs269_private *iqs269, if (fwnode_property_present(ch_node, "azoteq,slider0-select")) iqs269->sys_reg.slider_select[0] |= BIT(reg); - if (fwnode_property_present(ch_node, "azoteq,slider1-select")) + if (fwnode_property_present(ch_node, "azoteq,slider1-select") && + !(iqs269->otp_option & IQS269_OTP_OPTION_HOLD)) iqs269->sys_reg.slider_select[1] |= BIT(reg); ch_reg = &iqs269->sys_reg.ch_reg[reg]; @@ -990,7 +1009,43 @@ static int iqs269_parse_prop(struct iqs269_private *iqs269) sys_reg->blocking = 0; sys_reg->slider_select[0] = 0; - sys_reg->slider_select[1] = 0; + + /* + * If configured via OTP to do so, the device asserts a pulse on the + * GPIO4 pin for approximately 60 ms once a selected channel is held + * in a state of touch for a configurable length of time. + * + * In that case, the register used for slider 1 channel selection is + * repurposed for the touch-and-hold timer ceiling. + */ + if (iqs269->otp_option & IQS269_OTP_OPTION_HOLD) { + if (!device_property_read_u32(&client->dev, + "azoteq,touch-hold-ms", &val)) { + if (val < IQS269_TOUCH_HOLD_MS_MIN || + val > IQS269_TOUCH_HOLD_MS_MAX) { + dev_err(&client->dev, + "Invalid touch-and-hold ceiling: %u\n", + val); + return -EINVAL; + } + + sys_reg->slider_select[1] = val / 256; + } else if (iqs269->ver_info.fw_num < IQS269_VER_INFO_FW_NUM_3) { + /* + * The default touch-and-hold timer ceiling initially + * read from early revisions of silicon is invalid if + * the device experienced a soft reset between power- + * on and the read operation. + * + * To protect against this case, explicitly cache the + * default value so that it is restored each time the + * device is re-initialized. + */ + sys_reg->slider_select[1] = IQS269_TOUCH_HOLD_DEFAULT; + } + } else { + sys_reg->slider_select[1] = 0; + } sys_reg->event_mask = ~((u8)IQS269_EVENT_MASK_SYS); @@ -1137,12 +1192,30 @@ static int iqs269_parse_prop(struct iqs269_private *iqs269) return 0; } +static const struct reg_sequence iqs269_tws_init[] = { + { IQS269_TOUCH_HOLD_SLIDER_SEL, IQS269_TOUCH_HOLD_DEFAULT }, + { 0xF0, 0x580F }, + { 0xF0, 0x59EF }, +}; + static int iqs269_dev_init(struct iqs269_private *iqs269) { int error; mutex_lock(&iqs269->lock); + /* + * Early revisions of silicon require the following workaround in order + * to restore any OTP-enabled functionality after a soft reset. + */ + if (iqs269->otp_option == IQS269_OTP_OPTION_TWS && + iqs269->ver_info.fw_num < IQS269_VER_INFO_FW_NUM_3) { + error = regmap_multi_reg_write(iqs269->regmap, iqs269_tws_init, + ARRAY_SIZE(iqs269_tws_init)); + if (error) + goto err_mutex; + } + error = regmap_update_bits(iqs269->regmap, IQS269_HALL_UI, IQS269_HALL_UI_ENABLE, iqs269->hall_enable ? ~0 : 0); @@ -1779,6 +1852,8 @@ static int iqs269_probe(struct i2c_client *client) mutex_init(&iqs269->lock); init_completion(&iqs269->ati_done); + iqs269->otp_option = (uintptr_t)device_get_match_data(&client->dev); + error = regmap_raw_read(iqs269->regmap, IQS269_VER_INFO, &iqs269->ver_info, sizeof(iqs269->ver_info)); if (error) @@ -1889,7 +1964,18 @@ static int iqs269_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(iqs269_pm, iqs269_suspend, iqs269_resume); static const struct of_device_id iqs269_of_match[] = { - { .compatible = "azoteq,iqs269a" }, + { + .compatible = "azoteq,iqs269a", + .data = (void *)IQS269_OTP_OPTION_DEFAULT, + }, + { + .compatible = "azoteq,iqs269a-00", + .data = (void *)IQS269_OTP_OPTION_DEFAULT, + }, + { + .compatible = "azoteq,iqs269a-d0", + .data = (void *)IQS269_OTP_OPTION_TWS, + }, { } }; MODULE_DEVICE_TABLE(of, iqs269_of_match); |