diff options
Diffstat (limited to 'drivers/input/keyboard/samsung-keypad.c')
-rw-r--r-- | drivers/input/keyboard/samsung-keypad.c | 108 |
1 files changed, 82 insertions, 26 deletions
diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 8a0060cd3982..17ba7f9f80f3 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c | |||
@@ -20,11 +20,13 @@ | |||
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/of.h> | 26 | #include <linux/of.h> |
25 | #include <linux/of_gpio.h> | 27 | #include <linux/of_gpio.h> |
26 | #include <linux/sched.h> | 28 | #include <linux/sched.h> |
27 | #include <plat/keypad.h> | 29 | #include <linux/input/samsung-keypad.h> |
28 | 30 | ||
29 | #define SAMSUNG_KEYIFCON 0x00 | 31 | #define SAMSUNG_KEYIFCON 0x00 |
30 | #define SAMSUNG_KEYIFSTSCLR 0x04 | 32 | #define SAMSUNG_KEYIFSTSCLR 0x04 |
@@ -65,10 +67,12 @@ enum samsung_keypad_type { | |||
65 | 67 | ||
66 | struct samsung_keypad { | 68 | struct samsung_keypad { |
67 | struct input_dev *input_dev; | 69 | struct input_dev *input_dev; |
70 | struct platform_device *pdev; | ||
68 | struct clk *clk; | 71 | struct clk *clk; |
69 | void __iomem *base; | 72 | void __iomem *base; |
70 | wait_queue_head_t wait; | 73 | wait_queue_head_t wait; |
71 | bool stopped; | 74 | bool stopped; |
75 | bool wake_enabled; | ||
72 | int irq; | 76 | int irq; |
73 | enum samsung_keypad_type type; | 77 | enum samsung_keypad_type type; |
74 | unsigned int row_shift; | 78 | unsigned int row_shift; |
@@ -155,6 +159,8 @@ static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) | |||
155 | unsigned int val; | 159 | unsigned int val; |
156 | bool key_down; | 160 | bool key_down; |
157 | 161 | ||
162 | pm_runtime_get_sync(&keypad->pdev->dev); | ||
163 | |||
158 | do { | 164 | do { |
159 | val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR); | 165 | val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR); |
160 | /* Clear interrupt. */ | 166 | /* Clear interrupt. */ |
@@ -169,6 +175,8 @@ static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) | |||
169 | 175 | ||
170 | } while (key_down && !keypad->stopped); | 176 | } while (key_down && !keypad->stopped); |
171 | 177 | ||
178 | pm_runtime_put_sync(&keypad->pdev->dev); | ||
179 | |||
172 | return IRQ_HANDLED; | 180 | return IRQ_HANDLED; |
173 | } | 181 | } |
174 | 182 | ||
@@ -176,6 +184,8 @@ static void samsung_keypad_start(struct samsung_keypad *keypad) | |||
176 | { | 184 | { |
177 | unsigned int val; | 185 | unsigned int val; |
178 | 186 | ||
187 | pm_runtime_get_sync(&keypad->pdev->dev); | ||
188 | |||
179 | /* Tell IRQ thread that it may poll the device. */ | 189 | /* Tell IRQ thread that it may poll the device. */ |
180 | keypad->stopped = false; | 190 | keypad->stopped = false; |
181 | 191 | ||
@@ -188,12 +198,16 @@ static void samsung_keypad_start(struct samsung_keypad *keypad) | |||
188 | 198 | ||
189 | /* KEYIFCOL reg clear. */ | 199 | /* KEYIFCOL reg clear. */ |
190 | writel(0, keypad->base + SAMSUNG_KEYIFCOL); | 200 | writel(0, keypad->base + SAMSUNG_KEYIFCOL); |
201 | |||
202 | pm_runtime_put_sync(&keypad->pdev->dev); | ||
191 | } | 203 | } |
192 | 204 | ||
193 | static void samsung_keypad_stop(struct samsung_keypad *keypad) | 205 | static void samsung_keypad_stop(struct samsung_keypad *keypad) |
194 | { | 206 | { |
195 | unsigned int val; | 207 | unsigned int val; |
196 | 208 | ||
209 | pm_runtime_get_sync(&keypad->pdev->dev); | ||
210 | |||
197 | /* Signal IRQ thread to stop polling and disable the handler. */ | 211 | /* Signal IRQ thread to stop polling and disable the handler. */ |
198 | keypad->stopped = true; | 212 | keypad->stopped = true; |
199 | wake_up(&keypad->wait); | 213 | wake_up(&keypad->wait); |
@@ -214,6 +228,8 @@ static void samsung_keypad_stop(struct samsung_keypad *keypad) | |||
214 | * re-enable the handler. | 228 | * re-enable the handler. |
215 | */ | 229 | */ |
216 | enable_irq(keypad->irq); | 230 | enable_irq(keypad->irq); |
231 | |||
232 | pm_runtime_put_sync(&keypad->pdev->dev); | ||
217 | } | 233 | } |
218 | 234 | ||
219 | static int samsung_keypad_open(struct input_dev *input_dev) | 235 | static int samsung_keypad_open(struct input_dev *input_dev) |
@@ -418,9 +434,11 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) | |||
418 | } | 434 | } |
419 | 435 | ||
420 | keypad->input_dev = input_dev; | 436 | keypad->input_dev = input_dev; |
437 | keypad->pdev = pdev; | ||
421 | keypad->row_shift = row_shift; | 438 | keypad->row_shift = row_shift; |
422 | keypad->rows = pdata->rows; | 439 | keypad->rows = pdata->rows; |
423 | keypad->cols = pdata->cols; | 440 | keypad->cols = pdata->cols; |
441 | keypad->stopped = true; | ||
424 | init_waitqueue_head(&keypad->wait); | 442 | init_waitqueue_head(&keypad->wait); |
425 | 443 | ||
426 | if (pdev->dev.of_node) { | 444 | if (pdev->dev.of_node) { |
@@ -467,13 +485,14 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) | |||
467 | goto err_put_clk; | 485 | goto err_put_clk; |
468 | } | 486 | } |
469 | 487 | ||
488 | device_init_wakeup(&pdev->dev, pdata->wakeup); | ||
489 | platform_set_drvdata(pdev, keypad); | ||
490 | pm_runtime_enable(&pdev->dev); | ||
491 | |||
470 | error = input_register_device(keypad->input_dev); | 492 | error = input_register_device(keypad->input_dev); |
471 | if (error) | 493 | if (error) |
472 | goto err_free_irq; | 494 | goto err_free_irq; |
473 | 495 | ||
474 | device_init_wakeup(&pdev->dev, pdata->wakeup); | ||
475 | platform_set_drvdata(pdev, keypad); | ||
476 | |||
477 | if (pdev->dev.of_node) { | 496 | if (pdev->dev.of_node) { |
478 | devm_kfree(&pdev->dev, (void *)pdata->keymap_data->keymap); | 497 | devm_kfree(&pdev->dev, (void *)pdata->keymap_data->keymap); |
479 | devm_kfree(&pdev->dev, (void *)pdata->keymap_data); | 498 | devm_kfree(&pdev->dev, (void *)pdata->keymap_data); |
@@ -483,6 +502,9 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) | |||
483 | 502 | ||
484 | err_free_irq: | 503 | err_free_irq: |
485 | free_irq(keypad->irq, keypad); | 504 | free_irq(keypad->irq, keypad); |
505 | pm_runtime_disable(&pdev->dev); | ||
506 | device_init_wakeup(&pdev->dev, 0); | ||
507 | platform_set_drvdata(pdev, NULL); | ||
486 | err_put_clk: | 508 | err_put_clk: |
487 | clk_put(keypad->clk); | 509 | clk_put(keypad->clk); |
488 | samsung_keypad_dt_gpio_free(keypad); | 510 | samsung_keypad_dt_gpio_free(keypad); |
@@ -499,6 +521,7 @@ static int __devexit samsung_keypad_remove(struct platform_device *pdev) | |||
499 | { | 521 | { |
500 | struct samsung_keypad *keypad = platform_get_drvdata(pdev); | 522 | struct samsung_keypad *keypad = platform_get_drvdata(pdev); |
501 | 523 | ||
524 | pm_runtime_disable(&pdev->dev); | ||
502 | device_init_wakeup(&pdev->dev, 0); | 525 | device_init_wakeup(&pdev->dev, 0); |
503 | platform_set_drvdata(pdev, NULL); | 526 | platform_set_drvdata(pdev, NULL); |
504 | 527 | ||
@@ -519,11 +542,57 @@ static int __devexit samsung_keypad_remove(struct platform_device *pdev) | |||
519 | return 0; | 542 | return 0; |
520 | } | 543 | } |
521 | 544 | ||
522 | #ifdef CONFIG_PM | 545 | #ifdef CONFIG_PM_RUNTIME |
546 | static int samsung_keypad_runtime_suspend(struct device *dev) | ||
547 | { | ||
548 | struct platform_device *pdev = to_platform_device(dev); | ||
549 | struct samsung_keypad *keypad = platform_get_drvdata(pdev); | ||
550 | unsigned int val; | ||
551 | int error; | ||
552 | |||
553 | if (keypad->stopped) | ||
554 | return 0; | ||
555 | |||
556 | /* This may fail on some SoCs due to lack of controller support */ | ||
557 | error = enable_irq_wake(keypad->irq); | ||
558 | if (!error) | ||
559 | keypad->wake_enabled = true; | ||
560 | |||
561 | val = readl(keypad->base + SAMSUNG_KEYIFCON); | ||
562 | val |= SAMSUNG_KEYIFCON_WAKEUPEN; | ||
563 | writel(val, keypad->base + SAMSUNG_KEYIFCON); | ||
564 | |||
565 | clk_disable(keypad->clk); | ||
566 | |||
567 | return 0; | ||
568 | } | ||
569 | |||
570 | static int samsung_keypad_runtime_resume(struct device *dev) | ||
571 | { | ||
572 | struct platform_device *pdev = to_platform_device(dev); | ||
573 | struct samsung_keypad *keypad = platform_get_drvdata(pdev); | ||
574 | unsigned int val; | ||
575 | |||
576 | if (keypad->stopped) | ||
577 | return 0; | ||
578 | |||
579 | clk_enable(keypad->clk); | ||
580 | |||
581 | val = readl(keypad->base + SAMSUNG_KEYIFCON); | ||
582 | val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; | ||
583 | writel(val, keypad->base + SAMSUNG_KEYIFCON); | ||
584 | |||
585 | if (keypad->wake_enabled) | ||
586 | disable_irq_wake(keypad->irq); | ||
587 | |||
588 | return 0; | ||
589 | } | ||
590 | #endif | ||
591 | |||
592 | #ifdef CONFIG_PM_SLEEP | ||
523 | static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, | 593 | static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, |
524 | bool enable) | 594 | bool enable) |
525 | { | 595 | { |
526 | struct device *dev = keypad->input_dev->dev.parent; | ||
527 | unsigned int val; | 596 | unsigned int val; |
528 | 597 | ||
529 | clk_enable(keypad->clk); | 598 | clk_enable(keypad->clk); |
@@ -531,11 +600,11 @@ static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, | |||
531 | val = readl(keypad->base + SAMSUNG_KEYIFCON); | 600 | val = readl(keypad->base + SAMSUNG_KEYIFCON); |
532 | if (enable) { | 601 | if (enable) { |
533 | val |= SAMSUNG_KEYIFCON_WAKEUPEN; | 602 | val |= SAMSUNG_KEYIFCON_WAKEUPEN; |
534 | if (device_may_wakeup(dev)) | 603 | if (device_may_wakeup(&keypad->pdev->dev)) |
535 | enable_irq_wake(keypad->irq); | 604 | enable_irq_wake(keypad->irq); |
536 | } else { | 605 | } else { |
537 | val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; | 606 | val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; |
538 | if (device_may_wakeup(dev)) | 607 | if (device_may_wakeup(&keypad->pdev->dev)) |
539 | disable_irq_wake(keypad->irq); | 608 | disable_irq_wake(keypad->irq); |
540 | } | 609 | } |
541 | writel(val, keypad->base + SAMSUNG_KEYIFCON); | 610 | writel(val, keypad->base + SAMSUNG_KEYIFCON); |
@@ -578,12 +647,13 @@ static int samsung_keypad_resume(struct device *dev) | |||
578 | 647 | ||
579 | return 0; | 648 | return 0; |
580 | } | 649 | } |
650 | #endif | ||
581 | 651 | ||
582 | static const struct dev_pm_ops samsung_keypad_pm_ops = { | 652 | static const struct dev_pm_ops samsung_keypad_pm_ops = { |
583 | .suspend = samsung_keypad_suspend, | 653 | SET_SYSTEM_SLEEP_PM_OPS(samsung_keypad_suspend, samsung_keypad_resume) |
584 | .resume = samsung_keypad_resume, | 654 | SET_RUNTIME_PM_OPS(samsung_keypad_runtime_suspend, |
655 | samsung_keypad_runtime_resume, NULL) | ||
585 | }; | 656 | }; |
586 | #endif | ||
587 | 657 | ||
588 | #ifdef CONFIG_OF | 658 | #ifdef CONFIG_OF |
589 | static const struct of_device_id samsung_keypad_dt_match[] = { | 659 | static const struct of_device_id samsung_keypad_dt_match[] = { |
@@ -615,27 +685,13 @@ static struct platform_driver samsung_keypad_driver = { | |||
615 | .name = "samsung-keypad", | 685 | .name = "samsung-keypad", |
616 | .owner = THIS_MODULE, | 686 | .owner = THIS_MODULE, |
617 | .of_match_table = samsung_keypad_dt_match, | 687 | .of_match_table = samsung_keypad_dt_match, |
618 | #ifdef CONFIG_PM | ||
619 | .pm = &samsung_keypad_pm_ops, | 688 | .pm = &samsung_keypad_pm_ops, |
620 | #endif | ||
621 | }, | 689 | }, |
622 | .id_table = samsung_keypad_driver_ids, | 690 | .id_table = samsung_keypad_driver_ids, |
623 | }; | 691 | }; |
624 | 692 | module_platform_driver(samsung_keypad_driver); | |
625 | static int __init samsung_keypad_init(void) | ||
626 | { | ||
627 | return platform_driver_register(&samsung_keypad_driver); | ||
628 | } | ||
629 | module_init(samsung_keypad_init); | ||
630 | |||
631 | static void __exit samsung_keypad_exit(void) | ||
632 | { | ||
633 | platform_driver_unregister(&samsung_keypad_driver); | ||
634 | } | ||
635 | module_exit(samsung_keypad_exit); | ||
636 | 693 | ||
637 | MODULE_DESCRIPTION("Samsung keypad driver"); | 694 | MODULE_DESCRIPTION("Samsung keypad driver"); |
638 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | 695 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); |
639 | MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); | 696 | MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); |
640 | MODULE_LICENSE("GPL"); | 697 | MODULE_LICENSE("GPL"); |
641 | MODULE_ALIAS("platform:samsung-keypad"); | ||