mmc: sdhci-of-esdhc: add hs400 mode support
authorYangbo Lu <yangbo.lu@nxp.com>
Fri, 23 Nov 2018 03:15:34 +0000 (11:15 +0800)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 17 Dec 2018 07:26:24 +0000 (08:26 +0100)
1.  Perform the Tuning Process at the HS400 target operating frequency.
    Latched the clock division value.
2.  if read transaction, then set the SDTIMNGCTL[FLW_CTL_BG].
3.  Switch to High Speed mode and then set the card clock frequency to
    a value not greater than 52Mhz
4.  Clear TBCTL[TB_EN],tuning block enable bit.
5.  Change to 8 bit DDR Mode
6.  Switch the card to HS400 mode.
7.  Set TBCTL[TB_EN], tuning block enable bit.
8.  Clear SYSCTL[SDCLKEN]
9.  Wait for PRSSTAT[SDSTB] to be set
10. Change the clock division to latched value.Set TBCTL[HS 400 mode]
    and Set SDCLKCTL[CMD_CLK_CTRL]
11. Set SYSCTL[SDCLKEN]
12. Wait for PRSSTAT[SDSTB] to be set
13. Set DLLCFG0[DLL_ENABLE] and DLLCFG0[DLL_FREQ_SEL].
14. Wait for delay chain to lock.
15. Set TBCTL[HS400_WNDW_ADJUST]
16. Again clear SYSCTL[SDCLKEN]
17. Wait for PRSSTAT[SDSTB] to be set
18. Set ESDHCCTL[FAF]
19. Wait for ESDHCCTL[FAF] to be cleared
20. Set SYSCTL[SDCLKEN]
21. Wait for PRSSTAT[SDSTB] to be set.

Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Signed-off-by: Yinbo Zhu <yinbo.zhu@nxp.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/sdhci-esdhc.h
drivers/mmc/host/sdhci-of-esdhc.c

index 3f16d9c90ba2baba844a55143d28c4ba18472cf0..721a635b10e723442cadcf48bb7d1438a4b61e74 100644 (file)
 
 /* 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
index 86fc9f02200207e7c9fe6a80c7cd69b9691fdbaf..fb4fe96821ce484ffa98d99a95217b9ea07d3fa1 100644 (file)
@@ -592,6 +592,26 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
                | (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)) {
@@ -603,6 +623,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
                udelay(10);
        }
 
+       temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
        temp |= ESDHC_CLOCK_SDCLKEN;
        sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
 }
@@ -728,25 +749,46 @@ static struct soc_device_attribute soc_fixup_tuning[] = {
        { },
 };
 
-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
@@ -765,7 +807,16 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
                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
@@ -814,7 +865,7 @@ static const struct sdhci_ops sdhci_esdhc_be_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_ops sdhci_esdhc_le_ops = {
@@ -831,7 +882,7 @@ 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 = {
@@ -909,6 +960,12 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
        }
 }
 
+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;
@@ -932,6 +989,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
        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);