diff options
Diffstat (limited to 'drivers/amba')
-rw-r--r-- | drivers/amba/bus.c | 57 |
1 files changed, 54 insertions, 3 deletions
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index d74926e0939e..84bdaace56c8 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c | |||
@@ -365,6 +365,40 @@ static int amba_pm_restore_noirq(struct device *dev) | |||
365 | 365 | ||
366 | #endif /* !CONFIG_HIBERNATE_CALLBACKS */ | 366 | #endif /* !CONFIG_HIBERNATE_CALLBACKS */ |
367 | 367 | ||
368 | #ifdef CONFIG_PM_RUNTIME | ||
369 | /* | ||
370 | * Hooks to provide runtime PM of the pclk (bus clock). It is safe to | ||
371 | * enable/disable the bus clock at runtime PM suspend/resume as this | ||
372 | * does not result in loss of context. However, disabling vcore power | ||
373 | * would do, so we leave that to the driver. | ||
374 | */ | ||
375 | static int amba_pm_runtime_suspend(struct device *dev) | ||
376 | { | ||
377 | struct amba_device *pcdev = to_amba_device(dev); | ||
378 | int ret = pm_generic_runtime_suspend(dev); | ||
379 | |||
380 | if (ret == 0 && dev->driver) | ||
381 | clk_disable(pcdev->pclk); | ||
382 | |||
383 | return ret; | ||
384 | } | ||
385 | |||
386 | static int amba_pm_runtime_resume(struct device *dev) | ||
387 | { | ||
388 | struct amba_device *pcdev = to_amba_device(dev); | ||
389 | int ret; | ||
390 | |||
391 | if (dev->driver) { | ||
392 | ret = clk_enable(pcdev->pclk); | ||
393 | /* Failure is probably fatal to the system, but... */ | ||
394 | if (ret) | ||
395 | return ret; | ||
396 | } | ||
397 | |||
398 | return pm_generic_runtime_resume(dev); | ||
399 | } | ||
400 | #endif | ||
401 | |||
368 | #ifdef CONFIG_PM | 402 | #ifdef CONFIG_PM |
369 | 403 | ||
370 | static const struct dev_pm_ops amba_pm = { | 404 | static const struct dev_pm_ops amba_pm = { |
@@ -383,8 +417,8 @@ static const struct dev_pm_ops amba_pm = { | |||
383 | .poweroff_noirq = amba_pm_poweroff_noirq, | 417 | .poweroff_noirq = amba_pm_poweroff_noirq, |
384 | .restore_noirq = amba_pm_restore_noirq, | 418 | .restore_noirq = amba_pm_restore_noirq, |
385 | SET_RUNTIME_PM_OPS( | 419 | SET_RUNTIME_PM_OPS( |
386 | pm_generic_runtime_suspend, | 420 | amba_pm_runtime_suspend, |
387 | pm_generic_runtime_resume, | 421 | amba_pm_runtime_resume, |
388 | pm_generic_runtime_idle | 422 | pm_generic_runtime_idle |
389 | ) | 423 | ) |
390 | }; | 424 | }; |
@@ -494,10 +528,18 @@ static int amba_probe(struct device *dev) | |||
494 | if (ret) | 528 | if (ret) |
495 | break; | 529 | break; |
496 | 530 | ||
531 | pm_runtime_get_noresume(dev); | ||
532 | pm_runtime_set_active(dev); | ||
533 | pm_runtime_enable(dev); | ||
534 | |||
497 | ret = pcdrv->probe(pcdev, id); | 535 | ret = pcdrv->probe(pcdev, id); |
498 | if (ret == 0) | 536 | if (ret == 0) |
499 | break; | 537 | break; |
500 | 538 | ||
539 | pm_runtime_disable(dev); | ||
540 | pm_runtime_set_suspended(dev); | ||
541 | pm_runtime_put_noidle(dev); | ||
542 | |||
501 | amba_put_disable_pclk(pcdev); | 543 | amba_put_disable_pclk(pcdev); |
502 | amba_put_disable_vcore(pcdev); | 544 | amba_put_disable_vcore(pcdev); |
503 | } while (0); | 545 | } while (0); |
@@ -509,7 +551,16 @@ static int amba_remove(struct device *dev) | |||
509 | { | 551 | { |
510 | struct amba_device *pcdev = to_amba_device(dev); | 552 | struct amba_device *pcdev = to_amba_device(dev); |
511 | struct amba_driver *drv = to_amba_driver(dev->driver); | 553 | struct amba_driver *drv = to_amba_driver(dev->driver); |
512 | int ret = drv->remove(pcdev); | 554 | int ret; |
555 | |||
556 | pm_runtime_get_sync(dev); | ||
557 | ret = drv->remove(pcdev); | ||
558 | pm_runtime_put_noidle(dev); | ||
559 | |||
560 | /* Undo the runtime PM settings in amba_probe() */ | ||
561 | pm_runtime_disable(dev); | ||
562 | pm_runtime_set_suspended(dev); | ||
563 | pm_runtime_put_noidle(dev); | ||
513 | 564 | ||
514 | amba_put_disable_pclk(pcdev); | 565 | amba_put_disable_pclk(pcdev); |
515 | amba_put_disable_vcore(pcdev); | 566 | amba_put_disable_vcore(pcdev); |