1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Driver for TPS65219 Push Button |
4 | // |
5 | // Copyright (C) 2022 BayLibre Incorporated - https://www.baylibre.com/ |
6 | |
7 | #include <linux/init.h> |
8 | #include <linux/input.h> |
9 | #include <linux/interrupt.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/mfd/tps65219.h> |
12 | #include <linux/module.h> |
13 | #include <linux/of.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/regmap.h> |
16 | #include <linux/slab.h> |
17 | |
18 | struct tps65219_pwrbutton { |
19 | struct device *dev; |
20 | struct input_dev *idev; |
21 | char phys[32]; |
22 | }; |
23 | |
24 | static irqreturn_t tps65219_pb_push_irq(int irq, void *_pwr) |
25 | { |
26 | struct tps65219_pwrbutton *pwr = _pwr; |
27 | |
28 | input_report_key(dev: pwr->idev, KEY_POWER, value: 1); |
29 | pm_wakeup_event(dev: pwr->dev, msec: 0); |
30 | input_sync(dev: pwr->idev); |
31 | |
32 | return IRQ_HANDLED; |
33 | } |
34 | |
35 | static irqreturn_t tps65219_pb_release_irq(int irq, void *_pwr) |
36 | { |
37 | struct tps65219_pwrbutton *pwr = _pwr; |
38 | |
39 | input_report_key(dev: pwr->idev, KEY_POWER, value: 0); |
40 | input_sync(dev: pwr->idev); |
41 | |
42 | return IRQ_HANDLED; |
43 | } |
44 | |
45 | static int tps65219_pb_probe(struct platform_device *pdev) |
46 | { |
47 | struct tps65219 *tps = dev_get_drvdata(dev: pdev->dev.parent); |
48 | struct device *dev = &pdev->dev; |
49 | struct tps65219_pwrbutton *pwr; |
50 | struct input_dev *idev; |
51 | int error; |
52 | int push_irq; |
53 | int release_irq; |
54 | |
55 | pwr = devm_kzalloc(dev, size: sizeof(*pwr), GFP_KERNEL); |
56 | if (!pwr) |
57 | return -ENOMEM; |
58 | |
59 | idev = devm_input_allocate_device(dev); |
60 | if (!idev) |
61 | return -ENOMEM; |
62 | |
63 | idev->name = pdev->name; |
64 | snprintf(buf: pwr->phys, size: sizeof(pwr->phys), fmt: "%s/input0" , |
65 | pdev->name); |
66 | idev->phys = pwr->phys; |
67 | idev->id.bustype = BUS_I2C; |
68 | |
69 | input_set_capability(dev: idev, EV_KEY, KEY_POWER); |
70 | |
71 | pwr->dev = dev; |
72 | pwr->idev = idev; |
73 | device_init_wakeup(dev, enable: true); |
74 | |
75 | push_irq = platform_get_irq(pdev, 0); |
76 | if (push_irq < 0) |
77 | return -EINVAL; |
78 | |
79 | release_irq = platform_get_irq(pdev, 1); |
80 | if (release_irq < 0) |
81 | return -EINVAL; |
82 | |
83 | error = devm_request_threaded_irq(dev, irq: push_irq, NULL, |
84 | thread_fn: tps65219_pb_push_irq, |
85 | IRQF_ONESHOT, |
86 | devname: dev->init_name, dev_id: pwr); |
87 | if (error) { |
88 | dev_err(dev, "failed to request push IRQ #%d: %d\n" , push_irq, |
89 | error); |
90 | return error; |
91 | } |
92 | |
93 | error = devm_request_threaded_irq(dev, irq: release_irq, NULL, |
94 | thread_fn: tps65219_pb_release_irq, |
95 | IRQF_ONESHOT, |
96 | devname: dev->init_name, dev_id: pwr); |
97 | if (error) { |
98 | dev_err(dev, "failed to request release IRQ #%d: %d\n" , |
99 | release_irq, error); |
100 | return error; |
101 | } |
102 | |
103 | error = input_register_device(idev); |
104 | if (error) { |
105 | dev_err(dev, "Can't register power button: %d\n" , error); |
106 | return error; |
107 | } |
108 | |
109 | /* Enable interrupts for the pushbutton */ |
110 | regmap_clear_bits(map: tps->regmap, TPS65219_REG_MASK_CONFIG, |
111 | TPS65219_REG_MASK_INT_FOR_PB_MASK); |
112 | |
113 | /* Set PB/EN/VSENSE pin to be a pushbutton */ |
114 | regmap_update_bits(map: tps->regmap, TPS65219_REG_MFP_2_CONFIG, |
115 | TPS65219_MFP_2_EN_PB_VSENSE_MASK, TPS65219_MFP_2_PB); |
116 | |
117 | return 0; |
118 | } |
119 | |
120 | static void tps65219_pb_remove(struct platform_device *pdev) |
121 | { |
122 | struct tps65219 *tps = dev_get_drvdata(dev: pdev->dev.parent); |
123 | int ret; |
124 | |
125 | /* Disable interrupt for the pushbutton */ |
126 | ret = regmap_set_bits(map: tps->regmap, TPS65219_REG_MASK_CONFIG, |
127 | TPS65219_REG_MASK_INT_FOR_PB_MASK); |
128 | if (ret) |
129 | dev_warn(&pdev->dev, "Failed to disable irq (%pe)\n" , ERR_PTR(ret)); |
130 | } |
131 | |
132 | static const struct platform_device_id tps65219_pwrbtn_id_table[] = { |
133 | { "tps65219-pwrbutton" , }, |
134 | { /* sentinel */ } |
135 | }; |
136 | MODULE_DEVICE_TABLE(platform, tps65219_pwrbtn_id_table); |
137 | |
138 | static struct platform_driver tps65219_pb_driver = { |
139 | .probe = tps65219_pb_probe, |
140 | .remove_new = tps65219_pb_remove, |
141 | .driver = { |
142 | .name = "tps65219_pwrbutton" , |
143 | }, |
144 | .id_table = tps65219_pwrbtn_id_table, |
145 | }; |
146 | module_platform_driver(tps65219_pb_driver); |
147 | |
148 | MODULE_DESCRIPTION("TPS65219 Power Button" ); |
149 | MODULE_LICENSE("GPL" ); |
150 | MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com" ); |
151 | |