mmc: tegra: Set eMMC DDR clock based on emc clock
Pavan Kunapuli [Mon, 7 May 2012 12:13:25 +0000 (17:13 +0530)]
Set the eMMC ddr mode clock dynamically based on emc
clock rate. If ddr clock limit is specified and the emc
clock is less than max emc freq, then limit emmc ddr
clk. If not, set the max eMMC ddr clock.

Bug 967719

Change-Id: I9f70077c4ac4bb1f3e6d894fcb8420b1aba284dd
Signed-off-by: Pavan Kunapuli <pkunapuli@nvidia.com>
Reviewed-on: http://git-master/r/100579
Reviewed-by: Simone Willett <swillett@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>

drivers/mmc/host/sdhci-tegra.c

index a294de1..9bbec43 100644 (file)
@@ -112,9 +112,13 @@ struct tegra_sdhci_host {
        unsigned int vddio_max_uv;
        /* max clk supported by the platform */
        unsigned int max_clk_limit;
+       /* max ddr clk supported by the platform */
+       unsigned int ddr_clk_limit;
        struct tegra_io_dpd *dpd;
        bool card_present;
        bool is_rail_enabled;
+       struct clk *emc_clk;
+       unsigned int emc_max_clk;
 };
 
 static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
@@ -366,6 +370,7 @@ static void tegra_sdhci_set_clk_rate(struct sdhci_host *sdhci,
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
        struct tegra_sdhci_host *tegra_host = pltfm_host->priv;
        unsigned int clk_rate;
+       unsigned int emc_clk;
 
        /*
         * In SDR50 mode, run the sdmmc controller at freq greater than
@@ -379,7 +384,16 @@ static void tegra_sdhci_set_clk_rate(struct sdhci_host *sdhci,
                 * In ddr mode, tegra sdmmc controller clock frequency
                 * should be double the card clock frequency.
                 */
-                clk_rate = clock * 2;
+               if (tegra_host->ddr_clk_limit) {
+                       clk_rate = tegra_host->ddr_clk_limit * 2;
+                       if (tegra_host->emc_clk) {
+                               emc_clk = clk_get_rate(tegra_host->emc_clk);
+                               if (emc_clk == tegra_host->emc_max_clk)
+                                       clk_rate = clock * 2;
+                       }
+               } else {
+                       clk_rate = clock * 2;
+               }
        } else {
                if (clock <= tegra_sdhost_min_freq)
                        clk_rate = tegra_sdhost_min_freq;
@@ -1072,10 +1086,23 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
        rc = clk_enable(clk);
        if (rc != 0)
                goto err_clk_put;
+
+       if (!strcmp(dev_name(mmc_dev(host->mmc)), "sdhci-tegra.3")) {
+               tegra_host->emc_clk = clk_get(mmc_dev(host->mmc), "emc");
+               if (IS_ERR(tegra_host->emc_clk)) {
+                       dev_err(mmc_dev(host->mmc), "clk err\n");
+                       rc = PTR_ERR(tegra_host->emc_clk);
+                       goto err_clk_put;
+               }
+               tegra_host->emc_max_clk =
+                       clk_round_rate(tegra_host->emc_clk, ULONG_MAX);
+       }
+
        pltfm_host->clk = clk;
        pltfm_host->priv = tegra_host;
        tegra_host->clk_enabled = true;
        tegra_host->max_clk_limit = plat->max_clk_limit;
+       tegra_host->ddr_clk_limit = plat->ddr_clk_limit;
        tegra_host->instance = pdev->id;
        tegra_host->dpd = tegra_io_dpd_get(mmc_dev(host->mmc));
 
@@ -1117,6 +1144,7 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
        return 0;
 
 err_add_host:
+       clk_put(tegra_host->emc_clk);
        clk_disable(pltfm_host->clk);
 err_clk_put:
        clk_put(pltfm_host->clk);