diff mbox series

[v4,2/2] mmc: sdhci-of-esdhc: add hs400 mode support

Message ID 20181025025924.22492-2-yinbo.zhu@nxp.com (mailing list archive)
State New, archived
Headers show
Series [v4,1/2] mmc: mmc: add prepare_ddr_to_hs400 callback | expand

Commit Message

Yinbo Zhu Oct. 25, 2018, 2:59 a.m. UTC
From: Yangbo Lu <yangbo.lu@nxp.com>

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>
---
Change in v4
		post this as part of patch series

 drivers/mmc/host/sdhci-esdhc.h    |   20 +++++++++
 drivers/mmc/host/sdhci-of-esdhc.c |   78 ++++++++++++++++++++++++++++++++-----
 2 files changed, 88 insertions(+), 10 deletions(-)

Comments

Ulf Hansson Nov. 20, 2018, 8:18 a.m. UTC | #1
On 25 October 2018 at 04:59, Yinbo Zhu <yinbo.zhu@nxp.com> wrote:
> From: Yangbo Lu <yangbo.lu@nxp.com>
>
> 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.

Thanks for the detailed description of the sequence. Very helpful.

>
> Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
> Signed-off-by: Yinbo Zhu <yinbo.zhu@nxp.com>

Overall this looks goods to me, once you rebased this on top of a new
version of patch 1/2, then I am ready to apply this.

Kind regards
Uffe


> ---
> Change in v4
>                 post this as part of patch series
>
>  drivers/mmc/host/sdhci-esdhc.h    |   20 +++++++++
>  drivers/mmc/host/sdhci-of-esdhc.c |   78 ++++++++++++++++++++++++++++++++-----
>  2 files changed, 88 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h
> index 3f16d9c..721a635 100644
> --- a/drivers/mmc/host/sdhci-esdhc.h
> +++ b/drivers/mmc/host/sdhci-esdhc.h
> @@ -59,9 +59,29 @@
>
>  /* 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
> diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
> index 86fc9f0..65419cb 100644
> --- a/drivers/mmc/host/sdhci-of-esdhc.c
> +++ b/drivers/mmc/host/sdhci-of-esdhc.c
> @@ -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 int esdhc_signal_voltage_switch(struct mmc_host *mmc,
>         { },
>  };
>
> -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 SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_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 SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_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_prepare_ddr_to_hs400(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.prepare_ddr_to_hs400 = esdhc_prepare_ddr_to_hs400;
>         host->tuning_delay = 1;
>
>         esdhc_init(pdev, host);
> --
> 1.7.1
>
diff mbox series

Patch

diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h
index 3f16d9c..721a635 100644
--- a/drivers/mmc/host/sdhci-esdhc.h
+++ b/drivers/mmc/host/sdhci-esdhc.h
@@ -59,9 +59,29 @@ 
 
 /* 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
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 86fc9f0..65419cb 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -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 int esdhc_signal_voltage_switch(struct mmc_host *mmc,
 	{ },
 };
 
-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 SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_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 SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_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_prepare_ddr_to_hs400(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.prepare_ddr_to_hs400 = esdhc_prepare_ddr_to_hs400;
 	host->tuning_delay = 1;
 
 	esdhc_init(pdev, host);