diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c index 316cfec3f005..48f641953844 100644 --- a/drivers/mmc/host/sdhci-cadence.c +++ b/drivers/mmc/host/sdhci-cadence.c @@ -40,6 +40,7 @@ #define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3 #define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4 #define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5 +#define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6 /* SRS - Slot Register Set (SDHCI-compatible) */ #define SDHCI_CDNS_SRS_BASE 0x200 @@ -64,6 +65,7 @@ struct sdhci_cdns_priv { void __iomem *hrs_addr; + bool enhanced_strobe; }; static void sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv, @@ -108,11 +110,30 @@ static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host) return host->max_clk / 1000; } +static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode) +{ + u32 tmp; + + /* The speed mode for eMMC is selected by HRS06 register */ + tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06); + tmp &= ~SDHCI_CDNS_HRS06_MODE_MASK; + tmp |= mode; + writel(tmp, priv->hrs_addr + SDHCI_CDNS_HRS06); +} + +static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv) +{ + u32 tmp; + + tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06); + return tmp & SDHCI_CDNS_HRS06_MODE_MASK; +} + static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) { struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); - u32 mode, tmp; + u32 mode; switch (timing) { case MMC_TIMING_MMC_HS: @@ -125,18 +146,17 @@ static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host, mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200; break; case MMC_TIMING_MMC_HS400: - mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400; + if (priv->enhanced_strobe) + mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400ES; + else + mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400; break; default: mode = SDHCI_CDNS_HRS06_MODE_SD; break; } - /* The speed mode for eMMC is selected by HRS06 register */ - tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06); - tmp &= ~SDHCI_CDNS_HRS06_MODE_MASK; - tmp |= mode; - writel(tmp, priv->hrs_addr + SDHCI_CDNS_HRS06); + sdhci_cdns_set_emmc_mode(priv, mode); /* For SD, fall back to the default handler */ if (mode == SDHCI_CDNS_HRS06_MODE_SD) @@ -213,6 +233,26 @@ static int sdhci_cdns_execute_tuning(struct mmc_host *mmc, u32 opcode) return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2); } +static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); + u32 mode; + + priv->enhanced_strobe = ios->enhanced_strobe; + + mode = sdhci_cdns_get_emmc_mode(priv); + + if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400 && ios->enhanced_strobe) + sdhci_cdns_set_emmc_mode(priv, + SDHCI_CDNS_HRS06_MODE_MMC_HS400ES); + + if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400ES && !ios->enhanced_strobe) + sdhci_cdns_set_emmc_mode(priv, + SDHCI_CDNS_HRS06_MODE_MMC_HS400); +} + static int sdhci_cdns_probe(struct platform_device *pdev) { struct sdhci_host *host; @@ -240,8 +280,11 @@ static int sdhci_cdns_probe(struct platform_device *pdev) priv = sdhci_cdns_priv(host); priv->hrs_addr = host->ioaddr; + priv->enhanced_strobe = false; host->ioaddr += SDHCI_CDNS_SRS_BASE; host->mmc_host_ops.execute_tuning = sdhci_cdns_execute_tuning; + host->mmc_host_ops.hs400_enhanced_strobe = + sdhci_cdns_hs400_enhanced_strobe; ret = mmc_of_parse(host->mmc); if (ret)