Input: pm8941-pwrkey - add software key press debouncing support
authorDavid Collins <collinsd@codeaurora.org>
Mon, 25 Apr 2022 01:20:05 +0000 (18:20 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Mon, 25 Apr 2022 01:25:13 +0000 (18:25 -0700)
On certain PMICs, an unexpected assertion of KPDPWR_DEB (the
positive logic hardware debounced power key signal) may be seen
during the falling edge of KPDPWR_N (i.e. a power key press) when
it occurs close to the rising edge of SLEEP_CLK.  This then
triggers a spurious KPDPWR interrupt.

Handle this issue by adding software debouncing support to ignore
key events that occur within the hardware debounce delay after the
most recent key release event.

Signed-off-by: David Collins <collinsd@codeaurora.org>
Signed-off-by: Anjelique Melendez <quic_amelende@quicinc.com>
Link: https://lore.kernel.org/r/20220422191239.6271-5-quic_amelende@quicinc.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/misc/pm8941-pwrkey.c

index 6c08ec0..4ae6d0f 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
+#include <linux/ktime.h>
 #include <linux/log2.h>
 #include <linux/module.h>
 #include <linux/of.h>
 
 #define PON_REV2                       0x01
 
+#define PON_SUBTYPE                    0x05
+
+#define PON_SUBTYPE_PRIMARY            0x01
+#define PON_SUBTYPE_SECONDARY          0x02
+#define PON_SUBTYPE_1REG               0x03
+#define PON_SUBTYPE_GEN2_PRIMARY       0x04
+#define PON_SUBTYPE_GEN2_SECONDARY     0x05
+#define PON_SUBTYPE_GEN3_PBS           0x08
+#define PON_SUBTYPE_GEN3_HLOS          0x09
+
 #define PON_RT_STS                     0x10
 #define  PON_KPDPWR_N_SET              BIT(0)
 #define  PON_RESIN_N_SET               BIT(1)
@@ -60,9 +71,12 @@ struct pm8941_pwrkey {
        struct input_dev *input;
 
        unsigned int revision;
+       unsigned int subtype;
        struct notifier_block reboot_notifier;
 
        u32 code;
+       u32 sw_debounce_time_us;
+       ktime_t sw_debounce_end_time;
        const struct pm8941_data *data;
 };
 
@@ -132,20 +146,66 @@ static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
 {
        struct pm8941_pwrkey *pwrkey = _data;
        unsigned int sts;
-       int error;
+       int err;
+
+       if (pwrkey->sw_debounce_time_us) {
+               if (ktime_before(ktime_get(), pwrkey->sw_debounce_end_time)) {
+                       dev_dbg(pwrkey->dev,
+                               "ignoring key event received before debounce end %llu us\n",
+                               pwrkey->sw_debounce_end_time);
+                       return IRQ_HANDLED;
+               }
+       }
 
-       error = regmap_read(pwrkey->regmap,
-                           pwrkey->baseaddr + PON_RT_STS, &sts);
-       if (error)
+       err = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_RT_STS, &sts);
+       if (err)
                return IRQ_HANDLED;
 
-       input_report_key(pwrkey->input, pwrkey->code,
-                        sts & pwrkey->data->status_bit);
+       sts &= pwrkey->data->status_bit;
+
+       if (pwrkey->sw_debounce_time_us && !sts)
+               pwrkey->sw_debounce_end_time = ktime_add_us(ktime_get(),
+                                               pwrkey->sw_debounce_time_us);
+
+       input_report_key(pwrkey->input, pwrkey->code, sts);
        input_sync(pwrkey->input);
 
        return IRQ_HANDLED;
 }
 
+static int pm8941_pwrkey_sw_debounce_init(struct pm8941_pwrkey *pwrkey)
+{
+       unsigned int val, addr, mask;
+       int error;
+
+       if (pwrkey->data->has_pon_pbs && !pwrkey->pon_pbs_baseaddr) {
+               dev_err(pwrkey->dev,
+                       "PON_PBS address missing, can't read HW debounce time\n");
+               return 0;
+       }
+
+       if (pwrkey->pon_pbs_baseaddr)
+               addr = pwrkey->pon_pbs_baseaddr + PON_DBC_CTL;
+       else
+               addr = pwrkey->baseaddr + PON_DBC_CTL;
+       error = regmap_read(pwrkey->regmap, addr, &val);
+       if (error)
+               return error;
+
+       if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY)
+               mask = 0xf;
+       else
+               mask = 0x7;
+
+       pwrkey->sw_debounce_time_us =
+               2 * USEC_PER_SEC / (1 << (mask - (val & mask)));
+
+       dev_dbg(pwrkey->dev, "SW debounce time = %u us\n",
+               pwrkey->sw_debounce_time_us);
+
+       return 0;
+}
+
 static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev)
 {
        struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
@@ -238,6 +298,13 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
                return error;
        }
 
+       error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_SUBTYPE,
+                           &pwrkey->subtype);
+       if (error) {
+               dev_err(&pdev->dev, "failed to read subtype: %d\n", error);
+               return error;
+       }
+
        error = of_property_read_u32(pdev->dev.of_node, "linux,code",
                                     &pwrkey->code);
        if (error) {
@@ -272,6 +339,10 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
                }
        }
 
+       error = pm8941_pwrkey_sw_debounce_init(pwrkey);
+       if (error)
+               return error;
+
        if (pwrkey->data->pull_up_bit) {
                error = regmap_update_bits(pwrkey->regmap,
                                           pwrkey->baseaddr + PON_PULL_CTL,