diff options
-rw-r--r-- | Documentation/devicetree/bindings/input/ti,palmas-pwrbutton.txt | 36 | ||||
-rw-r--r-- | drivers/input/misc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/misc/palmas-pwrbutton.c | 330 |
4 files changed, 377 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/input/ti,palmas-pwrbutton.txt b/Documentation/devicetree/bindings/input/ti,palmas-pwrbutton.txt new file mode 100644 index 000000000000..a3dde8c30e67 --- /dev/null +++ b/Documentation/devicetree/bindings/input/ti,palmas-pwrbutton.txt | |||
@@ -0,0 +1,36 @@ | |||
1 | Texas Instruments Palmas family power button module | ||
2 | |||
3 | This module is part of the Palmas family of PMICs. For more details | ||
4 | about the whole chip see: | ||
5 | Documentation/devicetree/bindings/mfd/palmas.txt. | ||
6 | |||
7 | This module provides a simple power button event via an Interrupt. | ||
8 | |||
9 | Required properties: | ||
10 | - compatible: should be one of the following | ||
11 | - "ti,palmas-pwrbutton": For Palmas compatible power on button | ||
12 | - interrupt-parent: Parent interrupt device, must be handle of palmas node. | ||
13 | - interrupts: Interrupt number of power button submodule on device. | ||
14 | |||
15 | Optional Properties: | ||
16 | |||
17 | - ti,palmas-long-press-seconds: Duration in seconds which the power | ||
18 | button should be kept pressed for Palmas to power off automatically. | ||
19 | NOTE: This depends on OTP support and POWERHOLD signal configuration | ||
20 | on platform. Valid values are 6, 8, 10 and 12. | ||
21 | - ti,palmas-pwron-debounce-milli-seconds: Duration in milliseconds | ||
22 | which the power button should be kept pressed for Palmas to register | ||
23 | a press for debouncing purposes. NOTE: This depends on specific | ||
24 | Palmas variation capability. Valid values are 15, 100, 500 and 1000. | ||
25 | |||
26 | Example: | ||
27 | |||
28 | &palmas { | ||
29 | palmas_pwr_button: pwrbutton { | ||
30 | compatible = "ti,palmas-pwrbutton"; | ||
31 | interrupt-parent = <&tps659038>; | ||
32 | interrupts = <1 IRQ_TYPE_EDGE_FALLING>; | ||
33 | ti,palmas-long-press-seconds = <12>; | ||
34 | ti,palmas-pwron-debounce-milli-seconds = <15>; | ||
35 | }; | ||
36 | }; | ||
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 | ||
454 | config 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 | |||
454 | config INPUT_PCF50633_PMU | 464 | config 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 | |||
42 | obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o | 42 | obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o |
43 | obj-$(CONFIG_INPUT_MMA8450) += mma8450.o | 43 | obj-$(CONFIG_INPUT_MMA8450) += mma8450.o |
44 | obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o | 44 | obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o |
45 | obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o | ||
45 | obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o | 46 | obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o |
46 | obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o | 47 | obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o |
47 | obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o | 48 | obj-$(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 | */ | ||
39 | struct 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 | */ | ||
51 | struct 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 | */ | ||
60 | static 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, ®); | ||
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 | */ | ||
92 | static 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 | */ | ||
112 | static 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 | */ | ||
162 | static 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 | |||
232 | err_free_irq: | ||
233 | cancel_delayed_work_sync(&pwron->input_work); | ||
234 | free_irq(pwron->irq, pwron); | ||
235 | err_free_input: | ||
236 | input_free_device(input_dev); | ||
237 | err_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 | */ | ||
248 | static 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 | */ | ||
270 | static 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 | */ | ||
291 | static 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 | |||
303 | static SIMPLE_DEV_PM_OPS(palmas_pwron_pm, | ||
304 | palmas_pwron_suspend, palmas_pwron_resume); | ||
305 | |||
306 | #ifdef CONFIG_OF | ||
307 | static struct of_device_id of_palmas_pwr_match[] = { | ||
308 | { .compatible = "ti,palmas-pwrbutton" }, | ||
309 | { }, | ||
310 | }; | ||
311 | |||
312 | MODULE_DEVICE_TABLE(of, of_palmas_pwr_match); | ||
313 | #endif | ||
314 | |||
315 | static 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 | }; | ||
325 | module_platform_driver(palmas_pwron_driver); | ||
326 | |||
327 | MODULE_ALIAS("platform:palmas-pwrbutton"); | ||
328 | MODULE_DESCRIPTION("Palmas Power Button"); | ||
329 | MODULE_LICENSE("GPL V2"); | ||
330 | MODULE_AUTHOR("Texas Instruments Inc."); | ||