aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2011-12-29 12:58:16 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2011-12-30 03:39:19 -0500
commit48c98b1bb85a09adf4aa27316682d573e1f37ebf (patch)
treeca898d31965df3f7c94d7100a7ce81e58f93fe66 /drivers/input
parentfd0fc21350838d3073647be173242db0c58744c8 (diff)
Input: samsung-keypad - implement runtime power management support
When runtime power management is enabled put the Samsung keypad driver into suspend mode with wakeups disabled whenever the device is open but a key is not actually been pressed. As well as saving a trivial amount of power this will support the use of SoC wide idle modes which put the entire device into a retention mode and use explicit wakeup sources to exit. Since not all of the interrupt controllers used with the driver support set_irq_wake() (though they all do the right thing) and there's a nasty WARN() when we disable wake after failing to enable it keep track of the current wake status from runtime PM and only disable wake if we managed to enable it; I'm not entirely sure why this doesn't affect the existing uses of the API in the driver. System suspend is unaffected as the driver core will runtime resume any suspended devices prior to system suspend. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Dmitry Torokhov <dtor@mail.ru> Conflicts: drivers/input/keyboard/samsung-keypad.c
Diffstat (limited to 'drivers/input')
-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 {