diff options
Diffstat (limited to 'drivers/input/misc/pm8941-pwrkey.c')
-rw-r--r-- | drivers/input/misc/pm8941-pwrkey.c | 127 |
1 files changed, 113 insertions, 14 deletions
diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c index 89af52498c96..549df01b6ee3 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) @@ -45,6 +57,7 @@ struct pm8941_data { unsigned int status_bit; bool supports_ps_hold_poff_config; bool supports_debounce_config; + bool has_pon_pbs; const char *name; const char *phys; }; @@ -53,13 +66,18 @@ 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 sw_debounce_end_time; + bool last_status; const struct pm8941_data *data; }; @@ -129,20 +147,76 @@ 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); + + /* + * Simulate a press event in case a release event occurred without a + * corresponding press event. + */ + if (!pwrkey->last_status && !sts) { + input_report_key(pwrkey->input, pwrkey->code, 1); + input_sync(pwrkey->input); + } + pwrkey->last_status = sts; + + 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); @@ -171,6 +245,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; @@ -192,8 +268,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 @@ -204,15 +282,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_cpup(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_cpup(addr); } - if (error) - return error; pwrkey->irq = platform_get_irq(pdev, 0); if (pwrkey->irq < 0) @@ -221,7 +305,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; } @@ -259,6 +350,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, @@ -320,6 +415,7 @@ static const struct pm8941_data pwrkey_data = { .phys = "pm8941_pwrkey/input0", .supports_ps_hold_poff_config = true, .supports_debounce_config = true, + .has_pon_pbs = false, }; static const struct pm8941_data resin_data = { @@ -329,6 +425,7 @@ static const struct pm8941_data resin_data = { .phys = "pm8941_resin/input0", .supports_ps_hold_poff_config = true, .supports_debounce_config = true, + .has_pon_pbs = false, }; static const struct pm8941_data pon_gen3_pwrkey_data = { @@ -337,6 +434,7 @@ static const struct pm8941_data pon_gen3_pwrkey_data = { .phys = "pmic_pwrkey/input0", .supports_ps_hold_poff_config = false, .supports_debounce_config = false, + .has_pon_pbs = true, }; static const struct pm8941_data pon_gen3_resin_data = { @@ -345,6 +443,7 @@ static const struct pm8941_data pon_gen3_resin_data = { .phys = "pmic_resin/input0", .supports_ps_hold_poff_config = false, .supports_debounce_config = false, + .has_pon_pbs = true, }; static const struct of_device_id pm8941_pwr_key_id_table[] = { |