Commit d618978d authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Thierry Reding

mmc: sdhci-tegra: Add runtime PM and OPP support

The SDHCI on Tegra belongs to the core power domain and we're going to
enable GENPD support for the core domain. Now SDHCI must be resumed using
runtime PM API in order to initialize the SDHCI power state. The SDHCI
clock rate must be changed using OPP API that will reconfigure the power
domain performance state in accordance to the rate. Add runtime PM and OPP
support to the SDHCI driver.
Reviewed-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
Signed-off-by: default avatarDmitry Osipenko <digetx@gmail.com>
Acked-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent 3da9b0fe
......@@ -15,6 +15,8 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/mmc/card.h>
......@@ -24,6 +26,8 @@
#include <linux/gpio/consumer.h>
#include <linux/ktime.h>
#include <soc/tegra/common.h>
#include "sdhci-pltfm.h"
#include "cqhci.h"
......@@ -760,7 +764,9 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
struct device *dev = mmc_dev(host->mmc);
unsigned long host_clk;
int err;
if (!clock)
return sdhci_set_clock(host, clock);
......@@ -778,7 +784,12 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
* from clk_get_rate() is used.
*/
host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
clk_set_rate(pltfm_host->clk, host_clk);
err = dev_pm_opp_set_rate(dev, host_clk);
if (err)
dev_err(dev, "failed to set clk rate to %luHz: %d\n",
host_clk, err);
tegra_host->curr_clk_rate = host_clk;
if (tegra_host->ddr_signaling)
host->max_clk = host_clk;
......@@ -1705,7 +1716,6 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
"failed to get clock\n");
goto err_clk_get;
}
clk_prepare_enable(clk);
pltfm_host->clk = clk;
tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev,
......@@ -1716,15 +1726,24 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
goto err_rst_get;
}
rc = reset_control_assert(tegra_host->rst);
rc = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
if (rc)
goto err_rst_get;
pm_runtime_enable(&pdev->dev);
rc = pm_runtime_resume_and_get(&pdev->dev);
if (rc)
goto err_pm_get;
rc = reset_control_assert(tegra_host->rst);
if (rc)
goto err_rst_assert;
usleep_range(2000, 4000);
rc = reset_control_deassert(tegra_host->rst);
if (rc)
goto err_rst_get;
goto err_rst_assert;
usleep_range(2000, 4000);
......@@ -1736,8 +1755,11 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
err_add_host:
reset_control_assert(tegra_host->rst);
err_rst_assert:
pm_runtime_put_sync_suspend(&pdev->dev);
err_pm_get:
pm_runtime_disable(&pdev->dev);
err_rst_get:
clk_disable_unprepare(pltfm_host->clk);
err_clk_get:
clk_disable_unprepare(tegra_host->tmclk);
err_power_req:
......@@ -1756,19 +1778,38 @@ static int sdhci_tegra_remove(struct platform_device *pdev)
reset_control_assert(tegra_host->rst);
usleep_range(2000, 4000);
clk_disable_unprepare(pltfm_host->clk);
clk_disable_unprepare(tegra_host->tmclk);
pm_runtime_put_sync_suspend(&pdev->dev);
pm_runtime_force_suspend(&pdev->dev);
clk_disable_unprepare(tegra_host->tmclk);
sdhci_pltfm_free(pdev);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
static int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
clk_disable_unprepare(pltfm_host->clk);
return 0;
}
static int __maybe_unused sdhci_tegra_runtime_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
return clk_prepare_enable(pltfm_host->clk);
}
#ifdef CONFIG_PM_SLEEP
static int sdhci_tegra_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
int ret;
if (host->mmc->caps2 & MMC_CAP2_CQE) {
......@@ -1783,17 +1824,22 @@ static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
return ret;
}
clk_disable_unprepare(pltfm_host->clk);
ret = pm_runtime_force_suspend(dev);
if (ret) {
sdhci_resume_host(host);
cqhci_resume(host->mmc);
return ret;
}
return 0;
}
static int __maybe_unused sdhci_tegra_resume(struct device *dev)
static int sdhci_tegra_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);
ret = pm_runtime_force_resume(dev);
if (ret)
return ret;
......@@ -1812,13 +1858,16 @@ static int __maybe_unused sdhci_tegra_resume(struct device *dev)
suspend_host:
sdhci_suspend_host(host);
disable_clk:
clk_disable_unprepare(pltfm_host->clk);
pm_runtime_force_suspend(dev);
return ret;
}
#endif
static SIMPLE_DEV_PM_OPS(sdhci_tegra_dev_pm_ops, sdhci_tegra_suspend,
sdhci_tegra_resume);
static const struct dev_pm_ops sdhci_tegra_dev_pm_ops = {
SET_RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend, sdhci_tegra_runtime_resume,
NULL)
SET_SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume)
};
static struct platform_driver sdhci_tegra_driver = {
.driver = {
......
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