aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/misc
diff options
context:
space:
mode:
authorNishanth Menon <nm@ti.com>2014-08-26 18:57:47 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2014-09-12 02:40:16 -0400
commitadff5962fdd2f29bac943bc014ebd529444b2153 (patch)
tree82e58e33eb0e2e903d55f2813f2dfeea3dbf94de /drivers/input/misc
parent1c24622572d6ab2ec8e731588d1c131563a64b53 (diff)
Input: introduce palmas-pwrbutton
Many palmas family of PMICs have support for interrupt based power button. This allows the device to notify the processor of external push button events over the shared palmas interrupt. However, this event is generated only during a "press" operation. Software is supposed to poll(sigh!) for detecting a release event. The PMIC also supports ability to power off independent of the software decisions when the button is pressed for a long duration if the PMIC is appropriately configured on the platform. Even though the function is similar to twl4030_pwrbutton, it is substantially different in operation to belong to a new driver of it's own. Based on original work done by Girish S Ghongdemath <girishsg@ti.com> Signed-off-by: Nishanth Menon <nm@ti.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input/misc')
-rw-r--r--drivers/input/misc/Kconfig10
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/palmas-pwrbutton.c330
3 files changed, 341 insertions, 0 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 51891f67b7df..36382e5b7703 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -451,6 +451,16 @@ config HP_SDC_RTC
451 Say Y here if you want to support the built-in real time clock 451 Say Y here if you want to support the built-in real time clock
452 of the HP SDC controller. 452 of the HP SDC controller.
453 453
454config INPUT_PALMAS_PWRBUTTON
455 tristate "Palmas Power button Driver"
456 depends on MFD_PALMAS
457 help
458 Say Y here if you want to enable power key reporting via the
459 Palmas family of PMICs.
460
461 To compile this driver as a module, choose M here. The module will
462 be called palmas_pwrbutton.
463
454config INPUT_PCF50633_PMU 464config INPUT_PCF50633_PMU
455 tristate "PCF50633 PMU events" 465 tristate "PCF50633 PMU events"
456 depends on MFD_PCF50633 466 depends on MFD_PCF50633
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index e0cee1774579..e8b84d2c845f 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o
42obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o 42obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
43obj-$(CONFIG_INPUT_MMA8450) += mma8450.o 43obj-$(CONFIG_INPUT_MMA8450) += mma8450.o
44obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o 44obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o
45obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o
45obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o 46obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
46obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o 47obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
47obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o 48obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o
diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c
new file mode 100644
index 000000000000..3f902110c293
--- /dev/null
+++ b/drivers/input/misc/palmas-pwrbutton.c
@@ -0,0 +1,330 @@
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 | IRQF_TRIGGER_LOW,
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 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#ifdef CONFIG_PM_SLEEP
262/**
263 * palmas_pwron_suspend() - suspend handler
264 * @dev: power button device
265 *
266 * Cancel all pending work items for the power button, setup irq for wakeup
267 *
268 * Return: 0
269 */
270static int palmas_pwron_suspend(struct device *dev)
271{
272 struct platform_device *pdev = to_platform_device(dev);
273 struct palmas_pwron *pwron = platform_get_drvdata(pdev);
274
275 cancel_delayed_work_sync(&pwron->input_work);
276
277 if (device_may_wakeup(dev))
278 enable_irq_wake(pwron->irq);
279
280 return 0;
281}
282
283/**
284 * palmas_pwron_resume() - resume handler
285 * @dev: power button device
286 *
287 * Just disable the wakeup capability of irq here.
288 *
289 * Return: 0
290 */
291static int palmas_pwron_resume(struct device *dev)
292{
293 struct platform_device *pdev = to_platform_device(dev);
294 struct palmas_pwron *pwron = platform_get_drvdata(pdev);
295
296 if (device_may_wakeup(dev))
297 disable_irq_wake(pwron->irq);
298
299 return 0;
300}
301#endif
302
303static SIMPLE_DEV_PM_OPS(palmas_pwron_pm,
304 palmas_pwron_suspend, palmas_pwron_resume);
305
306#ifdef CONFIG_OF
307static 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 .owner = THIS_MODULE,
321 .of_match_table = of_match_ptr(of_palmas_pwr_match),
322 .pm = &palmas_pwron_pm,
323 },
324};
325module_platform_driver(palmas_pwron_driver);
326
327MODULE_ALIAS("platform:palmas-pwrbutton");
328MODULE_DESCRIPTION("Palmas Power Button");
329MODULE_LICENSE("GPL V2");
330MODULE_AUTHOR("Texas Instruments Inc.");