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>
This commit is contained in:
parent
689dc7eb2c
commit
a027b2c5fe
|
@ -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;
|
||||
}
|
||||
|
||||
static int xenon_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(pltfm_host->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* If SoCs power off the entire Xenon, registers setting will
|
||||
* be lost.
|
||||
* Re-configure Xenon specific register to enable current SDHC
|
||||
*/
|
||||
ret = xenon_sdhc_prepare(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sdhci_resume_host(host);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(xenon_pmops, xenon_suspend, xenon_resume);
|
||||
#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 = 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);
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
if (ret)
|
||||
goto out;
|
||||
priv->restore_needed = false;
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
Loading…
Reference in New Issue