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