xref: /linux/drivers/input/misc/palmas-pwrbutton.c (revision 0e9b70c1e3623fa110fb6be553e644524228ef60)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Texas Instruments' Palmas Power Button Input Driver
4  *
5  * Copyright (C) 2012-2014 Texas Instruments Incorporated - http://www.ti.com/
6  *	Girish S Ghongdemath
7  *	Nishanth Menon
8  */
9 
10 #include <linux/bitfield.h>
11 #include <linux/init.h>
12 #include <linux/input.h>
13 #include <linux/interrupt.h>
14 #include <linux/kernel.h>
15 #include <linux/mfd/palmas.h>
16 #include <linux/module.h>
17 #include <linux/of.h>
18 #include <linux/platform_device.h>
19 #include <linux/slab.h>
20 
21 #define PALMAS_LPK_TIME_MASK		0x0c
22 #define PALMAS_PWRON_DEBOUNCE_MASK	0x03
23 #define PALMAS_PWR_KEY_Q_TIME_MS	20
24 
25 /**
26  * struct palmas_pwron - Palmas power on data
27  * @palmas:		pointer to palmas device
28  * @input_dev:		pointer to input device
29  * @input_work:		work for detecting release of key
30  * @irq:		irq that we are hooked on to
31  */
32 struct palmas_pwron {
33 	struct palmas *palmas;
34 	struct input_dev *input_dev;
35 	struct delayed_work input_work;
36 	int irq;
37 };
38 
39 /**
40  * struct palmas_pwron_config - configuration of palmas power on
41  * @long_press_time_val:	value for long press h/w shutdown event
42  * @pwron_debounce_val:		value for debounce of power button
43  */
44 struct palmas_pwron_config {
45 	u8 long_press_time_val;
46 	u8 pwron_debounce_val;
47 };
48 
49 /**
50  * palmas_power_button_work() - Detects the button release event
51  * @work:	work item to detect button release
52  */
53 static void palmas_power_button_work(struct work_struct *work)
54 {
55 	struct palmas_pwron *pwron = container_of(work,
56 						  struct palmas_pwron,
57 						  input_work.work);
58 	struct input_dev *input_dev = pwron->input_dev;
59 	unsigned int reg;
60 	int error;
61 
62 	error = palmas_read(pwron->palmas, PALMAS_INTERRUPT_BASE,
63 			    PALMAS_INT1_LINE_STATE, &reg);
64 	if (error) {
65 		dev_err(input_dev->dev.parent,
66 			"Cannot read palmas PWRON status: %d\n", error);
67 	} else if (reg & BIT(1)) {
68 		/* The button is released, report event. */
69 		input_report_key(input_dev, KEY_POWER, 0);
70 		input_sync(input_dev);
71 	} else {
72 		/* The button is still depressed, keep checking. */
73 		schedule_delayed_work(&pwron->input_work,
74 				msecs_to_jiffies(PALMAS_PWR_KEY_Q_TIME_MS));
75 	}
76 }
77 
78 /**
79  * pwron_irq() - button press isr
80  * @irq:		irq
81  * @palmas_pwron:	pwron struct
82  *
83  * Return: IRQ_HANDLED
84  */
85 static irqreturn_t pwron_irq(int irq, void *palmas_pwron)
86 {
87 	struct palmas_pwron *pwron = palmas_pwron;
88 	struct input_dev *input_dev = pwron->input_dev;
89 
90 	input_report_key(input_dev, KEY_POWER, 1);
91 	pm_wakeup_event(input_dev->dev.parent, 0);
92 	input_sync(input_dev);
93 
94 	mod_delayed_work(system_wq, &pwron->input_work,
95 			 msecs_to_jiffies(PALMAS_PWR_KEY_Q_TIME_MS));
96 
97 	return IRQ_HANDLED;
98 }
99 
100 /**
101  * palmas_pwron_params_ofinit() - device tree parameter parser
102  * @dev:	palmas button device
103  * @config:	configuration params that this fills up
104  */
105 static void palmas_pwron_params_ofinit(struct device *dev,
106 				       struct palmas_pwron_config *config)
107 {
108 	struct device_node *np;
109 	u32 val;
110 	int i, error;
111 	static const u8 lpk_times[] = { 6, 8, 10, 12 };
112 	static const int pwr_on_deb_ms[] = { 15, 100, 500, 1000 };
113 
114 	memset(config, 0, sizeof(*config));
115 
116 	/* Default config parameters */
117 	config->long_press_time_val = ARRAY_SIZE(lpk_times) - 1;
118 
119 	np = dev->of_node;
120 	if (!np)
121 		return;
122 
123 	error = of_property_read_u32(np, "ti,palmas-long-press-seconds", &val);
124 	if (!error) {
125 		for (i = 0; i < ARRAY_SIZE(lpk_times); i++) {
126 			if (val <= lpk_times[i]) {
127 				config->long_press_time_val = i;
128 				break;
129 			}
130 		}
131 	}
132 
133 	error = of_property_read_u32(np,
134 				     "ti,palmas-pwron-debounce-milli-seconds",
135 				     &val);
136 	if (!error) {
137 		for (i = 0; i < ARRAY_SIZE(pwr_on_deb_ms); i++) {
138 			if (val <= pwr_on_deb_ms[i]) {
139 				config->pwron_debounce_val = i;
140 				break;
141 			}
142 		}
143 	}
144 
145 	dev_info(dev, "h/w controlled shutdown duration=%d seconds\n",
146 		 lpk_times[config->long_press_time_val]);
147 }
148 
149 /**
150  * palmas_pwron_probe() - probe
151  * @pdev:	platform device for the button
152  *
153  * Return: 0 for successful probe else appropriate error
154  */
155 static int palmas_pwron_probe(struct platform_device *pdev)
156 {
157 	struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
158 	struct device *dev = &pdev->dev;
159 	struct input_dev *input_dev;
160 	struct palmas_pwron *pwron;
161 	struct palmas_pwron_config config;
162 	int val;
163 	int error;
164 
165 	palmas_pwron_params_ofinit(dev, &config);
166 
167 	pwron = kzalloc(sizeof(*pwron), GFP_KERNEL);
168 	if (!pwron)
169 		return -ENOMEM;
170 
171 	input_dev = input_allocate_device();
172 	if (!input_dev) {
173 		dev_err(dev, "Can't allocate power button\n");
174 		error = -ENOMEM;
175 		goto err_free_mem;
176 	}
177 
178 	input_dev->name = "palmas_pwron";
179 	input_dev->phys = "palmas_pwron/input0";
180 	input_dev->dev.parent = dev;
181 
182 	input_set_capability(input_dev, EV_KEY, KEY_POWER);
183 
184 	/*
185 	 * Setup default hardware shutdown option (long key press)
186 	 * and debounce.
187 	 */
188 	val = FIELD_PREP(PALMAS_LPK_TIME_MASK, config.long_press_time_val) |
189 	      FIELD_PREP(PALMAS_PWRON_DEBOUNCE_MASK, config.pwron_debounce_val);
190 	error = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
191 				   PALMAS_LONG_PRESS_KEY,
192 				   PALMAS_LPK_TIME_MASK |
193 					PALMAS_PWRON_DEBOUNCE_MASK,
194 				   val);
195 	if (error) {
196 		dev_err(dev, "LONG_PRESS_KEY_UPDATE failed: %d\n", error);
197 		goto err_free_input;
198 	}
199 
200 	pwron->palmas = palmas;
201 	pwron->input_dev = input_dev;
202 
203 	INIT_DELAYED_WORK(&pwron->input_work, palmas_power_button_work);
204 
205 	pwron->irq = platform_get_irq(pdev, 0);
206 	if (pwron->irq < 0) {
207 		error = pwron->irq;
208 		goto err_free_input;
209 	}
210 
211 	error = request_threaded_irq(pwron->irq, NULL, pwron_irq,
212 				     IRQF_TRIGGER_HIGH |
213 					IRQF_TRIGGER_LOW |
214 					IRQF_ONESHOT,
215 				     dev_name(dev), pwron);
216 	if (error) {
217 		dev_err(dev, "Can't get IRQ for pwron: %d\n", error);
218 		goto err_free_input;
219 	}
220 
221 	error = input_register_device(input_dev);
222 	if (error) {
223 		dev_err(dev, "Can't register power button: %d\n", error);
224 		goto err_free_irq;
225 	}
226 
227 	platform_set_drvdata(pdev, pwron);
228 	device_init_wakeup(dev, true);
229 
230 	return 0;
231 
232 err_free_irq:
233 	cancel_delayed_work_sync(&pwron->input_work);
234 	free_irq(pwron->irq, pwron);
235 err_free_input:
236 	input_free_device(input_dev);
237 err_free_mem:
238 	kfree(pwron);
239 	return error;
240 }
241 
242 /**
243  * palmas_pwron_remove() - Cleanup on removal
244  * @pdev:	platform device for the button
245  *
246  * Return: 0
247  */
248 static int palmas_pwron_remove(struct platform_device *pdev)
249 {
250 	struct palmas_pwron *pwron = platform_get_drvdata(pdev);
251 
252 	free_irq(pwron->irq, pwron);
253 	cancel_delayed_work_sync(&pwron->input_work);
254 
255 	input_unregister_device(pwron->input_dev);
256 	kfree(pwron);
257 
258 	return 0;
259 }
260 
261 /**
262  * palmas_pwron_suspend() - suspend handler
263  * @dev:	power button device
264  *
265  * Cancel all pending work items for the power button, setup irq for wakeup
266  *
267  * Return: 0
268  */
269 static int palmas_pwron_suspend(struct device *dev)
270 {
271 	struct platform_device *pdev = to_platform_device(dev);
272 	struct palmas_pwron *pwron = platform_get_drvdata(pdev);
273 
274 	cancel_delayed_work_sync(&pwron->input_work);
275 
276 	if (device_may_wakeup(dev))
277 		enable_irq_wake(pwron->irq);
278 
279 	return 0;
280 }
281 
282 /**
283  * palmas_pwron_resume() - resume handler
284  * @dev:	power button device
285  *
286  * Just disable the wakeup capability of irq here.
287  *
288  * Return: 0
289  */
290 static int palmas_pwron_resume(struct device *dev)
291 {
292 	struct platform_device *pdev = to_platform_device(dev);
293 	struct palmas_pwron *pwron = platform_get_drvdata(pdev);
294 
295 	if (device_may_wakeup(dev))
296 		disable_irq_wake(pwron->irq);
297 
298 	return 0;
299 }
300 
301 static DEFINE_SIMPLE_DEV_PM_OPS(palmas_pwron_pm,
302 				palmas_pwron_suspend, palmas_pwron_resume);
303 
304 #ifdef CONFIG_OF
305 static const struct of_device_id of_palmas_pwr_match[] = {
306 	{ .compatible = "ti,palmas-pwrbutton" },
307 	{ },
308 };
309 
310 MODULE_DEVICE_TABLE(of, of_palmas_pwr_match);
311 #endif
312 
313 static struct platform_driver palmas_pwron_driver = {
314 	.probe	= palmas_pwron_probe,
315 	.remove	= palmas_pwron_remove,
316 	.driver	= {
317 		.name	= "palmas_pwrbutton",
318 		.of_match_table = of_match_ptr(of_palmas_pwr_match),
319 		.pm	= pm_sleep_ptr(&palmas_pwron_pm),
320 	},
321 };
322 module_platform_driver(palmas_pwron_driver);
323 
324 MODULE_ALIAS("platform:palmas-pwrbutton");
325 MODULE_DESCRIPTION("Palmas Power Button");
326 MODULE_LICENSE("GPL v2");
327 MODULE_AUTHOR("Texas Instruments Inc.");
328