#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
/*
{
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);
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);
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);
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
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;
}
}
}
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;
}
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;
}
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);
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");
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;
}
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)) {
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;
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 =
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);
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)
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
}
* 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
#include "sdhci.h"
#define DRIVER_NAME "sdhci"
-#define DEFAULT_SDHOST_FREQ 50000000
#define DBG(f, x...) \
pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x)
}
#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 */
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 */
(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);
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;