197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
268c581d5SCourtney Cavin /*
32fcbda9aSDavid Collins * Copyright (c) 2010-2011, 2020-2021, The Linux Foundation. All rights reserved.
468c581d5SCourtney Cavin * Copyright (c) 2014, Sony Mobile Communications Inc.
568c581d5SCourtney Cavin */
668c581d5SCourtney Cavin
768c581d5SCourtney Cavin #include <linux/delay.h>
868c581d5SCourtney Cavin #include <linux/errno.h>
968c581d5SCourtney Cavin #include <linux/input.h>
1068c581d5SCourtney Cavin #include <linux/interrupt.h>
1168c581d5SCourtney Cavin #include <linux/kernel.h>
120b65118eSDavid Collins #include <linux/ktime.h>
1368c581d5SCourtney Cavin #include <linux/log2.h>
1468c581d5SCourtney Cavin #include <linux/module.h>
1568c581d5SCourtney Cavin #include <linux/of.h>
168ac8904bSAnjelique Melendez #include <linux/of_address.h>
1768c581d5SCourtney Cavin #include <linux/platform_device.h>
1868c581d5SCourtney Cavin #include <linux/reboot.h>
1968c581d5SCourtney Cavin #include <linux/regmap.h>
2068c581d5SCourtney Cavin
2168c581d5SCourtney Cavin #define PON_REV2 0x01
2268c581d5SCourtney Cavin
230b65118eSDavid Collins #define PON_SUBTYPE 0x05
240b65118eSDavid Collins
250b65118eSDavid Collins #define PON_SUBTYPE_PRIMARY 0x01
260b65118eSDavid Collins #define PON_SUBTYPE_SECONDARY 0x02
270b65118eSDavid Collins #define PON_SUBTYPE_1REG 0x03
280b65118eSDavid Collins #define PON_SUBTYPE_GEN2_PRIMARY 0x04
290b65118eSDavid Collins #define PON_SUBTYPE_GEN2_SECONDARY 0x05
300b65118eSDavid Collins #define PON_SUBTYPE_GEN3_PBS 0x08
310b65118eSDavid Collins #define PON_SUBTYPE_GEN3_HLOS 0x09
320b65118eSDavid Collins
3368c581d5SCourtney Cavin #define PON_RT_STS 0x10
3468c581d5SCourtney Cavin #define PON_KPDPWR_N_SET BIT(0)
35955c594eSVinod Koul #define PON_RESIN_N_SET BIT(1)
362fcbda9aSDavid Collins #define PON_GEN3_RESIN_N_SET BIT(6)
372fcbda9aSDavid Collins #define PON_GEN3_KPDPWR_N_SET BIT(7)
3868c581d5SCourtney Cavin
3968c581d5SCourtney Cavin #define PON_PS_HOLD_RST_CTL 0x5a
4068c581d5SCourtney Cavin #define PON_PS_HOLD_RST_CTL2 0x5b
4168c581d5SCourtney Cavin #define PON_PS_HOLD_ENABLE BIT(7)
4268c581d5SCourtney Cavin #define PON_PS_HOLD_TYPE_MASK 0x0f
439e5afc84SShawn Guo #define PON_PS_HOLD_TYPE_WARM_RESET 1
4468c581d5SCourtney Cavin #define PON_PS_HOLD_TYPE_SHUTDOWN 4
4568c581d5SCourtney Cavin #define PON_PS_HOLD_TYPE_HARD_RESET 7
4668c581d5SCourtney Cavin
4768c581d5SCourtney Cavin #define PON_PULL_CTL 0x70
4868c581d5SCourtney Cavin #define PON_KPDPWR_PULL_UP BIT(1)
49955c594eSVinod Koul #define PON_RESIN_PULL_UP BIT(0)
5068c581d5SCourtney Cavin
5168c581d5SCourtney Cavin #define PON_DBC_CTL 0x71
52*8c9cce9cSCaleb Connolly #define PON_DBC_DELAY_MASK_GEN1 0x7
53*8c9cce9cSCaleb Connolly #define PON_DBC_DELAY_MASK_GEN2 0xf
54*8c9cce9cSCaleb Connolly #define PON_DBC_SHIFT_GEN1 6
55*8c9cce9cSCaleb Connolly #define PON_DBC_SHIFT_GEN2 14
5668c581d5SCourtney Cavin
572049a9e5SVinod Koul struct pm8941_data {
582049a9e5SVinod Koul unsigned int pull_up_bit;
592049a9e5SVinod Koul unsigned int status_bit;
602fcbda9aSDavid Collins bool supports_ps_hold_poff_config;
612fcbda9aSDavid Collins bool supports_debounce_config;
628ac8904bSAnjelique Melendez bool has_pon_pbs;
632fcbda9aSDavid Collins const char *name;
642fcbda9aSDavid Collins const char *phys;
652049a9e5SVinod Koul };
6668c581d5SCourtney Cavin
6768c581d5SCourtney Cavin struct pm8941_pwrkey {
6868c581d5SCourtney Cavin struct device *dev;
6968c581d5SCourtney Cavin int irq;
7068c581d5SCourtney Cavin u32 baseaddr;
718ac8904bSAnjelique Melendez u32 pon_pbs_baseaddr;
7268c581d5SCourtney Cavin struct regmap *regmap;
7368c581d5SCourtney Cavin struct input_dev *input;
7468c581d5SCourtney Cavin
7568c581d5SCourtney Cavin unsigned int revision;
760b65118eSDavid Collins unsigned int subtype;
7768c581d5SCourtney Cavin struct notifier_block reboot_notifier;
782049a9e5SVinod Koul
792049a9e5SVinod Koul u32 code;
800b65118eSDavid Collins u32 sw_debounce_time_us;
810b65118eSDavid Collins ktime_t sw_debounce_end_time;
82be8fc023SDavid Collins bool last_status;
832049a9e5SVinod Koul const struct pm8941_data *data;
8468c581d5SCourtney Cavin };
8568c581d5SCourtney Cavin
pm8941_reboot_notify(struct notifier_block * nb,unsigned long code,void * unused)8668c581d5SCourtney Cavin static int pm8941_reboot_notify(struct notifier_block *nb,
8768c581d5SCourtney Cavin unsigned long code, void *unused)
8868c581d5SCourtney Cavin {
8968c581d5SCourtney Cavin struct pm8941_pwrkey *pwrkey = container_of(nb, struct pm8941_pwrkey,
9068c581d5SCourtney Cavin reboot_notifier);
9168c581d5SCourtney Cavin unsigned int enable_reg;
9268c581d5SCourtney Cavin unsigned int reset_type;
9368c581d5SCourtney Cavin int error;
9468c581d5SCourtney Cavin
9568c581d5SCourtney Cavin /* PMICs with revision 0 have the enable bit in same register as ctrl */
9668c581d5SCourtney Cavin if (pwrkey->revision == 0)
9768c581d5SCourtney Cavin enable_reg = PON_PS_HOLD_RST_CTL;
9868c581d5SCourtney Cavin else
9968c581d5SCourtney Cavin enable_reg = PON_PS_HOLD_RST_CTL2;
10068c581d5SCourtney Cavin
10168c581d5SCourtney Cavin error = regmap_update_bits(pwrkey->regmap,
10268c581d5SCourtney Cavin pwrkey->baseaddr + enable_reg,
10368c581d5SCourtney Cavin PON_PS_HOLD_ENABLE,
10468c581d5SCourtney Cavin 0);
10568c581d5SCourtney Cavin if (error)
10668c581d5SCourtney Cavin dev_err(pwrkey->dev,
10768c581d5SCourtney Cavin "unable to clear ps hold reset enable: %d\n",
10868c581d5SCourtney Cavin error);
10968c581d5SCourtney Cavin
11068c581d5SCourtney Cavin /*
11168c581d5SCourtney Cavin * Updates of PON_PS_HOLD_ENABLE requires 3 sleep cycles between
11268c581d5SCourtney Cavin * writes.
11368c581d5SCourtney Cavin */
11468c581d5SCourtney Cavin usleep_range(100, 1000);
11568c581d5SCourtney Cavin
11668c581d5SCourtney Cavin switch (code) {
11768c581d5SCourtney Cavin case SYS_HALT:
11868c581d5SCourtney Cavin case SYS_POWER_OFF:
11968c581d5SCourtney Cavin reset_type = PON_PS_HOLD_TYPE_SHUTDOWN;
12068c581d5SCourtney Cavin break;
12168c581d5SCourtney Cavin case SYS_RESTART:
12268c581d5SCourtney Cavin default:
1239e5afc84SShawn Guo if (reboot_mode == REBOOT_WARM)
1249e5afc84SShawn Guo reset_type = PON_PS_HOLD_TYPE_WARM_RESET;
1259e5afc84SShawn Guo else
12668c581d5SCourtney Cavin reset_type = PON_PS_HOLD_TYPE_HARD_RESET;
12768c581d5SCourtney Cavin break;
128b9ab471bSJavier Martinez Canillas }
12968c581d5SCourtney Cavin
13068c581d5SCourtney Cavin error = regmap_update_bits(pwrkey->regmap,
13168c581d5SCourtney Cavin pwrkey->baseaddr + PON_PS_HOLD_RST_CTL,
13268c581d5SCourtney Cavin PON_PS_HOLD_TYPE_MASK,
13368c581d5SCourtney Cavin reset_type);
13468c581d5SCourtney Cavin if (error)
13568c581d5SCourtney Cavin dev_err(pwrkey->dev, "unable to set ps hold reset type: %d\n",
13668c581d5SCourtney Cavin error);
13768c581d5SCourtney Cavin
13868c581d5SCourtney Cavin error = regmap_update_bits(pwrkey->regmap,
13968c581d5SCourtney Cavin pwrkey->baseaddr + enable_reg,
14068c581d5SCourtney Cavin PON_PS_HOLD_ENABLE,
14168c581d5SCourtney Cavin PON_PS_HOLD_ENABLE);
14268c581d5SCourtney Cavin if (error)
14368c581d5SCourtney Cavin dev_err(pwrkey->dev, "unable to re-set enable: %d\n", error);
14468c581d5SCourtney Cavin
14568c581d5SCourtney Cavin return NOTIFY_DONE;
14668c581d5SCourtney Cavin }
14768c581d5SCourtney Cavin
pm8941_pwrkey_irq(int irq,void * _data)14868c581d5SCourtney Cavin static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
14968c581d5SCourtney Cavin {
15068c581d5SCourtney Cavin struct pm8941_pwrkey *pwrkey = _data;
15168c581d5SCourtney Cavin unsigned int sts;
1520b65118eSDavid Collins int err;
15368c581d5SCourtney Cavin
1540b65118eSDavid Collins if (pwrkey->sw_debounce_time_us) {
1550b65118eSDavid Collins if (ktime_before(ktime_get(), pwrkey->sw_debounce_end_time)) {
1560b65118eSDavid Collins dev_dbg(pwrkey->dev,
1570b65118eSDavid Collins "ignoring key event received before debounce end %llu us\n",
1580b65118eSDavid Collins pwrkey->sw_debounce_end_time);
1590b65118eSDavid Collins return IRQ_HANDLED;
1600b65118eSDavid Collins }
1610b65118eSDavid Collins }
1620b65118eSDavid Collins
1630b65118eSDavid Collins err = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_RT_STS, &sts);
1640b65118eSDavid Collins if (err)
16568c581d5SCourtney Cavin return IRQ_HANDLED;
16668c581d5SCourtney Cavin
1670b65118eSDavid Collins sts &= pwrkey->data->status_bit;
1680b65118eSDavid Collins
1690b65118eSDavid Collins if (pwrkey->sw_debounce_time_us && !sts)
1700b65118eSDavid Collins pwrkey->sw_debounce_end_time = ktime_add_us(ktime_get(),
1710b65118eSDavid Collins pwrkey->sw_debounce_time_us);
1720b65118eSDavid Collins
173be8fc023SDavid Collins /*
174be8fc023SDavid Collins * Simulate a press event in case a release event occurred without a
175be8fc023SDavid Collins * corresponding press event.
176be8fc023SDavid Collins */
177be8fc023SDavid Collins if (!pwrkey->last_status && !sts) {
178be8fc023SDavid Collins input_report_key(pwrkey->input, pwrkey->code, 1);
179be8fc023SDavid Collins input_sync(pwrkey->input);
180be8fc023SDavid Collins }
181be8fc023SDavid Collins pwrkey->last_status = sts;
182be8fc023SDavid Collins
1830b65118eSDavid Collins input_report_key(pwrkey->input, pwrkey->code, sts);
18468c581d5SCourtney Cavin input_sync(pwrkey->input);
18568c581d5SCourtney Cavin
18668c581d5SCourtney Cavin return IRQ_HANDLED;
18768c581d5SCourtney Cavin }
18868c581d5SCourtney Cavin
pm8941_pwrkey_sw_debounce_init(struct pm8941_pwrkey * pwrkey)1890b65118eSDavid Collins static int pm8941_pwrkey_sw_debounce_init(struct pm8941_pwrkey *pwrkey)
1900b65118eSDavid Collins {
1910b65118eSDavid Collins unsigned int val, addr, mask;
1920b65118eSDavid Collins int error;
1930b65118eSDavid Collins
1940b65118eSDavid Collins if (pwrkey->data->has_pon_pbs && !pwrkey->pon_pbs_baseaddr) {
1950b65118eSDavid Collins dev_err(pwrkey->dev,
1960b65118eSDavid Collins "PON_PBS address missing, can't read HW debounce time\n");
1970b65118eSDavid Collins return 0;
1980b65118eSDavid Collins }
1990b65118eSDavid Collins
2000b65118eSDavid Collins if (pwrkey->pon_pbs_baseaddr)
2010b65118eSDavid Collins addr = pwrkey->pon_pbs_baseaddr + PON_DBC_CTL;
2020b65118eSDavid Collins else
2030b65118eSDavid Collins addr = pwrkey->baseaddr + PON_DBC_CTL;
2040b65118eSDavid Collins error = regmap_read(pwrkey->regmap, addr, &val);
2050b65118eSDavid Collins if (error)
2060b65118eSDavid Collins return error;
2070b65118eSDavid Collins
2080b65118eSDavid Collins if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY)
2090b65118eSDavid Collins mask = 0xf;
2100b65118eSDavid Collins else
2110b65118eSDavid Collins mask = 0x7;
2120b65118eSDavid Collins
2130b65118eSDavid Collins pwrkey->sw_debounce_time_us =
2140b65118eSDavid Collins 2 * USEC_PER_SEC / (1 << (mask - (val & mask)));
2150b65118eSDavid Collins
2160b65118eSDavid Collins dev_dbg(pwrkey->dev, "SW debounce time = %u us\n",
2170b65118eSDavid Collins pwrkey->sw_debounce_time_us);
2180b65118eSDavid Collins
2190b65118eSDavid Collins return 0;
2200b65118eSDavid Collins }
2210b65118eSDavid Collins
pm8941_pwrkey_suspend(struct device * dev)222209cf27dSJonathan Cameron static int pm8941_pwrkey_suspend(struct device *dev)
22368c581d5SCourtney Cavin {
22468c581d5SCourtney Cavin struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
22568c581d5SCourtney Cavin
22668c581d5SCourtney Cavin if (device_may_wakeup(dev))
22768c581d5SCourtney Cavin enable_irq_wake(pwrkey->irq);
22868c581d5SCourtney Cavin
22968c581d5SCourtney Cavin return 0;
23068c581d5SCourtney Cavin }
23168c581d5SCourtney Cavin
pm8941_pwrkey_resume(struct device * dev)232209cf27dSJonathan Cameron static int pm8941_pwrkey_resume(struct device *dev)
23368c581d5SCourtney Cavin {
23468c581d5SCourtney Cavin struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
23568c581d5SCourtney Cavin
23668c581d5SCourtney Cavin if (device_may_wakeup(dev))
23768c581d5SCourtney Cavin disable_irq_wake(pwrkey->irq);
23868c581d5SCourtney Cavin
23968c581d5SCourtney Cavin return 0;
24068c581d5SCourtney Cavin }
24168c581d5SCourtney Cavin
242209cf27dSJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops,
24368c581d5SCourtney Cavin pm8941_pwrkey_suspend, pm8941_pwrkey_resume);
24468c581d5SCourtney Cavin
pm8941_pwrkey_probe(struct platform_device * pdev)24568c581d5SCourtney Cavin static int pm8941_pwrkey_probe(struct platform_device *pdev)
24668c581d5SCourtney Cavin {
24768c581d5SCourtney Cavin struct pm8941_pwrkey *pwrkey;
24868c581d5SCourtney Cavin bool pull_up;
2492049a9e5SVinod Koul struct device *parent;
2508ac8904bSAnjelique Melendez struct device_node *regmap_node;
2518ac8904bSAnjelique Melendez const __be32 *addr;
252*8c9cce9cSCaleb Connolly u32 req_delay, mask, delay_shift;
25368c581d5SCourtney Cavin int error;
25468c581d5SCourtney Cavin
25568c581d5SCourtney Cavin if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay))
25668c581d5SCourtney Cavin req_delay = 15625;
25768c581d5SCourtney Cavin
25868c581d5SCourtney Cavin if (req_delay > 2000000 || req_delay == 0) {
25968c581d5SCourtney Cavin dev_err(&pdev->dev, "invalid debounce time: %u\n", req_delay);
26068c581d5SCourtney Cavin return -EINVAL;
26168c581d5SCourtney Cavin }
26268c581d5SCourtney Cavin
26368c581d5SCourtney Cavin pull_up = of_property_read_bool(pdev->dev.of_node, "bias-pull-up");
26468c581d5SCourtney Cavin
26568c581d5SCourtney Cavin pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL);
26668c581d5SCourtney Cavin if (!pwrkey)
26768c581d5SCourtney Cavin return -ENOMEM;
26868c581d5SCourtney Cavin
26968c581d5SCourtney Cavin pwrkey->dev = &pdev->dev;
2702049a9e5SVinod Koul pwrkey->data = of_device_get_match_data(&pdev->dev);
27168c581d5SCourtney Cavin
2722049a9e5SVinod Koul parent = pdev->dev.parent;
2738ac8904bSAnjelique Melendez regmap_node = pdev->dev.of_node;
2742049a9e5SVinod Koul pwrkey->regmap = dev_get_regmap(parent, NULL);
2752049a9e5SVinod Koul if (!pwrkey->regmap) {
2768ac8904bSAnjelique Melendez regmap_node = parent->of_node;
2772049a9e5SVinod Koul /*
2782049a9e5SVinod Koul * We failed to get regmap for parent. Let's see if we are
2792049a9e5SVinod Koul * a child of pon node and read regmap and reg from its
2802049a9e5SVinod Koul * parent.
2812049a9e5SVinod Koul */
2822049a9e5SVinod Koul pwrkey->regmap = dev_get_regmap(parent->parent, NULL);
28368c581d5SCourtney Cavin if (!pwrkey->regmap) {
28468c581d5SCourtney Cavin dev_err(&pdev->dev, "failed to locate regmap\n");
28568c581d5SCourtney Cavin return -ENODEV;
28668c581d5SCourtney Cavin }
2872049a9e5SVinod Koul }
2888ac8904bSAnjelique Melendez
2898ac8904bSAnjelique Melendez addr = of_get_address(regmap_node, 0, NULL, NULL);
2908ac8904bSAnjelique Melendez if (!addr) {
2918ac8904bSAnjelique Melendez dev_err(&pdev->dev, "reg property missing\n");
2928ac8904bSAnjelique Melendez return -EINVAL;
2938ac8904bSAnjelique Melendez }
2948ac8904bSAnjelique Melendez pwrkey->baseaddr = be32_to_cpup(addr);
2958ac8904bSAnjelique Melendez
2968ac8904bSAnjelique Melendez if (pwrkey->data->has_pon_pbs) {
2978ac8904bSAnjelique Melendez /* PON_PBS base address is optional */
2988ac8904bSAnjelique Melendez addr = of_get_address(regmap_node, 1, NULL, NULL);
2998ac8904bSAnjelique Melendez if (addr)
3008ac8904bSAnjelique Melendez pwrkey->pon_pbs_baseaddr = be32_to_cpup(addr);
3018ac8904bSAnjelique Melendez }
3022049a9e5SVinod Koul
30368c581d5SCourtney Cavin pwrkey->irq = platform_get_irq(pdev, 0);
3040bec8b7eSStephen Boyd if (pwrkey->irq < 0)
30568c581d5SCourtney Cavin return pwrkey->irq;
30668c581d5SCourtney Cavin
30768c581d5SCourtney Cavin error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
30868c581d5SCourtney Cavin &pwrkey->revision);
30968c581d5SCourtney Cavin if (error) {
3102e7cfec0SAnjelique Melendez dev_err(&pdev->dev, "failed to read revision: %d\n", error);
31168c581d5SCourtney Cavin return error;
31268c581d5SCourtney Cavin }
31368c581d5SCourtney Cavin
3140b65118eSDavid Collins error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_SUBTYPE,
3150b65118eSDavid Collins &pwrkey->subtype);
3160b65118eSDavid Collins if (error) {
3170b65118eSDavid Collins dev_err(&pdev->dev, "failed to read subtype: %d\n", error);
3180b65118eSDavid Collins return error;
3190b65118eSDavid Collins }
3200b65118eSDavid Collins
3212049a9e5SVinod Koul error = of_property_read_u32(pdev->dev.of_node, "linux,code",
3222049a9e5SVinod Koul &pwrkey->code);
3232049a9e5SVinod Koul if (error) {
3242049a9e5SVinod Koul dev_dbg(&pdev->dev,
3252049a9e5SVinod Koul "no linux,code assuming power (%d)\n", error);
3262049a9e5SVinod Koul pwrkey->code = KEY_POWER;
3272049a9e5SVinod Koul }
3282049a9e5SVinod Koul
32968c581d5SCourtney Cavin pwrkey->input = devm_input_allocate_device(&pdev->dev);
33068c581d5SCourtney Cavin if (!pwrkey->input) {
33168c581d5SCourtney Cavin dev_dbg(&pdev->dev, "unable to allocate input device\n");
33268c581d5SCourtney Cavin return -ENOMEM;
33368c581d5SCourtney Cavin }
33468c581d5SCourtney Cavin
3352049a9e5SVinod Koul input_set_capability(pwrkey->input, EV_KEY, pwrkey->code);
33668c581d5SCourtney Cavin
3372fcbda9aSDavid Collins pwrkey->input->name = pwrkey->data->name;
3382fcbda9aSDavid Collins pwrkey->input->phys = pwrkey->data->phys;
33968c581d5SCourtney Cavin
3402fcbda9aSDavid Collins if (pwrkey->data->supports_debounce_config) {
341*8c9cce9cSCaleb Connolly if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY) {
342*8c9cce9cSCaleb Connolly mask = PON_DBC_DELAY_MASK_GEN2;
343*8c9cce9cSCaleb Connolly delay_shift = PON_DBC_SHIFT_GEN2;
344*8c9cce9cSCaleb Connolly } else {
345*8c9cce9cSCaleb Connolly mask = PON_DBC_DELAY_MASK_GEN1;
346*8c9cce9cSCaleb Connolly delay_shift = PON_DBC_SHIFT_GEN1;
347*8c9cce9cSCaleb Connolly }
348*8c9cce9cSCaleb Connolly
349*8c9cce9cSCaleb Connolly req_delay = (req_delay << delay_shift) / USEC_PER_SEC;
35068c581d5SCourtney Cavin req_delay = ilog2(req_delay);
35168c581d5SCourtney Cavin
35268c581d5SCourtney Cavin error = regmap_update_bits(pwrkey->regmap,
35368c581d5SCourtney Cavin pwrkey->baseaddr + PON_DBC_CTL,
354*8c9cce9cSCaleb Connolly mask,
35568c581d5SCourtney Cavin req_delay);
35668c581d5SCourtney Cavin if (error) {
3572fcbda9aSDavid Collins dev_err(&pdev->dev, "failed to set debounce: %d\n",
3582fcbda9aSDavid Collins error);
35968c581d5SCourtney Cavin return error;
36068c581d5SCourtney Cavin }
3612fcbda9aSDavid Collins }
36268c581d5SCourtney Cavin
3630b65118eSDavid Collins error = pm8941_pwrkey_sw_debounce_init(pwrkey);
3640b65118eSDavid Collins if (error)
3650b65118eSDavid Collins return error;
3660b65118eSDavid Collins
3672fcbda9aSDavid Collins if (pwrkey->data->pull_up_bit) {
36868c581d5SCourtney Cavin error = regmap_update_bits(pwrkey->regmap,
36968c581d5SCourtney Cavin pwrkey->baseaddr + PON_PULL_CTL,
3702049a9e5SVinod Koul pwrkey->data->pull_up_bit,
3712fcbda9aSDavid Collins pull_up ? pwrkey->data->pull_up_bit :
3722fcbda9aSDavid Collins 0);
37368c581d5SCourtney Cavin if (error) {
37468c581d5SCourtney Cavin dev_err(&pdev->dev, "failed to set pull: %d\n", error);
37568c581d5SCourtney Cavin return error;
37668c581d5SCourtney Cavin }
3772fcbda9aSDavid Collins }
37868c581d5SCourtney Cavin
37968c581d5SCourtney Cavin error = devm_request_threaded_irq(&pdev->dev, pwrkey->irq,
38068c581d5SCourtney Cavin NULL, pm8941_pwrkey_irq,
38168c581d5SCourtney Cavin IRQF_ONESHOT,
3822fcbda9aSDavid Collins pwrkey->data->name, pwrkey);
38368c581d5SCourtney Cavin if (error) {
38468c581d5SCourtney Cavin dev_err(&pdev->dev, "failed requesting IRQ: %d\n", error);
38568c581d5SCourtney Cavin return error;
38668c581d5SCourtney Cavin }
38768c581d5SCourtney Cavin
38868c581d5SCourtney Cavin error = input_register_device(pwrkey->input);
38968c581d5SCourtney Cavin if (error) {
39068c581d5SCourtney Cavin dev_err(&pdev->dev, "failed to register input device: %d\n",
39168c581d5SCourtney Cavin error);
39268c581d5SCourtney Cavin return error;
39368c581d5SCourtney Cavin }
39468c581d5SCourtney Cavin
3952fcbda9aSDavid Collins if (pwrkey->data->supports_ps_hold_poff_config) {
3965af9f79bSDmitry Torokhov pwrkey->reboot_notifier.notifier_call = pm8941_reboot_notify;
39768c581d5SCourtney Cavin error = register_reboot_notifier(&pwrkey->reboot_notifier);
39868c581d5SCourtney Cavin if (error) {
39968c581d5SCourtney Cavin dev_err(&pdev->dev, "failed to register reboot notifier: %d\n",
40068c581d5SCourtney Cavin error);
40168c581d5SCourtney Cavin return error;
40268c581d5SCourtney Cavin }
4032fcbda9aSDavid Collins }
40468c581d5SCourtney Cavin
40568c581d5SCourtney Cavin platform_set_drvdata(pdev, pwrkey);
40668c581d5SCourtney Cavin device_init_wakeup(&pdev->dev, 1);
40768c581d5SCourtney Cavin
40868c581d5SCourtney Cavin return 0;
40968c581d5SCourtney Cavin }
41068c581d5SCourtney Cavin
pm8941_pwrkey_remove(struct platform_device * pdev)41168c581d5SCourtney Cavin static int pm8941_pwrkey_remove(struct platform_device *pdev)
41268c581d5SCourtney Cavin {
41368c581d5SCourtney Cavin struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev);
41468c581d5SCourtney Cavin
4152fcbda9aSDavid Collins if (pwrkey->data->supports_ps_hold_poff_config)
41668c581d5SCourtney Cavin unregister_reboot_notifier(&pwrkey->reboot_notifier);
41768c581d5SCourtney Cavin
41868c581d5SCourtney Cavin return 0;
41968c581d5SCourtney Cavin }
42068c581d5SCourtney Cavin
4212049a9e5SVinod Koul static const struct pm8941_data pwrkey_data = {
4222049a9e5SVinod Koul .pull_up_bit = PON_KPDPWR_PULL_UP,
4232049a9e5SVinod Koul .status_bit = PON_KPDPWR_N_SET,
4242fcbda9aSDavid Collins .name = "pm8941_pwrkey",
4252fcbda9aSDavid Collins .phys = "pm8941_pwrkey/input0",
4262fcbda9aSDavid Collins .supports_ps_hold_poff_config = true,
4272fcbda9aSDavid Collins .supports_debounce_config = true,
4288ac8904bSAnjelique Melendez .has_pon_pbs = false,
4292049a9e5SVinod Koul };
4302049a9e5SVinod Koul
431955c594eSVinod Koul static const struct pm8941_data resin_data = {
432955c594eSVinod Koul .pull_up_bit = PON_RESIN_PULL_UP,
433955c594eSVinod Koul .status_bit = PON_RESIN_N_SET,
4342fcbda9aSDavid Collins .name = "pm8941_resin",
4352fcbda9aSDavid Collins .phys = "pm8941_resin/input0",
4362fcbda9aSDavid Collins .supports_ps_hold_poff_config = true,
4372fcbda9aSDavid Collins .supports_debounce_config = true,
4388ac8904bSAnjelique Melendez .has_pon_pbs = false,
4392fcbda9aSDavid Collins };
4402fcbda9aSDavid Collins
4412fcbda9aSDavid Collins static const struct pm8941_data pon_gen3_pwrkey_data = {
4422fcbda9aSDavid Collins .status_bit = PON_GEN3_KPDPWR_N_SET,
4432fcbda9aSDavid Collins .name = "pmic_pwrkey",
4442fcbda9aSDavid Collins .phys = "pmic_pwrkey/input0",
4452fcbda9aSDavid Collins .supports_ps_hold_poff_config = false,
4462fcbda9aSDavid Collins .supports_debounce_config = false,
4478ac8904bSAnjelique Melendez .has_pon_pbs = true,
4482fcbda9aSDavid Collins };
4492fcbda9aSDavid Collins
4502fcbda9aSDavid Collins static const struct pm8941_data pon_gen3_resin_data = {
4512fcbda9aSDavid Collins .status_bit = PON_GEN3_RESIN_N_SET,
4522fcbda9aSDavid Collins .name = "pmic_resin",
4532fcbda9aSDavid Collins .phys = "pmic_resin/input0",
4542fcbda9aSDavid Collins .supports_ps_hold_poff_config = false,
4552fcbda9aSDavid Collins .supports_debounce_config = false,
4568ac8904bSAnjelique Melendez .has_pon_pbs = true,
457955c594eSVinod Koul };
458955c594eSVinod Koul
45968c581d5SCourtney Cavin static const struct of_device_id pm8941_pwr_key_id_table[] = {
4602049a9e5SVinod Koul { .compatible = "qcom,pm8941-pwrkey", .data = &pwrkey_data },
461955c594eSVinod Koul { .compatible = "qcom,pm8941-resin", .data = &resin_data },
4622fcbda9aSDavid Collins { .compatible = "qcom,pmk8350-pwrkey", .data = &pon_gen3_pwrkey_data },
4632fcbda9aSDavid Collins { .compatible = "qcom,pmk8350-resin", .data = &pon_gen3_resin_data },
46468c581d5SCourtney Cavin { }
46568c581d5SCourtney Cavin };
46668c581d5SCourtney Cavin MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table);
46768c581d5SCourtney Cavin
46868c581d5SCourtney Cavin static struct platform_driver pm8941_pwrkey_driver = {
46968c581d5SCourtney Cavin .probe = pm8941_pwrkey_probe,
47068c581d5SCourtney Cavin .remove = pm8941_pwrkey_remove,
47168c581d5SCourtney Cavin .driver = {
47268c581d5SCourtney Cavin .name = "pm8941-pwrkey",
473209cf27dSJonathan Cameron .pm = pm_sleep_ptr(&pm8941_pwr_key_pm_ops),
47468c581d5SCourtney Cavin .of_match_table = of_match_ptr(pm8941_pwr_key_id_table),
47568c581d5SCourtney Cavin },
47668c581d5SCourtney Cavin };
47768c581d5SCourtney Cavin module_platform_driver(pm8941_pwrkey_driver);
47868c581d5SCourtney Cavin
47968c581d5SCourtney Cavin MODULE_DESCRIPTION("PM8941 Power Key driver");
48068c581d5SCourtney Cavin MODULE_LICENSE("GPL v2");
481