[v3, 07/11] mmc: fsl_esdhc: support eMMC HS400 mode

Yangbo Lu yangbo.lu at nxp.com
Thu Jul 23 06:30:12 CEST 2020


The process for eMMC HS400 mode for eSDHC is,

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 at nxp.com>
---
Changes for v2:
	- None.
Changes for v3:
	- Added HS400 exit code for downgrade.
---
 drivers/mmc/fsl_esdhc.c | 120 ++++++++++++++++++++++++++++++++++--------------
 include/fsl_esdhc.h     |  12 +++++
 2 files changed, 98 insertions(+), 34 deletions(-)

diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c
index c52ab0f..f2ab75c 100644
--- a/drivers/mmc/fsl_esdhc.c
+++ b/drivers/mmc/fsl_esdhc.c
@@ -62,7 +62,12 @@ struct fsl_esdhc {
 	uint    hostcapblt2;	/* Host controller capabilities register 2 */
 	char	reserved6[8];	/* reserved */
 	uint	tbctl;		/* Tuning block control register */
-	char    reserved7[744];	/* reserved */
+	char    reserved7[32];	/* reserved */
+	uint	sdclkctl;	/* SD clock control register */
+	uint	sdtimingctl;	/* SD timing control register */
+	char    reserved8[20];	/* reserved */
+	uint	dllcfg0;	/* DLL config 0 register */
+	char    reserved9[680];	/* reserved */
 	uint    esdhcctl;	/* eSDHC control register */
 };
 
@@ -568,16 +573,80 @@ static void esdhc_clock_control(struct fsl_esdhc_priv *priv, bool enable)
 	}
 }
 
+static void esdhc_flush_async_fifo(struct fsl_esdhc_priv *priv)
+{
+	struct fsl_esdhc *regs = priv->esdhc_regs;
+	u32 time_out;
+
+	esdhc_setbits32(&regs->esdhcctl, ESDHCCTL_FAF);
+
+	time_out = 20;
+	while (esdhc_read32(&regs->esdhcctl) & ESDHCCTL_FAF) {
+		if (time_out == 0) {
+			printf("fsl_esdhc: Flush asynchronous FIFO timeout.\n");
+			break;
+		}
+		time_out--;
+		mdelay(1);
+	}
+}
+
+static void esdhc_tuning_block_enable(struct fsl_esdhc_priv *priv,
+				      bool en)
+{
+	struct fsl_esdhc *regs = priv->esdhc_regs;
+
+	esdhc_clock_control(priv, false);
+	esdhc_flush_async_fifo(priv);
+	if (en)
+		esdhc_setbits32(&regs->tbctl, TBCTL_TB_EN);
+	else
+		esdhc_clrbits32(&regs->tbctl, TBCTL_TB_EN);
+	esdhc_clock_control(priv, true);
+}
+
+static void esdhc_exit_hs400(struct fsl_esdhc_priv *priv)
+{
+	struct fsl_esdhc *regs = priv->esdhc_regs;
+
+	esdhc_clrbits32(&regs->sdtimingctl, FLW_CTL_BG);
+	esdhc_clrbits32(&regs->sdclkctl, CMD_CLK_CTL);
+
+	esdhc_clock_control(priv, false);
+	esdhc_clrbits32(&regs->tbctl, HS400_MODE);
+	esdhc_clock_control(priv, true);
+
+	esdhc_clrbits32(&regs->dllcfg0, DLL_FREQ_SEL | DLL_ENABLE);
+	esdhc_clrbits32(&regs->tbctl, HS400_WNDW_ADJUST);
+
+	esdhc_tuning_block_enable(priv, false);
+}
+
 static void esdhc_set_timing(struct fsl_esdhc_priv *priv, enum bus_mode mode)
 {
 	struct fsl_esdhc *regs = priv->esdhc_regs;
 
+	/* Exit HS400 mode before setting any other mode */
+	if (esdhc_read32(&regs->tbctl) & HS400_MODE &&
+	    mode != MMC_HS_400)
+		esdhc_exit_hs400(priv);
+
 	esdhc_clock_control(priv, false);
 
 	if (mode == MMC_HS_200)
 		esdhc_clrsetbits32(&regs->autoc12err, UHSM_MASK,
 				   UHSM_SDR104_HS200);
+	if (mode == MMC_HS_400) {
+		esdhc_setbits32(&regs->tbctl, HS400_MODE);
+		esdhc_setbits32(&regs->sdclkctl, CMD_CLK_CTL);
+		esdhc_clock_control(priv, true);
 
+		esdhc_setbits32(&regs->dllcfg0, DLL_ENABLE | DLL_FREQ_SEL);
+		esdhc_setbits32(&regs->tbctl, HS400_WNDW_ADJUST);
+
+		esdhc_clock_control(priv, false);
+		esdhc_flush_async_fifo(priv);
+	}
 	esdhc_clock_control(priv, true);
 }
 
@@ -592,6 +661,9 @@ static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc)
 		esdhc_clock_control(priv, true);
 	}
 
+	if (mmc->selected_mode == MMC_HS_400)
+		esdhc_tuning_block_enable(priv, true);
+
 	/* Set the clock speed */
 	if (priv->clock != mmc->clock)
 		set_sysctl(priv, mmc, mmc->clock);
@@ -987,38 +1059,6 @@ static int fsl_esdhc_reinit(struct udevice *dev)
 }
 
 #ifdef MMC_SUPPORTS_TUNING
-static void esdhc_flush_async_fifo(struct fsl_esdhc_priv *priv)
-{
-	struct fsl_esdhc *regs = priv->esdhc_regs;
-	u32 time_out;
-
-	esdhc_setbits32(&regs->esdhcctl, ESDHCCTL_FAF);
-
-	time_out = 20;
-	while (esdhc_read32(&regs->esdhcctl) & ESDHCCTL_FAF) {
-		if (time_out == 0) {
-			printf("fsl_esdhc: Flush asynchronous FIFO timeout.\n");
-			break;
-		}
-		time_out--;
-		mdelay(1);
-	}
-}
-
-static void esdhc_tuning_block_enable(struct fsl_esdhc_priv *priv,
-				      bool en)
-{
-	struct fsl_esdhc *regs = priv->esdhc_regs;
-
-	esdhc_clock_control(priv, false);
-	esdhc_flush_async_fifo(priv);
-	if (en)
-		esdhc_setbits32(&regs->tbctl, TBCTL_TB_EN);
-	else
-		esdhc_clrbits32(&regs->tbctl, TBCTL_TB_EN);
-	esdhc_clock_control(priv, true);
-}
-
 static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode)
 {
 	struct fsl_esdhc_plat *plat = dev_get_platdata(dev);
@@ -1046,8 +1086,11 @@ static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode)
 
 	esdhc_write32(&regs->irqstaten, irqstaten);
 
-	if (i != MAX_TUNING_LOOP)
+	if (i != MAX_TUNING_LOOP) {
+		if (plat->mmc.hs400_tuning)
+			esdhc_setbits32(&regs->sdtimingctl, FLW_CTL_BG);
 		return 0;
+	}
 
 	printf("fsl_esdhc: tuning failed!\n");
 	esdhc_clrbits32(&regs->autoc12err, SMPCLKSEL);
@@ -1057,6 +1100,14 @@ static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode)
 }
 #endif
 
+int fsl_esdhc_hs400_prepare_ddr(struct udevice *dev)
+{
+	struct fsl_esdhc_priv *priv = dev_get_priv(dev);
+
+	esdhc_tuning_block_enable(priv, false);
+	return 0;
+}
+
 static const struct dm_mmc_ops fsl_esdhc_ops = {
 	.get_cd		= fsl_esdhc_get_cd,
 	.send_cmd	= fsl_esdhc_send_cmd,
@@ -1065,6 +1116,7 @@ static const struct dm_mmc_ops fsl_esdhc_ops = {
 	.execute_tuning = fsl_esdhc_execute_tuning,
 #endif
 	.reinit = fsl_esdhc_reinit,
+	.hs400_prepare_ddr = fsl_esdhc_hs400_prepare_ddr,
 };
 
 static const struct udevice_id fsl_esdhc_ids[] = {
diff --git a/include/fsl_esdhc.h b/include/fsl_esdhc.h
index aaa5941..b35ef0c 100644
--- a/include/fsl_esdhc.h
+++ b/include/fsl_esdhc.h
@@ -176,6 +176,18 @@
 
 /* Tuning block control register */
 #define TBCTL_TB_EN		0x00000004
+#define HS400_MODE		0x00000010
+#define HS400_WNDW_ADJUST	0x00000040
+
+/* SD clock control register */
+#define CMD_CLK_CTL		0x00008000
+
+/* SD timing control register */
+#define FLW_CTL_BG		0x00008000
+
+/* DLL config 0 register */
+#define DLL_ENABLE		0x80000000
+#define DLL_FREQ_SEL		0x08000000
 
 #define MAX_TUNING_LOOP		40
 
-- 
2.7.4



More information about the U-Boot mailing list