aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/input/keyboard/samsung-keypad.c87
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
64struct samsung_keypad { 66struct 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
196static void samsung_keypad_stop(struct samsung_keypad *keypad) 208static 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
222static int samsung_keypad_open(struct input_dev *input_dev) 238static 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
348err_free_irq: 368err_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);
350err_put_clk: 373err_put_clk:
351 clk_put(keypad->clk); 374 clk_put(keypad->clk);
352err_unmap_base: 375err_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
409static 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
433static 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
385static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, 456static 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
445static SIMPLE_DEV_PM_OPS(samsung_keypad_pm_ops, 515static 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
448static struct platform_device_id samsung_keypad_driver_ids[] = { 521static struct platform_device_id samsung_keypad_driver_ids[] = {
449 { 522 {