aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZhoujie Wu <zjwu@marvell.com>2017-08-29 14:54:49 -0400
committerUlf Hansson <ulf.hansson@linaro.org>2017-08-30 09:37:31 -0400
commita027b2c5fed78851e69fab395b02d127a7759fc7 (patch)
tree6ec1c1c37a005483aaa4bd0afc563f5d570dc73e
parent689dc7eb2ca2bd89872ef1510ff912cf31815811 (diff)
mmc: sdhci-xenon: add runtime pm support and reimplement standby
Enable runtime pm support for xenon controller, which uses 50ms auto runtime suspend by default. Reimplement system standby based on runtime pm API. Introduce restore_needed to restore the Xenon specific registers when resume. Signed-off-by: Zhoujie Wu <zjwu@marvell.com> Acked-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
-rw-r--r--drivers/mmc/host/sdhci-xenon.c87
-rw-r--r--drivers/mmc/host/sdhci-xenon.h1
2 files changed, 72 insertions, 16 deletions
diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c
index 306ffaf7a5b2..2eec2e652c53 100644
--- a/drivers/mmc/host/sdhci-xenon.c
+++ b/drivers/mmc/host/sdhci-xenon.c
@@ -18,6 +18,8 @@
18#include <linux/ktime.h> 18#include <linux/ktime.h>
19#include <linux/module.h> 19#include <linux/module.h>
20#include <linux/of.h> 20#include <linux/of.h>
21#include <linux/pm.h>
22#include <linux/pm_runtime.h>
21 23
22#include "sdhci-pltfm.h" 24#include "sdhci-pltfm.h"
23#include "sdhci-xenon.h" 25#include "sdhci-xenon.h"
@@ -506,13 +508,24 @@ static int xenon_probe(struct platform_device *pdev)
506 if (err) 508 if (err)
507 goto err_clk; 509 goto err_clk;
508 510
511 pm_runtime_get_noresume(&pdev->dev);
512 pm_runtime_set_active(&pdev->dev);
513 pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
514 pm_runtime_use_autosuspend(&pdev->dev);
515 pm_runtime_enable(&pdev->dev);
516 pm_suspend_ignore_children(&pdev->dev, 1);
517
509 err = sdhci_add_host(host); 518 err = sdhci_add_host(host);
510 if (err) 519 if (err)
511 goto remove_sdhc; 520 goto remove_sdhc;
512 521
522 pm_runtime_put_autosuspend(&pdev->dev);
523
513 return 0; 524 return 0;
514 525
515remove_sdhc: 526remove_sdhc:
527 pm_runtime_disable(&pdev->dev);
528 pm_runtime_put_noidle(&pdev->dev);
516 xenon_sdhc_unprepare(host); 529 xenon_sdhc_unprepare(host);
517err_clk: 530err_clk:
518 clk_disable_unprepare(pltfm_host->clk); 531 clk_disable_unprepare(pltfm_host->clk);
@@ -526,6 +539,10 @@ static int xenon_remove(struct platform_device *pdev)
526 struct sdhci_host *host = platform_get_drvdata(pdev); 539 struct sdhci_host *host = platform_get_drvdata(pdev);
527 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 540 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
528 541
542 pm_runtime_get_sync(&pdev->dev);
543 pm_runtime_disable(&pdev->dev);
544 pm_runtime_put_noidle(&pdev->dev);
545
529 sdhci_remove_host(host, 0); 546 sdhci_remove_host(host, 0);
530 547
531 xenon_sdhc_unprepare(host); 548 xenon_sdhc_unprepare(host);
@@ -542,40 +559,78 @@ static int xenon_suspend(struct device *dev)
542{ 559{
543 struct sdhci_host *host = dev_get_drvdata(dev); 560 struct sdhci_host *host = dev_get_drvdata(dev);
544 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 561 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
562 struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
545 int ret; 563 int ret;
546 564
547 ret = sdhci_suspend_host(host); 565 ret = pm_runtime_force_suspend(dev);
548 if (ret)
549 return ret;
550 566
551 clk_disable_unprepare(pltfm_host->clk); 567 priv->restore_needed = true;
552 return ret; 568 return ret;
553} 569}
570#endif
554 571
555static int xenon_resume(struct device *dev) 572#ifdef CONFIG_PM
573static int xenon_runtime_suspend(struct device *dev)
556{ 574{
557 struct sdhci_host *host = dev_get_drvdata(dev); 575 struct sdhci_host *host = dev_get_drvdata(dev);
558 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 576 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
577 struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
559 int ret; 578 int ret;
560 579
561 ret = clk_prepare_enable(pltfm_host->clk); 580 ret = sdhci_runtime_suspend_host(host);
562 if (ret) 581 if (ret)
563 return ret; 582 return ret;
564 583
584 if (host->tuning_mode != SDHCI_TUNING_MODE_3)
585 mmc_retune_needed(host->mmc);
586
587 clk_disable_unprepare(pltfm_host->clk);
565 /* 588 /*
566 * If SoCs power off the entire Xenon, registers setting will 589 * Need to update the priv->clock here, or when runtime resume
567 * be lost. 590 * back, phy don't aware the clock change and won't adjust phy
568 * Re-configure Xenon specific register to enable current SDHC 591 * which will cause cmd err
569 */ 592 */
570 ret = xenon_sdhc_prepare(host); 593 priv->clock = 0;
571 if (ret) 594 return 0;
595}
596
597static int xenon_runtime_resume(struct device *dev)
598{
599 struct sdhci_host *host = dev_get_drvdata(dev);
600 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
601 struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
602 int ret;
603
604 ret = clk_prepare_enable(pltfm_host->clk);
605 if (ret) {
606 dev_err(dev, "can't enable mainck\n");
572 return ret; 607 return ret;
608 }
573 609
574 return sdhci_resume_host(host); 610 if (priv->restore_needed) {
575} 611 ret = xenon_sdhc_prepare(host);
576#endif 612 if (ret)
613 goto out;
614 priv->restore_needed = false;
615 }
577 616
578static SIMPLE_DEV_PM_OPS(xenon_pmops, xenon_suspend, xenon_resume); 617 ret = sdhci_runtime_resume_host(host);
618 if (ret)
619 goto out;
620 return 0;
621out:
622 clk_disable_unprepare(pltfm_host->clk);
623 return ret;
624}
625#endif /* CONFIG_PM */
626
627static const struct dev_pm_ops sdhci_xenon_dev_pm_ops = {
628 SET_SYSTEM_SLEEP_PM_OPS(xenon_suspend,
629 pm_runtime_force_resume)
630 SET_RUNTIME_PM_OPS(xenon_runtime_suspend,
631 xenon_runtime_resume,
632 NULL)
633};
579 634
580static const struct of_device_id sdhci_xenon_dt_ids[] = { 635static const struct of_device_id sdhci_xenon_dt_ids[] = {
581 { .compatible = "marvell,armada-ap806-sdhci",}, 636 { .compatible = "marvell,armada-ap806-sdhci",},
@@ -589,7 +644,7 @@ static struct platform_driver sdhci_xenon_driver = {
589 .driver = { 644 .driver = {
590 .name = "xenon-sdhci", 645 .name = "xenon-sdhci",
591 .of_match_table = sdhci_xenon_dt_ids, 646 .of_match_table = sdhci_xenon_dt_ids,
592 .pm = &xenon_pmops, 647 .pm = &sdhci_xenon_dev_pm_ops,
593 }, 648 },
594 .probe = xenon_probe, 649 .probe = xenon_probe,
595 .remove = xenon_remove, 650 .remove = xenon_remove,
diff --git a/drivers/mmc/host/sdhci-xenon.h b/drivers/mmc/host/sdhci-xenon.h
index 01937f1681b1..2bc0510c0769 100644
--- a/drivers/mmc/host/sdhci-xenon.h
+++ b/drivers/mmc/host/sdhci-xenon.h
@@ -91,6 +91,7 @@ struct xenon_priv {
91 */ 91 */
92 void *phy_params; 92 void *phy_params;
93 struct xenon_emmc_phy_regs *emmc_phy_regs; 93 struct xenon_emmc_phy_regs *emmc_phy_regs;
94 bool restore_needed;
94}; 95};
95 96
96int xenon_phy_adj(struct sdhci_host *host, struct mmc_ios *ios); 97int xenon_phy_adj(struct sdhci_host *host, struct mmc_ios *ios);