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