diff options
-rw-r--r-- | drivers/input/keyboard/samsung-keypad.c | 87 |
1 files changed, 80 insertions, 7 deletions
diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index a6e010e016f8..b746fce2d120 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c | |||
@@ -20,6 +20,8 @@ | |||
20 | #include <linux/io.h> | 20 | #include <linux/io.h> |
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
23 | #include <linux/pm.h> | ||
24 | #include <linux/pm_runtime.h> | ||
23 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
24 | #include <linux/sched.h> | 26 | #include <linux/sched.h> |
25 | #include <linux/input/samsung-keypad.h> | 27 | #include <linux/input/samsung-keypad.h> |
@@ -63,10 +65,12 @@ enum samsung_keypad_type { | |||
63 | 65 | ||
64 | struct samsung_keypad { | 66 | struct samsung_keypad { |
65 | struct input_dev *input_dev; | 67 | struct input_dev *input_dev; |
68 | struct platform_device *pdev; | ||
66 | struct clk *clk; | 69 | struct clk *clk; |
67 | void __iomem *base; | 70 | void __iomem *base; |
68 | wait_queue_head_t wait; | 71 | wait_queue_head_t wait; |
69 | bool stopped; | 72 | bool stopped; |
73 | bool wake_enabled; | ||
70 | int irq; | 74 | int irq; |
71 | unsigned int row_shift; | 75 | unsigned int row_shift; |
72 | unsigned int rows; | 76 | unsigned int rows; |
@@ -158,6 +162,8 @@ static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) | |||
158 | unsigned int val; | 162 | unsigned int val; |
159 | bool key_down; | 163 | bool key_down; |
160 | 164 | ||
165 | pm_runtime_get_sync(&keypad->pdev->dev); | ||
166 | |||
161 | do { | 167 | do { |
162 | val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR); | 168 | val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR); |
163 | /* Clear interrupt. */ | 169 | /* Clear interrupt. */ |
@@ -172,6 +178,8 @@ static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) | |||
172 | 178 | ||
173 | } while (key_down && !keypad->stopped); | 179 | } while (key_down && !keypad->stopped); |
174 | 180 | ||
181 | pm_runtime_put_sync(&keypad->pdev->dev); | ||
182 | |||
175 | return IRQ_HANDLED; | 183 | return IRQ_HANDLED; |
176 | } | 184 | } |
177 | 185 | ||
@@ -179,6 +187,8 @@ static void samsung_keypad_start(struct samsung_keypad *keypad) | |||
179 | { | 187 | { |
180 | unsigned int val; | 188 | unsigned int val; |
181 | 189 | ||
190 | pm_runtime_get_sync(&keypad->pdev->dev); | ||
191 | |||
182 | /* Tell IRQ thread that it may poll the device. */ | 192 | /* Tell IRQ thread that it may poll the device. */ |
183 | keypad->stopped = false; | 193 | keypad->stopped = false; |
184 | 194 | ||
@@ -191,12 +201,16 @@ static void samsung_keypad_start(struct samsung_keypad *keypad) | |||
191 | 201 | ||
192 | /* KEYIFCOL reg clear. */ | 202 | /* KEYIFCOL reg clear. */ |
193 | writel(0, keypad->base + SAMSUNG_KEYIFCOL); | 203 | writel(0, keypad->base + SAMSUNG_KEYIFCOL); |
204 | |||
205 | pm_runtime_put_sync(&keypad->pdev->dev); | ||
194 | } | 206 | } |
195 | 207 | ||
196 | static void samsung_keypad_stop(struct samsung_keypad *keypad) | 208 | static void samsung_keypad_stop(struct samsung_keypad *keypad) |
197 | { | 209 | { |
198 | unsigned int val; | 210 | unsigned int val; |
199 | 211 | ||
212 | pm_runtime_get_sync(&keypad->pdev->dev); | ||
213 | |||
200 | /* Signal IRQ thread to stop polling and disable the handler. */ | 214 | /* Signal IRQ thread to stop polling and disable the handler. */ |
201 | keypad->stopped = true; | 215 | keypad->stopped = true; |
202 | wake_up(&keypad->wait); | 216 | wake_up(&keypad->wait); |
@@ -217,6 +231,8 @@ static void samsung_keypad_stop(struct samsung_keypad *keypad) | |||
217 | * re-enable the handler. | 231 | * re-enable the handler. |
218 | */ | 232 | */ |
219 | enable_irq(keypad->irq); | 233 | enable_irq(keypad->irq); |
234 | |||
235 | pm_runtime_put_sync(&keypad->pdev->dev); | ||
220 | } | 236 | } |
221 | 237 | ||
222 | static int samsung_keypad_open(struct input_dev *input_dev) | 238 | static int samsung_keypad_open(struct input_dev *input_dev) |
@@ -298,9 +314,11 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) | |||
298 | } | 314 | } |
299 | 315 | ||
300 | keypad->input_dev = input_dev; | 316 | keypad->input_dev = input_dev; |
317 | keypad->pdev = pdev; | ||
301 | keypad->row_shift = row_shift; | 318 | keypad->row_shift = row_shift; |
302 | keypad->rows = pdata->rows; | 319 | keypad->rows = pdata->rows; |
303 | keypad->cols = pdata->cols; | 320 | keypad->cols = pdata->cols; |
321 | keypad->stopped = true; | ||
304 | init_waitqueue_head(&keypad->wait); | 322 | init_waitqueue_head(&keypad->wait); |
305 | 323 | ||
306 | input_dev->name = pdev->name; | 324 | input_dev->name = pdev->name; |
@@ -337,16 +355,21 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) | |||
337 | goto err_put_clk; | 355 | goto err_put_clk; |
338 | } | 356 | } |
339 | 357 | ||
358 | device_init_wakeup(&pdev->dev, pdata->wakeup); | ||
359 | platform_set_drvdata(pdev, keypad); | ||
360 | pm_runtime_enable(&pdev->dev); | ||
361 | |||
340 | error = input_register_device(keypad->input_dev); | 362 | error = input_register_device(keypad->input_dev); |
341 | if (error) | 363 | if (error) |
342 | goto err_free_irq; | 364 | goto err_free_irq; |
343 | 365 | ||
344 | device_init_wakeup(&pdev->dev, pdata->wakeup); | ||
345 | platform_set_drvdata(pdev, keypad); | ||
346 | return 0; | 366 | return 0; |
347 | 367 | ||
348 | err_free_irq: | 368 | err_free_irq: |
349 | free_irq(keypad->irq, keypad); | 369 | free_irq(keypad->irq, keypad); |
370 | pm_runtime_disable(&pdev->dev); | ||
371 | device_init_wakeup(&pdev->dev, 0); | ||
372 | platform_set_drvdata(pdev, NULL); | ||
350 | err_put_clk: | 373 | err_put_clk: |
351 | clk_put(keypad->clk); | 374 | clk_put(keypad->clk); |
352 | err_unmap_base: | 375 | err_unmap_base: |
@@ -362,6 +385,7 @@ static int __devexit samsung_keypad_remove(struct platform_device *pdev) | |||
362 | { | 385 | { |
363 | struct samsung_keypad *keypad = platform_get_drvdata(pdev); | 386 | struct samsung_keypad *keypad = platform_get_drvdata(pdev); |
364 | 387 | ||
388 | pm_runtime_disable(&pdev->dev); | ||
365 | device_init_wakeup(&pdev->dev, 0); | 389 | device_init_wakeup(&pdev->dev, 0); |
366 | platform_set_drvdata(pdev, NULL); | 390 | platform_set_drvdata(pdev, NULL); |
367 | 391 | ||
@@ -381,11 +405,57 @@ static int __devexit samsung_keypad_remove(struct platform_device *pdev) | |||
381 | return 0; | 405 | return 0; |
382 | } | 406 | } |
383 | 407 | ||
408 | #ifdef CONFIG_PM_RUNTIME | ||
409 | static int samsung_keypad_runtime_suspend(struct device *dev) | ||
410 | { | ||
411 | struct platform_device *pdev = to_platform_device(dev); | ||
412 | struct samsung_keypad *keypad = platform_get_drvdata(pdev); | ||
413 | unsigned int val; | ||
414 | int error; | ||
415 | |||
416 | if (keypad->stopped) | ||
417 | return 0; | ||
418 | |||
419 | /* This may fail on some SoCs due to lack of controller support */ | ||
420 | error = enable_irq_wake(keypad->irq); | ||
421 | if (!error) | ||
422 | keypad->wake_enabled = true; | ||
423 | |||
424 | val = readl(keypad->base + SAMSUNG_KEYIFCON); | ||
425 | val |= SAMSUNG_KEYIFCON_WAKEUPEN; | ||
426 | writel(val, keypad->base + SAMSUNG_KEYIFCON); | ||
427 | |||
428 | clk_disable(keypad->clk); | ||
429 | |||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | static int samsung_keypad_runtime_resume(struct device *dev) | ||
434 | { | ||
435 | struct platform_device *pdev = to_platform_device(dev); | ||
436 | struct samsung_keypad *keypad = platform_get_drvdata(pdev); | ||
437 | unsigned int val; | ||
438 | |||
439 | if (keypad->stopped) | ||
440 | return 0; | ||
441 | |||
442 | clk_enable(keypad->clk); | ||
443 | |||
444 | val = readl(keypad->base + SAMSUNG_KEYIFCON); | ||
445 | val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; | ||
446 | writel(val, keypad->base + SAMSUNG_KEYIFCON); | ||
447 | |||
448 | if (keypad->wake_enabled) | ||
449 | disable_irq_wake(keypad->irq); | ||
450 | |||
451 | return 0; | ||
452 | } | ||
453 | #endif | ||
454 | |||
384 | #ifdef CONFIG_PM_SLEEP | 455 | #ifdef CONFIG_PM_SLEEP |
385 | static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, | 456 | static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, |
386 | bool enable) | 457 | bool enable) |
387 | { | 458 | { |
388 | struct device *dev = keypad->input_dev->dev.parent; | ||
389 | unsigned int val; | 459 | unsigned int val; |
390 | 460 | ||
391 | clk_enable(keypad->clk); | 461 | clk_enable(keypad->clk); |
@@ -393,11 +463,11 @@ static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, | |||
393 | val = readl(keypad->base + SAMSUNG_KEYIFCON); | 463 | val = readl(keypad->base + SAMSUNG_KEYIFCON); |
394 | if (enable) { | 464 | if (enable) { |
395 | val |= SAMSUNG_KEYIFCON_WAKEUPEN; | 465 | val |= SAMSUNG_KEYIFCON_WAKEUPEN; |
396 | if (device_may_wakeup(dev)) | 466 | if (device_may_wakeup(&keypad->pdev->dev)) |
397 | enable_irq_wake(keypad->irq); | 467 | enable_irq_wake(keypad->irq); |
398 | } else { | 468 | } else { |
399 | val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; | 469 | val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; |
400 | if (device_may_wakeup(dev)) | 470 | if (device_may_wakeup(&keypad->pdev->dev)) |
401 | disable_irq_wake(keypad->irq); | 471 | disable_irq_wake(keypad->irq); |
402 | } | 472 | } |
403 | writel(val, keypad->base + SAMSUNG_KEYIFCON); | 473 | writel(val, keypad->base + SAMSUNG_KEYIFCON); |
@@ -442,8 +512,11 @@ static int samsung_keypad_resume(struct device *dev) | |||
442 | } | 512 | } |
443 | #endif | 513 | #endif |
444 | 514 | ||
445 | static SIMPLE_DEV_PM_OPS(samsung_keypad_pm_ops, | 515 | static const struct dev_pm_ops samsung_keypad_pm_ops = { |
446 | samsung_keypad_suspend, samsung_keypad_resume); | 516 | SET_SYSTEM_SLEEP_PM_OPS(samsung_keypad_suspend, samsung_keypad_resume) |
517 | SET_RUNTIME_PM_OPS(samsung_keypad_runtime_suspend, | ||
518 | samsung_keypad_runtime_resume, NULL) | ||
519 | }; | ||
447 | 520 | ||
448 | static struct platform_device_id samsung_keypad_driver_ids[] = { | 521 | static struct platform_device_id samsung_keypad_driver_ids[] = { |
449 | { | 522 | { |