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