summaryrefslogtreecommitdiff
path: root/drivers/mmc/fsl_esdhc.c
diff options
context:
space:
mode:
authorYangbo Lu <yangbo.lu@nxp.com>2020-09-01 11:58:05 +0300
committerPeng Fan <peng.fan@nxp.com>2020-10-12 10:46:34 +0300
commitdb8f93672b42411856b11cf294a6f9c25482179c (patch)
treea3a17eedf5c2999323a3ed7b9b6c9008884b8313 /drivers/mmc/fsl_esdhc.c
parentd271e10581e7f15451e9d4c3cdd39044440a848a (diff)
downloadu-boot-db8f93672b42411856b11cf294a6f9c25482179c.tar.xz
mmc: fsl_esdhc: support eMMC HS400 mode
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@nxp.com>
Diffstat (limited to 'drivers/mmc/fsl_esdhc.c')
-rw-r--r--drivers/mmc/fsl_esdhc.c120
1 files changed, 86 insertions, 34 deletions
diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c
index e7db55d881..c53751d0bb 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);
@@ -948,38 +1020,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);
@@ -1007,8 +1047,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);
@@ -1018,6 +1061,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,
@@ -1026,6 +1077,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[] = {