aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/keyboard/samsung-keypad.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-01-10 13:55:52 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2012-01-10 13:55:52 -0500
commitdbe950f201a8edd353b0bd9079e8d536ee4ce37c (patch)
treedffbada6b3d33cc67383758570de22b4f45693b6 /drivers/input/keyboard/samsung-keypad.c
parentf62f61917d72c1fb0101ad405664f6fc868d676b (diff)
parentda733563be5a9da26fe81d9f007262d00b846e22 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (64 commits) Input: tc3589x-keypad - add missing kerneldoc Input: ucb1400-ts - switch to using dev_xxx() for diagnostic messages Input: ucb1400_ts - convert to threaded IRQ Input: ucb1400_ts - drop inline annotations Input: usb1400_ts - add __devinit/__devexit section annotations Input: ucb1400_ts - set driver owner Input: ucb1400_ts - convert to use dev_pm_ops Input: psmouse - make sure we do not use stale methods Input: evdev - do not block waiting for an event if fd is nonblock Input: evdev - if no events and non-block, return EAGAIN not 0 Input: evdev - only allow reading events if a full packet is present Input: add driver for pixcir i2c touchscreens Input: samsung-keypad - implement runtime power management support Input: tegra-kbc - report wakeup key for some platforms Input: tegra-kbc - add device tree bindings Input: add driver for AUO In-Cell touchscreens using pixcir ICs Input: mpu3050 - configure the sampling method Input: mpu3050 - ensure we enable interrupts Input: mpu3050 - add of_match table for device-tree probing Input: sentelic - document the latest hardware ... Fix up fairly trivial conflicts (device tree matching conflicting with some independent cleanups) in drivers/input/keyboard/samsung-keypad.c
Diffstat (limited to 'drivers/input/keyboard/samsung-keypad.c')
-rw-r--r--drivers/input/keyboard/samsung-keypad.c108
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
66struct samsung_keypad { 68struct 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
193static void samsung_keypad_stop(struct samsung_keypad *keypad) 205static 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
219static int samsung_keypad_open(struct input_dev *input_dev) 235static 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
484err_free_irq: 503err_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);
486err_put_clk: 508err_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
546static 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
570static 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
523static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, 593static 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
582static const struct dev_pm_ops samsung_keypad_pm_ops = { 652static 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
589static const struct of_device_id samsung_keypad_dt_match[] = { 659static 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 692module_platform_driver(samsung_keypad_driver);
625static int __init samsung_keypad_init(void)
626{
627 return platform_driver_register(&samsung_keypad_driver);
628}
629module_init(samsung_keypad_init);
630
631static void __exit samsung_keypad_exit(void)
632{
633 platform_driver_unregister(&samsung_keypad_driver);
634}
635module_exit(samsung_keypad_exit);
636 693
637MODULE_DESCRIPTION("Samsung keypad driver"); 694MODULE_DESCRIPTION("Samsung keypad driver");
638MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); 695MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
639MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); 696MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
640MODULE_LICENSE("GPL"); 697MODULE_LICENSE("GPL");
641MODULE_ALIAS("platform:samsung-keypad");