diff mbox series

[1/3] input: misc: pm8941-pwrkey: add software key press debouncing support

Message ID 20220120204132.17875-2-quic_amelende@quicinc.com (mailing list archive)
State Not Applicable
Headers show
Series Add support for pm8941-pwrkey.c | expand

Commit Message

Anjelique Melendez Jan. 20, 2022, 8:41 p.m. UTC
From: David Collins <collinsd@codeaurora.org>

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.

Change-Id: Ifa3809935c01aab9078ba2302397bc9ebf390021
Signed-off-by: David Collins <collinsd@codeaurora.org>
Signed-off-by: Anjelique Melendez <quic_amelende@quicinc.com>
---
 drivers/input/misc/pm8941-pwrkey.c | 115 ++++++++++++++++++++++++++---
 1 file changed, 105 insertions(+), 10 deletions(-)

Comments

Stephen Boyd Jan. 21, 2022, 4:08 a.m. UTC | #1
Quoting Anjelique Melendez (2022-01-20 12:41:33)
> From: David Collins <collinsd@codeaurora.org>
>
> 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.
>
> Change-Id: Ifa3809935c01aab9078ba2302397bc9ebf390021

Please remove change-id when upstreaming.

> Signed-off-by: David Collins <collinsd@codeaurora.org>
> Signed-off-by: Anjelique Melendez <quic_amelende@quicinc.com>
> ---
> diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c
> index 33609603245d..b912ce00ce1c 100644
> --- a/drivers/input/misc/pm8941-pwrkey.c
> +++ b/drivers/input/misc/pm8941-pwrkey.c
> @@ -126,19 +144,65 @@ static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
>         struct pm8941_pwrkey *pwrkey = _data;
>         unsigned int sts;
>         int error;
> +       u64 elapsed_us;
> +
> +       if (pwrkey->sw_debounce_time_us) {
> +               elapsed_us = ktime_us_delta(ktime_get(),
> +                                           pwrkey->last_release_time);
> +               if (elapsed_us < pwrkey->sw_debounce_time_us) {

Perhaps storing last_release_time + sw_debounce_time_us via
ktime_add_us() in the struct would be better. Then this line would be

	if (ktime_before(debounce_end, ktime_get()))

and we'd avoid a division when converting to microseconds to compare
time.

> +                       dev_dbg(pwrkey->dev, "ignoring key event received after %llu us, debounce time=%u us\n",
> +                               elapsed_us, pwrkey->sw_debounce_time_us);
> +                       return IRQ_HANDLED;
> +               }
> +       }
>
>         error = regmap_read(pwrkey->regmap,
>                             pwrkey->baseaddr + PON_RT_STS, &sts);

Nitpick: I'd prefer this be on one line. And 'ret' or 'err' be used as
it's shorter.

>         if (error)
>                 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->last_release_time = ktime_get();
> +
> +       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;
> +       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)
> +               pwrkey->sw_debounce_time_us = 2 * USEC_PER_SEC /
> +                                               (1 << (0xf - (val & 0xf)));
> +       else
> +               pwrkey->sw_debounce_time_us = 2 * USEC_PER_SEC /
> +                                               (1 << (0x7 - (val & 0x7)));

Can we have one more local variable like 'mask' or 'offset'. Then the
code would be easier to read

	if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY)
		mask = 0xf;
	else
		mask = 0x7
	
	pwrkey->sw_debounce_time_us = 2 * USEC_PER_SEC / (1 << mask - (val & 0x7));

> +
> +       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);
> @@ -167,6 +231,8 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>         struct pm8941_pwrkey *pwrkey;
>         bool pull_up;
>         struct device *parent;
> +       struct device_node *regmap_node;
> +       const __be32 *addr;
>         u32 req_delay;
>         int error;
>
> @@ -188,8 +254,10 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>         pwrkey->data = of_device_get_match_data(&pdev->dev);
>
>         parent = pdev->dev.parent;
> +       regmap_node = pdev->dev.of_node;
>         pwrkey->regmap = dev_get_regmap(parent, NULL);
>         if (!pwrkey->regmap) {
> +               regmap_node = parent->of_node;
>                 /*
>                  * We failed to get regmap for parent. Let's see if we are
>                  * a child of pon node and read regmap and reg from its
> @@ -200,15 +268,21 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>                         dev_err(&pdev->dev, "failed to locate regmap\n");
>                         return -ENODEV;
>                 }
> +       }
>
> -               error = of_property_read_u32(parent->of_node,
> -                                            "reg", &pwrkey->baseaddr);
> -       } else {
> -               error = of_property_read_u32(pdev->dev.of_node, "reg",
> -                                            &pwrkey->baseaddr);
> +       addr = of_get_address(regmap_node, 0, NULL, NULL);
> +       if (!addr) {
> +               dev_err(&pdev->dev, "reg property missing\n");
> +               return -EINVAL;
> +       }
> +       pwrkey->baseaddr = be32_to_cpu(*addr);

Can this hunk be split off? A new API is used and it doesn't look
relevant to this patch.

> +
> +       if (pwrkey->data->has_pon_pbs) {
> +               /* PON_PBS base address is optional */
> +               addr = of_get_address(regmap_node, 1, NULL, NULL);
> +               if (addr)
> +                       pwrkey->pon_pbs_baseaddr = be32_to_cpu(*addr);
>         }
> -       if (error)
> -               return error;
>
>         pwrkey->irq = platform_get_irq(pdev, 0);
>         if (pwrkey->irq < 0)
> @@ -217,7 +291,14 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>         error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
>                             &pwrkey->revision);
>         if (error) {
> -               dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
> +               dev_err(&pdev->dev, "failed to read revision: %d\n", error);

Nice error message fix!

> +               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;
>         }
>
> @@ -255,6 +336,12 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>                 }
>         }
>
> +       if (pwrkey->data->needs_sw_debounce) {
> +               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,
> @@ -316,6 +403,8 @@ static const struct pm8941_data pwrkey_data = {
>         .phys = "pm8941_pwrkey/input0",
>         .supports_ps_hold_poff_config = true,
>         .supports_debounce_config = true,
> +       .needs_sw_debounce = true,

needs_sw_debounce is always true? Why is it even an option then?

> +       .has_pon_pbs = false,
>  };
>
>  static const struct pm8941_data resin_data = {
Anjelique Melendez Jan. 22, 2022, 12:04 a.m. UTC | #2
On 1/20/2022 8:08 PM, Stephen Boyd wrote:
> Quoting Anjelique Melendez (2022-01-20 12:41:33)
>> From: David Collins <collinsd@codeaurora.org>
>>
>> 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.
>>
>> Change-Id: Ifa3809935c01aab9078ba2302397bc9ebf390021
> Please remove change-id when upstreaming.

Will remove change-id from other 2 patches as well.

>
>> Signed-off-by: David Collins <collinsd@codeaurora.org>
>> Signed-off-by: Anjelique Melendez <quic_amelende@quicinc.com>
>> ---
>> diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c
>> index 33609603245d..b912ce00ce1c 100644
>> --- a/drivers/input/misc/pm8941-pwrkey.c
>> +++ b/drivers/input/misc/pm8941-pwrkey.c
>> @@ -126,19 +144,65 @@ static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
>>         struct pm8941_pwrkey *pwrkey = _data;
>>         unsigned int sts;
>>         int error;
>> +       u64 elapsed_us;
>> +
>> +       if (pwrkey->sw_debounce_time_us) {
>> +               elapsed_us = ktime_us_delta(ktime_get(),
>> +                                           pwrkey->last_release_time);
>> +               if (elapsed_us < pwrkey->sw_debounce_time_us) {
> Perhaps storing last_release_time + sw_debounce_time_us via
> ktime_add_us() in the struct would be better. Then this line would be
>
> 	if (ktime_before(debounce_end, ktime_get()))
>
> and we'd avoid a division when converting to microseconds to compare
> time.

Sure this can be done!

>> +                       dev_dbg(pwrkey->dev, "ignoring key event received after %llu us, debounce time=%u us\n",
>> +                               elapsed_us, pwrkey->sw_debounce_time_us);
>> +                       return IRQ_HANDLED;
>> +               }
>> +       }
>>
>>         error = regmap_read(pwrkey->regmap,
>>                             pwrkey->baseaddr + PON_RT_STS, &sts);
> Nitpick: I'd prefer this be on one line. And 'ret' or 'err' be used as
> it's shorter.

ACK

>
>>         if (error)
>>                 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->last_release_time = ktime_get();
>> +
>> +       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;
>> +       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)
>> +               pwrkey->sw_debounce_time_us = 2 * USEC_PER_SEC /
>> +                                               (1 << (0xf - (val & 0xf)));
>> +       else
>> +               pwrkey->sw_debounce_time_us = 2 * USEC_PER_SEC /
>> +                                               (1 << (0x7 - (val & 0x7)));
> Can we have one more local variable like 'mask' or 'offset'. Then the
> code would be easier to read
>
> 	if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY)
> 		mask = 0xf;
> 	else
> 		mask = 0x7
> 	
> 	pwrkey->sw_debounce_time_us = 2 * USEC_PER_SEC / (1 << mask - (val & 0x7));

Sure not a problem!

>> +
>> +       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);
>> @@ -167,6 +231,8 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>>         struct pm8941_pwrkey *pwrkey;
>>         bool pull_up;
>>         struct device *parent;
>> +       struct device_node *regmap_node;
>> +       const __be32 *addr;
>>         u32 req_delay;
>>         int error;
>>
>> @@ -188,8 +254,10 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>>         pwrkey->data = of_device_get_match_data(&pdev->dev);
>>
>>         parent = pdev->dev.parent;
>> +       regmap_node = pdev->dev.of_node;
>>         pwrkey->regmap = dev_get_regmap(parent, NULL);
>>         if (!pwrkey->regmap) {
>> +               regmap_node = parent->of_node;
>>                 /*
>>                  * We failed to get regmap for parent. Let's see if we are
>>                  * a child of pon node and read regmap and reg from its
>> @@ -200,15 +268,21 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>>                         dev_err(&pdev->dev, "failed to locate regmap\n");
>>                         return -ENODEV;
>>                 }
>> +       }
>>
>> -               error = of_property_read_u32(parent->of_node,
>> -                                            "reg", &pwrkey->baseaddr);
>> -       } else {
>> -               error = of_property_read_u32(pdev->dev.of_node, "reg",
>> -                                            &pwrkey->baseaddr);
>> +       addr = of_get_address(regmap_node, 0, NULL, NULL);
>> +       if (!addr) {
>> +               dev_err(&pdev->dev, "reg property missing\n");
>> +               return -EINVAL;
>> +       }
>> +       pwrkey->baseaddr = be32_to_cpu(*addr);
> Can this hunk be split off? A new API is used and it doesn't look
> relevant to this patch.

In PMK8350 and following chips the reg property will have the pon hlos address first,
followed by a second pon pbs address. This change is needed so that both the older chipsets
and the newer can be used regardless of how many reg addresses are being used.

>
>> +
>> +       if (pwrkey->data->has_pon_pbs) {
>> +               /* PON_PBS base address is optional */
>> +               addr = of_get_address(regmap_node, 1, NULL, NULL);
>> +               if (addr)
>> +                       pwrkey->pon_pbs_baseaddr = be32_to_cpu(*addr);
>>         }
>> -       if (error)
>> -               return error;
>>
>>         pwrkey->irq = platform_get_irq(pdev, 0);
>>         if (pwrkey->irq < 0)
>> @@ -217,7 +291,14 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>>         error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
>>                             &pwrkey->revision);
>>         if (error) {
>> -               dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
>> +               dev_err(&pdev->dev, "failed to read revision: %d\n", error);
> Nice error message fix!
>
>> +               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;
>>         }
>>
>> @@ -255,6 +336,12 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>>                 }
>>         }
>>
>> +       if (pwrkey->data->needs_sw_debounce) {
>> +               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,
>> @@ -316,6 +403,8 @@ static const struct pm8941_data pwrkey_data = {
>>         .phys = "pm8941_pwrkey/input0",
>>         .supports_ps_hold_poff_config = true,
>>         .supports_debounce_config = true,
>> +       .needs_sw_debounce = true,
> needs_sw_debounce is always true? Why is it even an option then?

As of right now the "needs_sw_debounce" property is being used for a sw work around for a hw
problem. We anticipate that chips in the future will fix this hw problem and we would then have
devices where needs_sw_debounce would be set to false.

>
>> +       .has_pon_pbs = false,
>>  };
>>
>>  static const struct pm8941_data resin_data = {
Stephen Boyd Jan. 24, 2022, 7:33 p.m. UTC | #3
Quoting Anjelique Melendez (2022-01-21 16:04:13)
>
>
> On 1/20/2022 8:08 PM, Stephen Boyd wrote:
> > Quoting Anjelique Melendez (2022-01-20 12:41:33)
> >> @@ -200,15 +268,21 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
> >>                         dev_err(&pdev->dev, "failed to locate regmap\n");
> >>                         return -ENODEV;
> >>                 }
> >> +       }
> >>
> >> -               error = of_property_read_u32(parent->of_node,
> >> -                                            "reg", &pwrkey->baseaddr);
> >> -       } else {
> >> -               error = of_property_read_u32(pdev->dev.of_node, "reg",
> >> -                                            &pwrkey->baseaddr);
> >> +       addr = of_get_address(regmap_node, 0, NULL, NULL);
> >> +       if (!addr) {
> >> +               dev_err(&pdev->dev, "reg property missing\n");
> >> +               return -EINVAL;
> >> +       }
> >> +       pwrkey->baseaddr = be32_to_cpu(*addr);
> > Can this hunk be split off? A new API is used and it doesn't look
> > relevant to this patch.
>
> In PMK8350 and following chips the reg property will have the pon hlos address first,
> followed by a second pon pbs address. This change is needed so that both the older chipsets
> and the newer can be used regardless of how many reg addresses are being used.

Got it, but do we ned to change to of_get_address() in this patch? I was
suggesting that the change to the new API be done first so that it's
clearer what's going on with the change in address location.

>
> >
> >> +
> >> +       if (pwrkey->data->has_pon_pbs) {
> >> +               /* PON_PBS base address is optional */
> >> +               addr = of_get_address(regmap_node, 1, NULL, NULL);
> >> +               if (addr)
> >> +                       pwrkey->pon_pbs_baseaddr = be32_to_cpu(*addr);
> >>         }
> >> -       if (error)
> >> -               return error;
> >>
> >>         pwrkey->irq = platform_get_irq(pdev, 0);
> >>         if (pwrkey->irq < 0)
> >> @@ -217,7 +291,14 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
> >>         error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
> >>                             &pwrkey->revision);
> >>         if (error) {
> >> -               dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
> >> +               dev_err(&pdev->dev, "failed to read revision: %d\n", error);
> > Nice error message fix!

This can be split off to a different patch as well.

> >
> >> +               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;
> >>         }
> >>
> >> @@ -255,6 +336,12 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
> >>                 }
> >>         }
> >>
> >> +       if (pwrkey->data->needs_sw_debounce) {
> >> +               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,
> >> @@ -316,6 +403,8 @@ static const struct pm8941_data pwrkey_data = {
> >>         .phys = "pm8941_pwrkey/input0",
> >>         .supports_ps_hold_poff_config = true,
> >>         .supports_debounce_config = true,
> >> +       .needs_sw_debounce = true,
> > needs_sw_debounce is always true? Why is it even an option then?
>
> As of right now the "needs_sw_debounce" property is being used for a sw work around for a hw
> problem. We anticipate that chips in the future will fix this hw problem and we would then have
> devices where needs_sw_debounce would be set to false.

Hmm ok. Why can't future chips be supported in this series? What happens
if nobody ever adds support for the new chips? We're left with this
condition that looks like dead code.
Anjelique Melendez Jan. 25, 2022, 7:24 p.m. UTC | #4
On 1/24/2022 11:33 AM, Stephen Boyd wrote:
> Quoting Anjelique Melendez (2022-01-21 16:04:13)
>>
>> On 1/20/2022 8:08 PM, Stephen Boyd wrote:
>>> Quoting Anjelique Melendez (2022-01-20 12:41:33)
>>>> @@ -200,15 +268,21 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>>>>                         dev_err(&pdev->dev, "failed to locate regmap\n");
>>>>                         return -ENODEV;
>>>>                 }
>>>> +       }
>>>>
>>>> -               error = of_property_read_u32(parent->of_node,
>>>> -                                            "reg", &pwrkey->baseaddr);
>>>> -       } else {
>>>> -               error = of_property_read_u32(pdev->dev.of_node, "reg",
>>>> -                                            &pwrkey->baseaddr);
>>>> +       addr = of_get_address(regmap_node, 0, NULL, NULL);
>>>> +       if (!addr) {
>>>> +               dev_err(&pdev->dev, "reg property missing\n");
>>>> +               return -EINVAL;
>>>> +       }
>>>> +       pwrkey->baseaddr = be32_to_cpu(*addr);
>>> Can this hunk be split off? A new API is used and it doesn't look
>>> relevant to this patch.
>> In PMK8350 and following chips the reg property will have the pon hlos address first,
>> followed by a second pon pbs address. This change is needed so that both the older chipsets
>> and the newer can be used regardless of how many reg addresses are being used.
> Got it, but do we ned to change to of_get_address() in this patch? I was
> suggesting that the change to the new API be done first so that it's
> clearer what's going on with the change in address location.

Ok, makes sense. Will separate this into it's own patch for v2.

>>>> +
>>>> +       if (pwrkey->data->has_pon_pbs) {
>>>> +               /* PON_PBS base address is optional */
>>>> +               addr = of_get_address(regmap_node, 1, NULL, NULL);
>>>> +               if (addr)
>>>> +                       pwrkey->pon_pbs_baseaddr = be32_to_cpu(*addr);
>>>>         }
>>>> -       if (error)
>>>> -               return error;
>>>>
>>>>         pwrkey->irq = platform_get_irq(pdev, 0);
>>>>         if (pwrkey->irq < 0)
>>>> @@ -217,7 +291,14 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>>>>         error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
>>>>                             &pwrkey->revision);
>>>>         if (error) {
>>>> -               dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
>>>> +               dev_err(&pdev->dev, "failed to read revision: %d\n", error);
>>> Nice error message fix!
> This can be split off to a different patch as well.

Will do.

>>>> +               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;
>>>>         }
>>>>
>>>> @@ -255,6 +336,12 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
>>>>                 }
>>>>         }
>>>>
>>>> +       if (pwrkey->data->needs_sw_debounce) {
>>>> +               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,
>>>> @@ -316,6 +403,8 @@ static const struct pm8941_data pwrkey_data = {
>>>>         .phys = "pm8941_pwrkey/input0",
>>>>         .supports_ps_hold_poff_config = true,
>>>>         .supports_debounce_config = true,
>>>> +       .needs_sw_debounce = true,
>>> needs_sw_debounce is always true? Why is it even an option then?
>> As of right now the "needs_sw_debounce" property is being used for a sw work around for a hw
>> problem. We anticipate that chips in the future will fix this hw problem and we would then have
>> devices where needs_sw_debounce would be set to false.
> Hmm ok. Why can't future chips be supported in this series? What happens
> if nobody ever adds support for the new chips? We're left with this
> condition that looks like dead code.

Sure, makes sense. Will remove "needs_sw_debounce" property for V2.
diff mbox series

Patch

diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c
index 33609603245d..b912ce00ce1c 100644
--- a/drivers/input/misc/pm8941-pwrkey.c
+++ b/drivers/input/misc/pm8941-pwrkey.c
@@ -9,9 +9,11 @@ 
 #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>
+#include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/reboot.h>
@@ -19,6 +21,16 @@ 
 
 #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)
@@ -44,6 +56,8 @@  struct pm8941_data {
 	unsigned int	status_bit;
 	bool		supports_ps_hold_poff_config;
 	bool		supports_debounce_config;
+	bool		needs_sw_debounce;
+	bool		has_pon_pbs;
 	const char	*name;
 	const char	*phys;
 };
@@ -52,13 +66,17 @@  struct pm8941_pwrkey {
 	struct device *dev;
 	int irq;
 	u32 baseaddr;
+	u32 pon_pbs_baseaddr;
 	struct regmap *regmap;
 	struct input_dev *input;
 
 	unsigned int revision;
+	unsigned int subtype;
 	struct notifier_block reboot_notifier;
 
 	u32 code;
+	u32 sw_debounce_time_us;
+	ktime_t last_release_time;
 	const struct pm8941_data *data;
 };
 
@@ -126,19 +144,65 @@  static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
 	struct pm8941_pwrkey *pwrkey = _data;
 	unsigned int sts;
 	int error;
+	u64 elapsed_us;
+
+	if (pwrkey->sw_debounce_time_us) {
+		elapsed_us = ktime_us_delta(ktime_get(),
+					    pwrkey->last_release_time);
+		if (elapsed_us < pwrkey->sw_debounce_time_us) {
+			dev_dbg(pwrkey->dev, "ignoring key event received after %llu us, debounce time=%u us\n",
+				elapsed_us, pwrkey->sw_debounce_time_us);
+			return IRQ_HANDLED;
+		}
+	}
 
 	error = regmap_read(pwrkey->regmap,
 			    pwrkey->baseaddr + PON_RT_STS, &sts);
 	if (error)
 		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->last_release_time = ktime_get();
+
+	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;
+	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)
+		pwrkey->sw_debounce_time_us = 2 * USEC_PER_SEC /
+						(1 << (0xf - (val & 0xf)));
+	else
+		pwrkey->sw_debounce_time_us = 2 * USEC_PER_SEC /
+						(1 << (0x7 - (val & 0x7)));
+
+	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);
@@ -167,6 +231,8 @@  static int pm8941_pwrkey_probe(struct platform_device *pdev)
 	struct pm8941_pwrkey *pwrkey;
 	bool pull_up;
 	struct device *parent;
+	struct device_node *regmap_node;
+	const __be32 *addr;
 	u32 req_delay;
 	int error;
 
@@ -188,8 +254,10 @@  static int pm8941_pwrkey_probe(struct platform_device *pdev)
 	pwrkey->data = of_device_get_match_data(&pdev->dev);
 
 	parent = pdev->dev.parent;
+	regmap_node = pdev->dev.of_node;
 	pwrkey->regmap = dev_get_regmap(parent, NULL);
 	if (!pwrkey->regmap) {
+		regmap_node = parent->of_node;
 		/*
 		 * We failed to get regmap for parent. Let's see if we are
 		 * a child of pon node and read regmap and reg from its
@@ -200,15 +268,21 @@  static int pm8941_pwrkey_probe(struct platform_device *pdev)
 			dev_err(&pdev->dev, "failed to locate regmap\n");
 			return -ENODEV;
 		}
+	}
 
-		error = of_property_read_u32(parent->of_node,
-					     "reg", &pwrkey->baseaddr);
-	} else {
-		error = of_property_read_u32(pdev->dev.of_node, "reg",
-					     &pwrkey->baseaddr);
+	addr = of_get_address(regmap_node, 0, NULL, NULL);
+	if (!addr) {
+		dev_err(&pdev->dev, "reg property missing\n");
+		return -EINVAL;
+	}
+	pwrkey->baseaddr = be32_to_cpu(*addr);
+
+	if (pwrkey->data->has_pon_pbs) {
+		/* PON_PBS base address is optional */
+		addr = of_get_address(regmap_node, 1, NULL, NULL);
+		if (addr)
+			pwrkey->pon_pbs_baseaddr = be32_to_cpu(*addr);
 	}
-	if (error)
-		return error;
 
 	pwrkey->irq = platform_get_irq(pdev, 0);
 	if (pwrkey->irq < 0)
@@ -217,7 +291,14 @@  static int pm8941_pwrkey_probe(struct platform_device *pdev)
 	error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
 			    &pwrkey->revision);
 	if (error) {
-		dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
+		dev_err(&pdev->dev, "failed to read revision: %d\n", error);
+		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;
 	}
 
@@ -255,6 +336,12 @@  static int pm8941_pwrkey_probe(struct platform_device *pdev)
 		}
 	}
 
+	if (pwrkey->data->needs_sw_debounce) {
+		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,
@@ -316,6 +403,8 @@  static const struct pm8941_data pwrkey_data = {
 	.phys = "pm8941_pwrkey/input0",
 	.supports_ps_hold_poff_config = true,
 	.supports_debounce_config = true,
+	.needs_sw_debounce = true,
+	.has_pon_pbs = false,
 };
 
 static const struct pm8941_data resin_data = {
@@ -325,6 +414,8 @@  static const struct pm8941_data resin_data = {
 	.phys = "pm8941_resin/input0",
 	.supports_ps_hold_poff_config = true,
 	.supports_debounce_config = true,
+	.needs_sw_debounce = true,
+	.has_pon_pbs = false,
 };
 
 static const struct pm8941_data pon_gen3_pwrkey_data = {
@@ -333,6 +424,8 @@  static const struct pm8941_data pon_gen3_pwrkey_data = {
 	.phys = "pmic_pwrkey/input0",
 	.supports_ps_hold_poff_config = false,
 	.supports_debounce_config = false,
+	.needs_sw_debounce = true,
+	.has_pon_pbs = true,
 };
 
 static const struct pm8941_data pon_gen3_resin_data = {
@@ -341,6 +434,8 @@  static const struct pm8941_data pon_gen3_resin_data = {
 	.phys = "pmic_resin/input0",
 	.supports_ps_hold_poff_config = false,
 	.supports_debounce_config = false,
+	.needs_sw_debounce = true,
+	.has_pon_pbs = true,
 };
 
 static const struct of_device_id pm8941_pwr_key_id_table[] = {