/* Tuning Block Control Register */
#define ESDHC_TBCTL 0x120
+#define ESDHC_HS400_WNDW_ADJUST 0x00000040
+#define ESDHC_HS400_MODE 0x00000010
#define ESDHC_TB_EN 0x00000004
#define ESDHC_TBPTR 0x128
+/* SD Clock Control Register */
+#define ESDHC_SDCLKCTL 0x144
+#define ESDHC_LPBK_CLK_SEL 0x80000000
+#define ESDHC_CMD_CLK_CTL 0x00008000
+
+/* SD Timing Control Register */
+#define ESDHC_SDTIMNGCTL 0x148
+#define ESDHC_FLW_CTL_BG 0x00008000
+
+/* DLL Config 0 Register */
+#define ESDHC_DLLCFG0 0x160
+#define ESDHC_DLL_ENABLE 0x80000000
+#define ESDHC_DLL_FREQ_SEL 0x08000000
+
+/* DLL Status 0 Register */
+#define ESDHC_DLLSTAT0 0x170
+#define ESDHC_DLL_STS_SLV_LOCK 0x08000000
+
/* Control Register for DMA transfer */
#define ESDHC_DMA_SYSCTL 0x40c
#define ESDHC_PERIPHERAL_CLK_SEL 0x00080000
| (pre_div << ESDHC_PREDIV_SHIFT));
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
+ if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 &&
+ clock == MMC_HS200_MAX_DTR) {
+ temp = sdhci_readl(host, ESDHC_TBCTL);
+ sdhci_writel(host, temp | ESDHC_HS400_MODE, ESDHC_TBCTL);
+ temp = sdhci_readl(host, ESDHC_SDCLKCTL);
+ sdhci_writel(host, temp | ESDHC_CMD_CLK_CTL, ESDHC_SDCLKCTL);
+ esdhc_clock_enable(host, true);
+
+ temp = sdhci_readl(host, ESDHC_DLLCFG0);
+ temp |= ESDHC_DLL_ENABLE | ESDHC_DLL_FREQ_SEL;
+ sdhci_writel(host, temp, ESDHC_DLLCFG0);
+ temp = sdhci_readl(host, ESDHC_TBCTL);
+ sdhci_writel(host, temp | ESDHC_HS400_WNDW_ADJUST, ESDHC_TBCTL);
+
+ esdhc_clock_enable(host, false);
+ temp = sdhci_readl(host, ESDHC_DMA_SYSCTL);
+ temp |= ESDHC_FLUSH_ASYNC_FIFO;
+ sdhci_writel(host, temp, ESDHC_DMA_SYSCTL);
+ }
+
/* Wait max 20 ms */
timeout = ktime_add_ms(ktime_get(), 20);
while (!(sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)) {
udelay(10);
}
+ temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp |= ESDHC_CLOCK_SDCLKEN;
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
}
{ },
};
-static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+static void esdhc_tuning_block_enable(struct sdhci_host *host, bool enable)
{
- struct sdhci_host *host = mmc_priv(mmc);
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
u32 val;
- /* Use tuning block for tuning procedure */
esdhc_clock_enable(host, false);
+
val = sdhci_readl(host, ESDHC_DMA_SYSCTL);
val |= ESDHC_FLUSH_ASYNC_FIFO;
sdhci_writel(host, val, ESDHC_DMA_SYSCTL);
val = sdhci_readl(host, ESDHC_TBCTL);
- val |= ESDHC_TB_EN;
+ if (enable)
+ val |= ESDHC_TB_EN;
+ else
+ val &= ~ESDHC_TB_EN;
sdhci_writel(host, val, ESDHC_TBCTL);
+
esdhc_clock_enable(host, true);
+}
+
+static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
+ bool hs400_tuning;
+ u32 val;
+ int ret;
+
+ esdhc_tuning_block_enable(host, true);
+
+ hs400_tuning = host->flags & SDHCI_HS400_TUNING;
+ ret = sdhci_execute_tuning(mmc, opcode);
+
+ if (hs400_tuning) {
+ val = sdhci_readl(host, ESDHC_SDTIMNGCTL);
+ val |= ESDHC_FLW_CTL_BG;
+ sdhci_writel(host, val, ESDHC_SDTIMNGCTL);
+ }
- sdhci_execute_tuning(mmc, opcode);
if (host->tuning_err == -EAGAIN && esdhc->quirk_fixup_tuning) {
/* program TBPTR[TB_WNDW_END_PTR] = 3*DIV_RATIO and
sdhci_writel(host, val, ESDHC_TBCTL);
sdhci_execute_tuning(mmc, opcode);
}
- return 0;
+ return ret;
+}
+
+static void esdhc_set_uhs_signaling(struct sdhci_host *host,
+ unsigned int timing)
+{
+ if (timing == MMC_TIMING_MMC_HS400)
+ esdhc_tuning_block_enable(host, true);
+ else
+ sdhci_set_uhs_signaling(host, timing);
}
#ifdef CONFIG_PM_SLEEP
.adma_workaround = esdhc_of_adma_workaround,
.set_bus_width = esdhc_pltfm_set_bus_width,
.reset = esdhc_reset,
- .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .set_uhs_signaling = esdhc_set_uhs_signaling,
};
static const struct sdhci_ops sdhci_esdhc_le_ops = {
.adma_workaround = esdhc_of_adma_workaround,
.set_bus_width = esdhc_pltfm_set_bus_width,
.reset = esdhc_reset,
- .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .set_uhs_signaling = esdhc_set_uhs_signaling,
};
static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = {
}
}
+static int esdhc_hs400_prepare_ddr(struct mmc_host *mmc)
+{
+ esdhc_tuning_block_enable(mmc_priv(mmc), false);
+ return 0;
+}
+
static int sdhci_esdhc_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
host->mmc_host_ops.start_signal_voltage_switch =
esdhc_signal_voltage_switch;
host->mmc_host_ops.execute_tuning = esdhc_execute_tuning;
+ host->mmc_host_ops.hs400_prepare_ddr = esdhc_hs400_prepare_ddr;
host->tuning_delay = 1;
esdhc_init(pdev, host);