Commit a027b2c5 authored by Zhoujie Wu's avatar Zhoujie Wu Committed by Ulf Hansson

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: default avatarZhoujie Wu <zjwu@marvell.com>
Acked-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 689dc7eb
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
#include "sdhci-xenon.h" #include "sdhci-xenon.h"
...@@ -506,13 +508,24 @@ static int xenon_probe(struct platform_device *pdev) ...@@ -506,13 +508,24 @@ static int xenon_probe(struct platform_device *pdev)
if (err) if (err)
goto err_clk; goto err_clk;
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_suspend_ignore_children(&pdev->dev, 1);
err = sdhci_add_host(host); err = sdhci_add_host(host);
if (err) if (err)
goto remove_sdhc; goto remove_sdhc;
pm_runtime_put_autosuspend(&pdev->dev);
return 0; return 0;
remove_sdhc: remove_sdhc:
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
xenon_sdhc_unprepare(host); xenon_sdhc_unprepare(host);
err_clk: err_clk:
clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(pltfm_host->clk);
...@@ -526,6 +539,10 @@ static int xenon_remove(struct platform_device *pdev) ...@@ -526,6 +539,10 @@ static int xenon_remove(struct platform_device *pdev)
struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
sdhci_remove_host(host, 0); sdhci_remove_host(host, 0);
xenon_sdhc_unprepare(host); xenon_sdhc_unprepare(host);
...@@ -542,40 +559,78 @@ static int xenon_suspend(struct device *dev) ...@@ -542,40 +559,78 @@ static int xenon_suspend(struct device *dev)
{ {
struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
int ret; int ret;
ret = sdhci_suspend_host(host); ret = pm_runtime_force_suspend(dev);
if (ret)
return ret;
clk_disable_unprepare(pltfm_host->clk); priv->restore_needed = true;
return ret; return ret;
} }
#endif
static int xenon_resume(struct device *dev) #ifdef CONFIG_PM
static int xenon_runtime_suspend(struct device *dev)
{ {
struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
int ret; int ret;
ret = clk_prepare_enable(pltfm_host->clk); ret = sdhci_runtime_suspend_host(host);
if (ret) if (ret)
return ret; return ret;
if (host->tuning_mode != SDHCI_TUNING_MODE_3)
mmc_retune_needed(host->mmc);
clk_disable_unprepare(pltfm_host->clk);
/* /*
* If SoCs power off the entire Xenon, registers setting will * Need to update the priv->clock here, or when runtime resume
* be lost. * back, phy don't aware the clock change and won't adjust phy
* Re-configure Xenon specific register to enable current SDHC * which will cause cmd err
*/ */
priv->clock = 0;
return 0;
}
static int xenon_runtime_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
int ret;
ret = clk_prepare_enable(pltfm_host->clk);
if (ret) {
dev_err(dev, "can't enable mainck\n");
return ret;
}
if (priv->restore_needed) {
ret = xenon_sdhc_prepare(host); ret = xenon_sdhc_prepare(host);
if (ret) if (ret)
return ret; goto out;
priv->restore_needed = false;
}
return sdhci_resume_host(host); ret = sdhci_runtime_resume_host(host);
if (ret)
goto out;
return 0;
out:
clk_disable_unprepare(pltfm_host->clk);
return ret;
} }
#endif #endif /* CONFIG_PM */
static SIMPLE_DEV_PM_OPS(xenon_pmops, xenon_suspend, xenon_resume); static const struct dev_pm_ops sdhci_xenon_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(xenon_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(xenon_runtime_suspend,
xenon_runtime_resume,
NULL)
};
static const struct of_device_id sdhci_xenon_dt_ids[] = { static const struct of_device_id sdhci_xenon_dt_ids[] = {
{ .compatible = "marvell,armada-ap806-sdhci",}, { .compatible = "marvell,armada-ap806-sdhci",},
...@@ -589,7 +644,7 @@ static struct platform_driver sdhci_xenon_driver = { ...@@ -589,7 +644,7 @@ static struct platform_driver sdhci_xenon_driver = {
.driver = { .driver = {
.name = "xenon-sdhci", .name = "xenon-sdhci",
.of_match_table = sdhci_xenon_dt_ids, .of_match_table = sdhci_xenon_dt_ids,
.pm = &xenon_pmops, .pm = &sdhci_xenon_dev_pm_ops,
}, },
.probe = xenon_probe, .probe = xenon_probe,
.remove = xenon_remove, .remove = xenon_remove,
......
...@@ -91,6 +91,7 @@ struct xenon_priv { ...@@ -91,6 +91,7 @@ struct xenon_priv {
*/ */
void *phy_params; void *phy_params;
struct xenon_emmc_phy_regs *emmc_phy_regs; struct xenon_emmc_phy_regs *emmc_phy_regs;
bool restore_needed;
}; };
int xenon_phy_adj(struct sdhci_host *host, struct mmc_ios *ios); int xenon_phy_adj(struct sdhci_host *host, struct mmc_ios *ios);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment