Input: iqs269a - add support for OTP variants
authorJeff LaBundy <jeff@labundy.com>
Mon, 1 Jan 2024 20:02:45 +0000 (14:02 -0600)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Mon, 1 Jan 2024 21:24:03 +0000 (13:24 -0800)
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>
drivers/input/misc/iqs269a.c

index 0d0b5cdc783032c9a5eadf066248e9959ee69da2..cd14ff9f57cf239819d76cf3bae0da8c13524655 100644 (file)
 #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
 
 #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);