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 @@
#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include "sdhci-pltfm.h"
#include "sdhci-xenon.h"
......@@ -506,13 +508,24 @@ static int xenon_probe(struct platform_device *pdev)
if (err)
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);
if (err)
goto remove_sdhc;
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
remove_sdhc:
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
xenon_sdhc_unprepare(host);
err_clk:
clk_disable_unprepare(pltfm_host->clk);
......@@ -526,6 +539,10 @@ static int xenon_remove(struct platform_device *pdev)
struct sdhci_host *host = platform_get_drvdata(pdev);
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);
xenon_sdhc_unprepare(host);
......@@ -542,40 +559,78 @@ static int xenon_suspend(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 = sdhci_suspend_host(host);
if (ret)
return ret;
ret = pm_runtime_force_suspend(dev);
clk_disable_unprepare(pltfm_host->clk);
priv->restore_needed = true;
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_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);
ret = sdhci_runtime_suspend_host(host);
if (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
* be lost.
* Re-configure Xenon specific register to enable current SDHC
* Need to update the priv->clock here, or when runtime resume
* back, phy don't aware the clock change and won't adjust phy
* which will cause cmd err
*/
ret = xenon_sdhc_prepare(host);
if (ret)
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;
}
return sdhci_resume_host(host);
}
#endif
if (priv->restore_needed) {
ret = xenon_sdhc_prepare(host);
if (ret)
goto out;
priv->restore_needed = false;
}
static SIMPLE_DEV_PM_OPS(xenon_pmops, xenon_suspend, xenon_resume);
ret = sdhci_runtime_resume_host(host);
if (ret)
goto out;
return 0;
out:
clk_disable_unprepare(pltfm_host->clk);
return ret;
}
#endif /* CONFIG_PM */
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[] = {
{ .compatible = "marvell,armada-ap806-sdhci",},
......@@ -589,7 +644,7 @@ static struct platform_driver sdhci_xenon_driver = {
.driver = {
.name = "xenon-sdhci",
.of_match_table = sdhci_xenon_dt_ids,
.pm = &xenon_pmops,
.pm = &sdhci_xenon_dev_pm_ops,
},
.probe = xenon_probe,
.remove = xenon_remove,
......
......@@ -91,6 +91,7 @@ struct xenon_priv {
*/
void *phy_params;
struct xenon_emmc_phy_regs *emmc_phy_regs;
bool restore_needed;
};
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