summaryrefslogtreecommitdiff
path: root/drivers/mmc/host/sdhci-tegra.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/sdhci-tegra.c')
-rw-r--r--drivers/mmc/host/sdhci-tegra.c37
1 files changed, 29 insertions, 8 deletions
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 970d38f68939..908b23e6a03c 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -210,9 +210,24 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
if (!clock)
return sdhci_set_clock(host, clock);
+ /*
+ * In DDR50/52 modes the Tegra SDHCI controllers require the SDHCI
+ * divider to be configured to divided the host clock by two. The SDHCI
+ * clock divider is calculated as part of sdhci_set_clock() by
+ * sdhci_calc_clk(). The divider is calculated from host->max_clk and
+ * the requested clock rate.
+ *
+ * By setting the host->max_clk to clock * 2 the divider calculation
+ * will always result in the correct value for DDR50/52 modes,
+ * regardless of clock rate rounding, which may happen if the value
+ * from clk_get_rate() is used.
+ */
host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
clk_set_rate(pltfm_host->clk, host_clk);
- host->max_clk = clk_get_rate(pltfm_host->clk);
+ if (tegra_host->ddr_signaling)
+ host->max_clk = host_clk;
+ else
+ host->max_clk = clk_get_rate(pltfm_host->clk);
sdhci_set_clock(host, clock);
@@ -228,7 +243,8 @@ static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
- if (timing == MMC_TIMING_UHS_DDR50)
+ if (timing == MMC_TIMING_UHS_DDR50 ||
+ timing == MMC_TIMING_MMC_DDR52)
tegra_host->ddr_signaling = true;
sdhci_set_uhs_signaling(host, timing);
@@ -238,11 +254,7 @@ static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- /*
- * DDR modes require the host to run at double the card frequency, so
- * the maximum rate we can support is half of the module input clock.
- */
- return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2;
+ return clk_round_rate(pltfm_host->clk, UINT_MAX);
}
static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap)
@@ -334,7 +346,16 @@ static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_BROKEN_HS200 |
+ /*
+ * Auto-CMD23 leads to "Got command interrupt 0x00010000 even
+ * though no command operation was in progress."
+ *
+ * The exact reason is unknown, as the same hardware seems
+ * to support Auto CMD23 on a downstream 3.1 kernel.
+ */
+ SDHCI_QUIRK2_ACMD23_BROKEN,
.ops = &tegra_sdhci_ops,
};