]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
mmc: sdhci: tegra: runtime pm enable through DT
authorBitan Biswas <bbiswas@nvidia.com>
Wed, 24 Dec 2014 05:23:46 +0000 (10:53 +0530)
committerBitan Biswas <bbiswas@nvidia.com>
Sat, 7 Feb 2015 10:39:09 +0000 (02:39 -0800)
sdhci tegra support for runtime pm enable
through DT

bug 200023075

Change-Id: I4b9eb2d34b39011fb14bab5648740cc02fc5ee79
Signed-off-by: Bitan Biswas <bbiswas@nvidia.com>
Reviewed-on: http://git-master/r/667459

drivers/mmc/host/sdhci-tegra.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
include/linux/mmc/sdhci.h
include/linux/platform_data/mmc-sdhci-tegra.h

index 0f2401f8560370a2a9efea540527df88c101c3a4..ee94f3400b54c84dcbffedaadab0d26111dee059 100644 (file)
 #define TAP_CMD_TRIM_DEFAULT_VOLTAGE   1
 #define TAP_CMD_TRIM_HIGH_VOLTAGE      2
 
-/* Some boards show reset during boot if RTPM TMOUT is 10msec */
-#define MMC_RTPM_MSEC_TMOUT 20
-
 /* Max number of clock parents for sdhci is fixed to 2 */
 #define TEGRA_SDHCI_MAX_PLL_SOURCE 2
 /*
@@ -1739,15 +1736,15 @@ static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
        struct sdhci_tegra *tegra_host = pltfm_host->priv;
-#if !defined(CONFIG_MMC_RTPM)
        struct platform_device *pdev = to_platform_device(mmc_dev(sdhci->mmc));
-#endif
+       struct tegra_sdhci_platform_data *plat;
        u8 ctrl;
        int ret = 0;
 
        mutex_lock(&tegra_host->set_clock_mutex);
        pr_debug("%s %s %u enabled=%u\n", __func__,
                mmc_hostname(sdhci->mmc), clock, tegra_host->clk_enabled);
+       plat = pdev->dev.platform_data;
        if (clock) {
                if (!tegra_host->clk_enabled) {
                        ret = clk_prepare_enable(pltfm_host->clk);
@@ -1757,9 +1754,11 @@ static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock)
                                mutex_unlock(&tegra_host->set_clock_mutex);
                                return;
                        }
-#ifndef CONFIG_MMC_RTPM
-                       pm_runtime_get_sync(&pdev->dev);
-#endif
+                       if (sdhci->runtime_pm_init_done &&
+                               IS_RTPM_DELAY_CG(plat->rtpm_type)) {
+                               sdhci->runtime_pm_enable_dcg = true;
+                               pm_runtime_get_sync(&pdev->dev);
+                       }
                        tegra_host->clk_enabled = true;
                        sdhci->is_clk_on = true;
                        ctrl = sdhci_readb(sdhci, SDHCI_VNDR_CLK_CTRL);
@@ -1813,9 +1812,12 @@ static void tegra_sdhci_set_clock(struct sdhci_host *sdhci, unsigned int clock)
 
                tegra_host->clk_enabled = false;
                sdhci->is_clk_on = false;
-#ifndef CONFIG_MMC_RTPM
-               pm_runtime_put_sync(&pdev->dev);
-#endif
+               if (sdhci->runtime_pm_init_done &&
+                       sdhci->runtime_pm_enable_dcg &&
+                       IS_RTPM_DELAY_CG(plat->rtpm_type)) {
+                       sdhci->runtime_pm_enable_dcg = false;
+                       pm_runtime_put_sync(&pdev->dev);
+               }
                clk_disable_unprepare(pltfm_host->clk);
        }
        mutex_unlock(&tegra_host->set_clock_mutex);
@@ -4122,8 +4124,11 @@ static int show_disableclkgating_value(void *data, u64 *value)
 static int set_disableclkgating_value(void *data, u64 value)
 {
        struct sdhci_host *host = (struct sdhci_host *)data;
+       struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
+       struct tegra_sdhci_platform_data *plat;
        if (host != NULL) {
                struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+               plat = pdev->dev.platform_data;
                if (pltfm_host != NULL) {
                        struct sdhci_tegra *tegra_host = pltfm_host->priv;
                        /* Set the CAPS2 register to reflect
@@ -4134,12 +4139,14 @@ static int set_disableclkgating_value(void *data, u64 value)
                                        host->mmc->ops->set_ios(host->mmc,
                                                &host->mmc->ios);
                                        tegra_host->dbg_cfg.clk_ungated = true;
-                                       host->mmc->caps2 &=
-                                               ~MMC_CAP2_CLOCK_GATING;
+                                       if (IS_RTPM_DELAY_CG(plat->rtpm_type))
+                                               host->mmc->caps2 &=
+                                                       ~MMC_CAP2_CLOCK_GATING;
                                } else {
                                        tegra_host->dbg_cfg.clk_ungated = false;
-                                       host->mmc->caps2 |=
-                                               MMC_CAP2_CLOCK_GATING;
+                                       if (IS_RTPM_DELAY_CG(plat->rtpm_type))
+                                               host->mmc->caps2 |=
+                                                       MMC_CAP2_CLOCK_GATING;
                                }
                        }
                }
@@ -4743,6 +4750,7 @@ static struct tegra_sdhci_platform_data *sdhci_tegra_dt_parse_pdata(
                                                struct platform_device *pdev)
 {
        int val;
+       int ret;
        struct tegra_sdhci_platform_data *plat;
        struct device_node *np = pdev->dev.of_node;
        u32 bus_width;
@@ -4834,6 +4842,11 @@ static struct tegra_sdhci_platform_data *sdhci_tegra_dt_parse_pdata(
        }
        plat->enable_autocal_slew_override = of_property_read_bool(np,
                                        "nvidia,auto-cal-slew-override");
+       ret = of_property_read_u32(np, "nvidia,runtime-pm-type",
+               &plat->rtpm_type);
+       /* use delayed clock gate if runtime type not specified explicitly */
+       if (ret < 0)
+               plat->rtpm_type = RTPM_TYPE_DELAY_CG;
 
        return plat;
 }
@@ -5053,9 +5066,6 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
 
        host = sdhci_pltfm_init(pdev, soc_data->pdata);
 
-       /* sdio delayed clock gate quirk in sdhci_host used */
-       host->quirks2 |= SDHCI_QUIRK2_DELAYED_CLK_GATE;
-
        if (IS_ERR(host))
                return PTR_ERR(host);
 
@@ -5065,12 +5075,14 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
 
        if (plat == NULL) {
                plat = sdhci_tegra_dt_parse_pdata(pdev);
-               pr_debug("%s: %s line=%d disable-clock-gate=%d\n",
-                       mmc_hostname(host->mmc), __func__,
-                       __LINE__, plat->disable_clock_gate);
+               pr_info("%s: %s line=%d runtime pm type=%s, disable-clock-gate=%d\n",
+                       mmc_hostname(host->mmc), __func__, __LINE__,
+                       GET_RTPM_TYPE(plat->rtpm_type),
+                       plat->disable_clock_gate);
        } else {
                pr_err("%s using board files instead of DT\n",
                        mmc_hostname(host->mmc));
+               plat->rtpm_type = RTPM_TYPE_DELAY_CG;
        }
        if (plat == NULL) {
                dev_err(mmc_dev(host->mmc), "missing platform data\n");
@@ -5078,6 +5090,12 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
                goto err_no_plat;
        }
 
+       /* sdio delayed clock gate quirk in sdhci_host used */
+       if (IS_RTPM_DELAY_CG(plat->rtpm_type))
+               host->quirks2 |= SDHCI_QUIRK2_DELAYED_CLK_GATE;
+       if (IS_MMC_RTPM(plat->rtpm_type))
+               host->quirks2 |= SDHCI_QUIRK2_MMC_RTPM;
+
        if (sdhci_tegra_check_bondout(plat->id)) {
                dev_err(mmc_dev(host->mmc), "bonded out\n");
                rc = -ENODEV;
@@ -5274,18 +5292,6 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
        }
 
        tegra_pd_add_device(&pdev->dev);
-       pm_runtime_enable(&pdev->dev);
-       pm_runtime_use_autosuspend(&pdev->dev);
-#ifdef CONFIG_MMC_RTPM
-       /*
-        * Below Autosuspend delay can be increased/decreased based on
-        * power and perf data
-        */
-       if (host->quirks2 & SDHCI_QUIRK2_MMC_RTPM)
-               pm_runtime_set_autosuspend_delay(&pdev->dev,
-                       MMC_RTPM_MSEC_TMOUT);
-#endif
-
        /* Get the ddr clock */
        tegra_host->ddr_clk = clk_get(mmc_dev(host->mmc), "ddr");
        if (IS_ERR(tegra_host->ddr_clk)) {
@@ -5318,9 +5324,6 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
        if (clk_get_parent(pltfm_host->clk) == tegra_host->pll_source[0].pll)
                tegra_host->is_parent_pll_source_1 = true;
 
-#if !defined(CONFIG_MMC_RTPM)
-       pm_runtime_get_sync(&pdev->dev);
-#endif
        rc = clk_prepare_enable(pltfm_host->clk);
        if (rc != 0)
                goto err_clk_put;
@@ -5423,22 +5426,11 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
        if (plat->en_freq_scaling && (plat->max_clk_limit > low_freq))
                host->mmc->caps2 |= MMC_CAP2_FREQ_SCALING;
 
-#ifdef CONFIG_MMC_RTPM
-       /* MMC runtime PM clock gate has preference over delayed clock gate */
-       /* If precedence changes SDHCI_QUIRK2_MMC_RTPM quirk2 needs update */
-       host->quirks2 |= SDHCI_QUIRK2_MMC_RTPM;
-       if (!plat->disable_clock_gate) {
-               pr_debug("Force disable delayed clock gate since MMC RTPM enabled\n");
-               plat->disable_clock_gate = true;
-       }
-#endif
-
-       if (!plat->disable_clock_gate)
-               host->mmc->caps2 |= MMC_CAP2_CLOCK_GATING;
-
        if (plat->pwr_off_during_lp0)
                host->mmc->caps2 |= MMC_CAP2_NO_SLEEP_CMD;
 
+       if (IS_RTPM_DELAY_CG(plat->rtpm_type) && (!plat->disable_clock_gate))
+               host->mmc->caps2 |= MMC_CAP2_CLOCK_GATING;
        tegra_host->nominal_vcore_mv =
                tegra_dvfs_get_core_nominal_millivolts();
        tegra_host->min_vcore_override_mv =
@@ -5501,9 +5493,6 @@ err_add_host:
        else
                clk_disable_unprepare(tegra_host->sdr_clk);
 
-#if !defined(CONFIG_MMC_RTPM)
-       pm_runtime_put_sync(&pdev->dev);
-#endif
 err_clk_put:
        if (tegra_host->ddr_clk)
                clk_put(tegra_host->ddr_clk);
@@ -5561,9 +5550,6 @@ static int sdhci_tegra_remove(struct platform_device *pdev)
                        clk_disable_unprepare(tegra_host->ddr_clk);
                else
                        clk_disable_unprepare(tegra_host->sdr_clk);
-#if !defined(CONFIG_MMC_RTPM)
-               pm_runtime_put_sync(&pdev->dev);
-#endif
        }
 
        if (tegra_host->ddr_clk)
@@ -5585,11 +5571,12 @@ static int sdhci_tegra_remove(struct platform_device *pdev)
 
 static void sdhci_tegra_shutdown(struct platform_device *pdev)
 {
+#ifdef CONFIG_MMC_RTPM
        struct sdhci_host *host = platform_get_drvdata(pdev);
        dev_dbg(&pdev->dev, " %s shutting down\n",
                mmc_hostname(host->mmc));
-#ifdef CONFIG_MMC_RTPM
-       pm_runtime_forbid(&pdev->dev);
+       /* applies to delayed clock gate RTPM and MMC RTPM cases */
+       sdhci_runtime_forbid(host);
 #endif
 }
 
index 73aa96c3eabfa2b576c4b487e51cef73e0156f24..6e2954dace5e9ee66aac895a70e42eec230d9276 100644 (file)
@@ -2,7 +2,7 @@
  *  linux/drivers/mmc/host/sdhci.c - Secure Digital Host Controller Interface driver
  *
  *  Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
- *  Copyright (C) 2012-2014, NVIDIA CORPORATION.  All rights reserved.
+ *  Copyright (C) 2012-2015, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -46,7 +46,6 @@
 #include "sdhci.h"
 
 #define DRIVER_NAME "sdhci"
-#define DEFAULT_SDHOST_FREQ     50000000
 
 #define DBG(f, x...) \
        pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x)
@@ -95,6 +94,9 @@ void dbg_add_host_log(struct mmc_host *host, int type, int cmd, int arg)
 }
 #endif
 
+/* MMC_RTPM timeout */
+#define MMC_RTPM_MSEC_TMOUT 10
+
 /* SDIO 1msec timeout, but use 10msec timeout for HZ=100 */
 #define SDIO_CLK_GATING_TICK_TMOUT ((HZ >= 1000) ? (HZ / 1000) : 1)
 /* 20msec EMMC delayed clock gate timeout */
@@ -3649,14 +3651,6 @@ int sdhci_resume_host(struct sdhci_host *host)
                disable_irq_wake(host->irq);
        }
 
-#if defined(CONFIG_MMC_RTPM)
-       if (!host->clock || !host->mmc->ios.clock) {
-               if (host->ops->set_clock)
-                       host->ops->set_clock(host, DEFAULT_SDHOST_FREQ);
-               sdhci_set_clock(host, DEFAULT_SDHOST_FREQ);
-       }
-#endif
-
        if ((host->mmc->pm_flags & MMC_PM_KEEP_POWER) &&
            (host->quirks2 & SDHCI_QUIRK2_HOST_OFF_CARD_ON)) {
                /* Card keeps power but host controller does not */
@@ -4612,6 +4606,19 @@ out_dma_alloc:
                (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
 
        sdhci_enable_card_detection(host);
+
+       pm_runtime_enable(mmc_dev(mmc));
+       pm_runtime_use_autosuspend(mmc_dev(mmc));
+       if (host->quirks2 & SDHCI_QUIRK2_MMC_RTPM) {
+               /*
+                * Below Autosuspend delay can be increased/decreased based on
+                * power and perf data
+                */
+               pm_runtime_set_autosuspend_delay(mmc_dev(mmc),
+                       MMC_RTPM_MSEC_TMOUT);
+       }
+       host->runtime_pm_init_done = true;
+
 #ifdef CONFIG_DEBUG_FS
        /* Add debugfs nodes */
        sdhci_debugfs_init(host);
@@ -4636,6 +4643,12 @@ untasklet:
 
 EXPORT_SYMBOL_GPL(sdhci_add_host);
 
+void sdhci_runtime_forbid(struct sdhci_host *host)
+{
+       pm_runtime_forbid(mmc_dev(host->mmc));
+}
+EXPORT_SYMBOL_GPL(sdhci_runtime_forbid);
+
 void sdhci_remove_host(struct sdhci_host *host, int dead)
 {
        unsigned long flags;
index 90cc9f0daae7e21365ec8dbc8bc0267701159c4b..817fe7a6d2f069fcbdeed8657d164e3af98e8afa 100644 (file)
@@ -4,7 +4,7 @@
  * Header file for Host Controller registers and I/O accessors.
  *
  *  Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
- *  Copyright (c) 2011-2014, NVIDIA CORPORATION. All Rights Reserved.
+ *  Copyright (c) 2011-2015, NVIDIA CORPORATION. All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -514,6 +514,7 @@ static inline void *sdhci_priv(struct sdhci_host *host)
 
 extern void sdhci_card_detect(struct sdhci_host *host);
 extern int sdhci_add_host(struct sdhci_host *host);
+extern void sdhci_runtime_forbid(struct sdhci_host *host);
 extern void sdhci_remove_host(struct sdhci_host *host, int dead);
 
 #ifdef CONFIG_PM
index 45a8d9101b160aadc4f1119920fe53290efc4b17..93e9c136ad0e4a1e82eccfc4d43d2434ffc664e8 100644 (file)
@@ -254,6 +254,8 @@ struct sdhci_host {
 
        struct delayed_work     delayed_clk_gate_wrk;
        bool                    is_clk_on;
+       bool                    runtime_pm_init_done;
+       bool                    runtime_pm_enable_dcg;
 #ifdef CONFIG_DEBUG_FS
        unsigned int            enable_sdhci_perf_stats;
 #endif
index 803195e43a7308ee4a65861c05baa4beed205a4c..59943a3ba751b6158673fee07924f4374edbba66 100644 (file)
 #define MMC_MASK_HS200         0x20
 #define MMC_MASK_HS400         0x40
 
+/* runtime power management implementation type */
+enum {
+       RTPM_TYPE_DELAY_CG = 0,
+       RTPM_TYPE_MMC
+};
+
+#define IS_MMC_RTPM(type)      (type == RTPM_TYPE_MMC)
+#define IS_RTPM_DELAY_CG(type) (type == RTPM_TYPE_DELAY_CG)
+#define GET_RTPM_TYPE(type) \
+               (IS_RTPM_DELAY_CG(type) ? "delayed clock gate rtpm" : \
+               "mmc rtpm coupled with clock gate")
+
 struct tegra_sdhci_platform_data {
        bool pwrdet_support;
        int cd_gpio;
@@ -90,6 +102,7 @@ struct tegra_sdhci_platform_data {
        /*Index 0 is fixed for ID mode. Rest according the MMC_TIMINGS modes*/
        unsigned int fixed_clk_freq_table[MMC_TIMINGS_MAX_MODES + 1];
        bool enable_autocal_slew_override;
+       unsigned int rtpm_type;
 };
 
 #endif