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