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 */
32struct 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 */
44struct 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 */
53static 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 */
85static 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 */
105static 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 */
155static 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
232err_free_irq:
233	cancel_delayed_work_sync(&pwron->input_work);
234	free_irq(pwron->irq, pwron);
235err_free_input:
236	input_free_device(input_dev);
237err_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 */
248static void 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
259/**
260 * palmas_pwron_suspend() - suspend handler
261 * @dev:	power button device
262 *
263 * Cancel all pending work items for the power button, setup irq for wakeup
264 *
265 * Return: 0
266 */
267static int palmas_pwron_suspend(struct device *dev)
268{
269	struct platform_device *pdev = to_platform_device(dev);
270	struct palmas_pwron *pwron = platform_get_drvdata(pdev);
271
272	cancel_delayed_work_sync(&pwron->input_work);
273
274	if (device_may_wakeup(dev))
275		enable_irq_wake(pwron->irq);
276
277	return 0;
278}
279
280/**
281 * palmas_pwron_resume() - resume handler
282 * @dev:	power button device
283 *
284 * Just disable the wakeup capability of irq here.
285 *
286 * Return: 0
287 */
288static int palmas_pwron_resume(struct device *dev)
289{
290	struct platform_device *pdev = to_platform_device(dev);
291	struct palmas_pwron *pwron = platform_get_drvdata(pdev);
292
293	if (device_may_wakeup(dev))
294		disable_irq_wake(pwron->irq);
295
296	return 0;
297}
298
299static DEFINE_SIMPLE_DEV_PM_OPS(palmas_pwron_pm,
300				palmas_pwron_suspend, palmas_pwron_resume);
301
302#ifdef CONFIG_OF
303static const struct of_device_id of_palmas_pwr_match[] = {
304	{ .compatible = "ti,palmas-pwrbutton" },
305	{ },
306};
307
308MODULE_DEVICE_TABLE(of, of_palmas_pwr_match);
309#endif
310
311static struct platform_driver palmas_pwron_driver = {
312	.probe	= palmas_pwron_probe,
313	.remove_new = palmas_pwron_remove,
314	.driver	= {
315		.name	= "palmas_pwrbutton",
316		.of_match_table = of_match_ptr(of_palmas_pwr_match),
317		.pm	= pm_sleep_ptr(&palmas_pwron_pm),
318	},
319};
320module_platform_driver(palmas_pwron_driver);
321
322MODULE_ALIAS("platform:palmas-pwrbutton");
323MODULE_DESCRIPTION("Palmas Power Button");
324MODULE_LICENSE("GPL v2");
325MODULE_AUTHOR("Texas Instruments Inc.");
326