mmc: sdhci-cadence: add HS400 enhanced strobe support

Add support for HS400ES mode to Cadence SDHCI driver.

Signed-off-by: Piotr Sroka <piotrs@cadence.com>
Reviewed-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
Piotr Sroka 2017-03-06 08:28:41 +00:00 committed by Ulf Hansson
parent 4346b7c794
commit d12990f990

View file

@ -40,6 +40,7 @@
#define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3 #define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
#define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4 #define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4
#define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5 #define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
#define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
/* SRS - Slot Register Set (SDHCI-compatible) */ /* SRS - Slot Register Set (SDHCI-compatible) */
#define SDHCI_CDNS_SRS_BASE 0x200 #define SDHCI_CDNS_SRS_BASE 0x200
@ -64,6 +65,7 @@
struct sdhci_cdns_priv { struct sdhci_cdns_priv {
void __iomem *hrs_addr; void __iomem *hrs_addr;
bool enhanced_strobe;
}; };
static void sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv, 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; 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, static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
unsigned int timing) unsigned int timing)
{ {
struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
u32 mode, tmp; u32 mode;
switch (timing) { switch (timing) {
case MMC_TIMING_MMC_HS: 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; mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200;
break; break;
case MMC_TIMING_MMC_HS400: 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; break;
default: default:
mode = SDHCI_CDNS_HRS06_MODE_SD; mode = SDHCI_CDNS_HRS06_MODE_SD;
break; break;
} }
/* The speed mode for eMMC is selected by HRS06 register */ sdhci_cdns_set_emmc_mode(priv, mode);
tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
tmp &= ~SDHCI_CDNS_HRS06_MODE_MASK;
tmp |= mode;
writel(tmp, priv->hrs_addr + SDHCI_CDNS_HRS06);
/* For SD, fall back to the default handler */ /* For SD, fall back to the default handler */
if (mode == SDHCI_CDNS_HRS06_MODE_SD) 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); 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) static int sdhci_cdns_probe(struct platform_device *pdev)
{ {
struct sdhci_host *host; struct sdhci_host *host;
@ -240,8 +280,11 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
priv = sdhci_cdns_priv(host); priv = sdhci_cdns_priv(host);
priv->hrs_addr = host->ioaddr; priv->hrs_addr = host->ioaddr;
priv->enhanced_strobe = false;
host->ioaddr += SDHCI_CDNS_SRS_BASE; host->ioaddr += SDHCI_CDNS_SRS_BASE;
host->mmc_host_ops.execute_tuning = sdhci_cdns_execute_tuning; 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); ret = mmc_of_parse(host->mmc);
if (ret) if (ret)