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