diff options
author | Pavan Kunapuli <pkunapuli@nvidia.com> | 2013-10-24 19:03:45 +0530 |
---|---|---|
committer | Harry Hong <hhong@nvidia.com> | 2013-12-08 16:37:13 -0800 |
commit | 1753207a0133dd0839d73ec96a45d6a7c46a6003 (patch) | |
tree | 8b35f8282cbe76b5b62a86b9272236dc7c2ec8ec /drivers | |
parent | cb2ae3ffb426802a46ce928024a52ab0ec3a6392 (diff) |
mmc: host: sdhci: delayed clock gate support
Aggressive clock gate degrades sdhci performance.
Hence, sdhci clock gate is delayed.
- sdhci clock gate is done if no further
transaction starts within 20msec interval
- delayed clock gate only supported for EMMC
in this patch
- switch is set as delayed clock gate off
bug 1372006
Change-Id: I9672cbd643dfb45192062dc827275daacc813cc5
Signed-off-by: Pavan Kunapuli <pkunapuli@nvidia.com>
Signed-off-by: Bitan Biswas <bbiswas@nvidia.com>
Reviewed-on: http://git-master/r/335081
(cherry picked from commit 378d42be0224ed9f57c77ee57997786c5e8e218f)
Reviewed-on: http://git-master/r/339098
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Harry Hong <hhong@nvidia.com>
Tested-by: Harry Hong <hhong@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/host/sdhci-tegra.c | 6 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.c | 51 |
2 files changed, 52 insertions, 5 deletions
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 85eab572adad..8055817bd00b 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -1172,6 +1172,7 @@ static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock) pm_runtime_put_sync(&pdev->dev); tegra_host->clk_enabled = false; } + sdhci->is_clk_on = tegra_host->clk_enabled; } static void tegra_sdhci_do_calibration(struct sdhci_host *sdhci) { @@ -2809,6 +2810,7 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) } host = sdhci_pltfm_init(pdev, soc_data->pdata); + if (IS_ERR(host)) return PTR_ERR(host); @@ -3031,6 +3033,7 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) pltfm_host->priv = tegra_host; tegra_host->clk_enabled = true; + host->is_clk_on = tegra_host->clk_enabled; tegra_host->max_clk_limit = plat->max_clk_limit; tegra_host->ddr_clk_limit = plat->ddr_clk_limit; tegra_host->sd_detect_in_suspend = plat->sd_detect_in_suspend; @@ -3098,6 +3101,9 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) rc = sdhci_add_host(host); device_create_file(&pdev->dev, &dev_attr_cmd_state); + + INIT_DELAYED_WORK(&host->delayed_clk_gate_wrk, delayed_clk_gate_cb); + sdhci_tegra_error_stats_debugfs(host); if (rc) goto err_add_host; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index ee0602f8af4c..cafcad2a894c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -47,6 +47,14 @@ #define MAX_TUNING_LOOP 40 +#define DELAYED_CLK_GATING_TICK_TMOUT (HZ / 50) + +#define IS_DELAYED_CLK_GATE(host) \ + ((host->quirks2 & SDHCI_QUIRK2_DELAYED_CLK_GATE) && \ + (host->mmc->card && ( \ + (host->mmc->card->type == MMC_TYPE_MMC) /* EMMC */ \ + ))) + static unsigned int debug_quirks = 0; static unsigned int debug_quirks2; @@ -2040,6 +2048,11 @@ int sdhci_enable(struct mmc_host *mmc) if (!mmc->card || mmc->card->type == MMC_TYPE_SDIO) return 0; + if (IS_DELAYED_CLK_GATE(host)) { + /* cancel delayed clk gate work */ + cancel_delayed_work_sync(&host->delayed_clk_gate_wrk); + } + if (mmc->ios.clock) { if (host->ops->set_clock) host->ops->set_clock(host, mmc->ios.clock); @@ -2056,14 +2069,11 @@ int sdhci_enable(struct mmc_host *mmc) return 0; } -int sdhci_disable(struct mmc_host *mmc) +static void mmc_host_clk_gate(struct sdhci_host *host) { - struct sdhci_host *host = mmc_priv(mmc); int ret; struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); - - if (!mmc->card || mmc->card->type == MMC_TYPE_SDIO) - return 0; + struct mmc_host *mmc = host->mmc; sdhci_set_clock(host, 0); if (host->ops->set_clock) @@ -2075,6 +2085,37 @@ int sdhci_disable(struct mmc_host *mmc) if (ret) dev_err(&pdev->dev, "Unable to set SD_EDP_LOW state\n"); } + return; +} + +void delayed_clk_gate_cb(struct work_struct *work) +{ + struct sdhci_host *host = container_of(work, struct sdhci_host, + delayed_clk_gate_wrk.work); + /* power off check */ + if (host->mmc->ios.power_mode == MMC_POWER_OFF) + goto end; + + mmc_host_clk_gate(host); +end: + return; +} +EXPORT_SYMBOL_GPL(delayed_clk_gate_cb); + +int sdhci_disable(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + if (!mmc->card || mmc->card->type == MMC_TYPE_SDIO) + return 0; + + if (IS_DELAYED_CLK_GATE(host)) { + schedule_delayed_work(&host->delayed_clk_gate_wrk, + DELAYED_CLK_GATING_TICK_TMOUT); + return 0; + } + + mmc_host_clk_gate(host); return 0; } |