diff options
author | Kevin Liu <kliu5@marvell.com> | 2013-02-01 04:48:30 -0500 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2013-02-24 14:37:13 -0500 |
commit | bb691ae464b77d30e74c66480e98d74e88d6b194 (patch) | |
tree | c49997ac0975f3e20d43725ea233e48f910ac651 /drivers/mmc | |
parent | f5c2758fbb3095dc0fe4725e4a2eaec3580e1eba (diff) |
mmc: sdhci-pxav3: add pm runtime support
Signed-off-by: Kevin Liu <kliu5@marvell.com>
Signed-off-by: Jialing Fu <jlfu@marvell.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/sdhci-pxav3.c | 96 |
1 files changed, 95 insertions, 1 deletions
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index f09877fcc3ec..a0cdbc570a83 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c | |||
@@ -32,10 +32,14 @@ | |||
32 | #include <linux/of.h> | 32 | #include <linux/of.h> |
33 | #include <linux/of_device.h> | 33 | #include <linux/of_device.h> |
34 | #include <linux/of_gpio.h> | 34 | #include <linux/of_gpio.h> |
35 | #include <linux/pm.h> | ||
36 | #include <linux/pm_runtime.h> | ||
35 | 37 | ||
36 | #include "sdhci.h" | 38 | #include "sdhci.h" |
37 | #include "sdhci-pltfm.h" | 39 | #include "sdhci-pltfm.h" |
38 | 40 | ||
41 | #define PXAV3_RPM_DELAY_MS 50 | ||
42 | |||
39 | #define SD_CLOCK_BURST_SIZE_SETUP 0x10A | 43 | #define SD_CLOCK_BURST_SIZE_SETUP 0x10A |
40 | #define SDCLK_SEL 0x100 | 44 | #define SDCLK_SEL 0x100 |
41 | #define SDCLK_DELAY_SHIFT 9 | 45 | #define SDCLK_DELAY_SHIFT 9 |
@@ -296,9 +300,18 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) | |||
296 | 300 | ||
297 | sdhci_get_of_property(pdev); | 301 | sdhci_get_of_property(pdev); |
298 | 302 | ||
303 | pm_runtime_set_active(&pdev->dev); | ||
304 | pm_runtime_enable(&pdev->dev); | ||
305 | pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS); | ||
306 | pm_runtime_use_autosuspend(&pdev->dev); | ||
307 | pm_suspend_ignore_children(&pdev->dev, 1); | ||
308 | pm_runtime_get_noresume(&pdev->dev); | ||
309 | |||
299 | ret = sdhci_add_host(host); | 310 | ret = sdhci_add_host(host); |
300 | if (ret) { | 311 | if (ret) { |
301 | dev_err(&pdev->dev, "failed to add host\n"); | 312 | dev_err(&pdev->dev, "failed to add host\n"); |
313 | pm_runtime_forbid(&pdev->dev); | ||
314 | pm_runtime_disable(&pdev->dev); | ||
302 | goto err_add_host; | 315 | goto err_add_host; |
303 | } | 316 | } |
304 | 317 | ||
@@ -311,6 +324,8 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) | |||
311 | device_init_wakeup(&pdev->dev, 0); | 324 | device_init_wakeup(&pdev->dev, 0); |
312 | } | 325 | } |
313 | 326 | ||
327 | pm_runtime_put_autosuspend(&pdev->dev); | ||
328 | |||
314 | return 0; | 329 | return 0; |
315 | 330 | ||
316 | err_add_host: | 331 | err_add_host: |
@@ -329,7 +344,9 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) | |||
329 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | 344 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
330 | struct sdhci_pxa *pxa = pltfm_host->priv; | 345 | struct sdhci_pxa *pxa = pltfm_host->priv; |
331 | 346 | ||
347 | pm_runtime_get_sync(&pdev->dev); | ||
332 | sdhci_remove_host(host, 1); | 348 | sdhci_remove_host(host, 1); |
349 | pm_runtime_disable(&pdev->dev); | ||
333 | 350 | ||
334 | clk_disable_unprepare(pltfm_host->clk); | 351 | clk_disable_unprepare(pltfm_host->clk); |
335 | clk_put(pltfm_host->clk); | 352 | clk_put(pltfm_host->clk); |
@@ -342,6 +359,83 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) | |||
342 | return 0; | 359 | return 0; |
343 | } | 360 | } |
344 | 361 | ||
362 | #ifdef CONFIG_PM_SLEEP | ||
363 | static int sdhci_pxav3_suspend(struct device *dev) | ||
364 | { | ||
365 | int ret; | ||
366 | struct sdhci_host *host = dev_get_drvdata(dev); | ||
367 | |||
368 | pm_runtime_get_sync(dev); | ||
369 | ret = sdhci_suspend_host(host); | ||
370 | pm_runtime_mark_last_busy(dev); | ||
371 | pm_runtime_put_autosuspend(dev); | ||
372 | |||
373 | return ret; | ||
374 | } | ||
375 | |||
376 | static int sdhci_pxav3_resume(struct device *dev) | ||
377 | { | ||
378 | int ret; | ||
379 | struct sdhci_host *host = dev_get_drvdata(dev); | ||
380 | |||
381 | pm_runtime_get_sync(dev); | ||
382 | ret = sdhci_resume_host(host); | ||
383 | pm_runtime_mark_last_busy(dev); | ||
384 | pm_runtime_put_autosuspend(dev); | ||
385 | |||
386 | return ret; | ||
387 | } | ||
388 | #endif | ||
389 | |||
390 | #ifdef CONFIG_PM_RUNTIME | ||
391 | static int sdhci_pxav3_runtime_suspend(struct device *dev) | ||
392 | { | ||
393 | struct sdhci_host *host = dev_get_drvdata(dev); | ||
394 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | ||
395 | unsigned long flags; | ||
396 | |||
397 | if (pltfm_host->clk) { | ||
398 | spin_lock_irqsave(&host->lock, flags); | ||
399 | host->runtime_suspended = true; | ||
400 | spin_unlock_irqrestore(&host->lock, flags); | ||
401 | |||
402 | clk_disable_unprepare(pltfm_host->clk); | ||
403 | } | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static int sdhci_pxav3_runtime_resume(struct device *dev) | ||
409 | { | ||
410 | struct sdhci_host *host = dev_get_drvdata(dev); | ||
411 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | ||
412 | unsigned long flags; | ||
413 | |||
414 | if (pltfm_host->clk) { | ||
415 | clk_prepare_enable(pltfm_host->clk); | ||
416 | |||
417 | spin_lock_irqsave(&host->lock, flags); | ||
418 | host->runtime_suspended = false; | ||
419 | spin_unlock_irqrestore(&host->lock, flags); | ||
420 | } | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | #endif | ||
425 | |||
426 | #ifdef CONFIG_PM | ||
427 | static const struct dev_pm_ops sdhci_pxav3_pmops = { | ||
428 | SET_SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume) | ||
429 | SET_RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend, | ||
430 | sdhci_pxav3_runtime_resume, NULL) | ||
431 | }; | ||
432 | |||
433 | #define SDHCI_PXAV3_PMOPS (&sdhci_pxav3_pmops) | ||
434 | |||
435 | #else | ||
436 | #define SDHCI_PXAV3_PMOPS NULL | ||
437 | #endif | ||
438 | |||
345 | static struct platform_driver sdhci_pxav3_driver = { | 439 | static struct platform_driver sdhci_pxav3_driver = { |
346 | .driver = { | 440 | .driver = { |
347 | .name = "sdhci-pxav3", | 441 | .name = "sdhci-pxav3", |
@@ -349,7 +443,7 @@ static struct platform_driver sdhci_pxav3_driver = { | |||
349 | .of_match_table = sdhci_pxav3_of_match, | 443 | .of_match_table = sdhci_pxav3_of_match, |
350 | #endif | 444 | #endif |
351 | .owner = THIS_MODULE, | 445 | .owner = THIS_MODULE, |
352 | .pm = SDHCI_PLTFM_PMOPS, | 446 | .pm = SDHCI_PXAV3_PMOPS, |
353 | }, | 447 | }, |
354 | .probe = sdhci_pxav3_probe, | 448 | .probe = sdhci_pxav3_probe, |
355 | .remove = sdhci_pxav3_remove, | 449 | .remove = sdhci_pxav3_remove, |