diff options
author | Yangbo Lu <yangbo.lu@nxp.com> | 2020-09-01 16:58:01 +0800 |
---|---|---|
committer | Peng Fan <peng.fan@nxp.com> | 2020-10-12 15:46:29 +0800 |
commit | b1a4247b411522bc4b81dd349c5945dea0b3e9f8 (patch) | |
tree | 3c1f41cde9aeaeda3d872f5a93422c90b11e0dbf /drivers/mmc | |
parent | 1fdefd1d0d12ac728e29a67a224777299840a397 (diff) | |
download | u-boot-b1a4247b411522bc4b81dd349c5945dea0b3e9f8.zip u-boot-b1a4247b411522bc4b81dd349c5945dea0b3e9f8.tar.gz u-boot-b1a4247b411522bc4b81dd349c5945dea0b3e9f8.tar.bz2 |
mmc: fsl_esdhc: support tuning for eMMC HS200
Support tuning process for eMMC HS200 for eSDHC.
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/fsl_esdhc.c | 106 |
1 files changed, 103 insertions, 3 deletions
diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 83feaf1..4e04c10 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -60,7 +60,9 @@ struct fsl_esdhc { uint dmaerrattr; /* DMA error attribute register */ char reserved5[4]; /* reserved */ uint hostcapblt2; /* Host controller capabilities register 2 */ - char reserved6[756]; /* reserved */ + char reserved6[8]; /* reserved */ + uint tbctl; /* Tuning block control register */ + char reserved7[744]; /* reserved */ uint esdhcctl; /* eSDHC control register */ }; @@ -101,7 +103,9 @@ static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) if (data) { xfertyp |= XFERTYP_DPSEL; #ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO - xfertyp |= XFERTYP_DMAEN; + if (cmd->cmdidx != MMC_CMD_SEND_TUNING_BLOCK && + cmd->cmdidx != MMC_CMD_SEND_TUNING_BLOCK_HS200) + xfertyp |= XFERTYP_DMAEN; #endif if (data->blocks > 1) { xfertyp |= XFERTYP_MSBSEL; @@ -380,6 +384,10 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc, esdhc_write32(®s->cmdarg, cmd->cmdarg); esdhc_write32(®s->xfertyp, xfertyp); + if (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK || + cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) + flags = IRQSTAT_BRR; + /* Wait for the command to complete */ start = get_timer(0); while (!(esdhc_read32(®s->irqstat) & flags)) { @@ -439,6 +447,11 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc, #ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO esdhc_pio_read_write(priv, data); #else + flags = DATA_COMPLETE; + if (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK || + cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) + flags = IRQSTAT_BRR; + do { irqstat = esdhc_read32(®s->irqstat); @@ -451,7 +464,7 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc, err = -ECOMM; goto out; } - } while ((irqstat & DATA_COMPLETE) != DATA_COMPLETE); + } while ((irqstat & flags) != flags); /* * Need invalidate the dcache here again to avoid any @@ -555,6 +568,19 @@ static void esdhc_clock_control(struct fsl_esdhc_priv *priv, bool enable) } } +static void esdhc_set_timing(struct fsl_esdhc_priv *priv, enum bus_mode mode) +{ + struct fsl_esdhc *regs = priv->esdhc_regs; + + esdhc_clock_control(priv, false); + + if (mode == MMC_HS_200) + esdhc_clrsetbits32(®s->autoc12err, UHSM_MASK, + UHSM_SDR104_HS200); + + esdhc_clock_control(priv, true); +} + static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) { struct fsl_esdhc *regs = priv->esdhc_regs; @@ -570,6 +596,9 @@ static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) if (priv->clock != mmc->clock) set_sysctl(priv, mmc, mmc->clock); + /* Set timing */ + esdhc_set_timing(priv, mmc->selected_mode); + /* Set the bus width */ esdhc_clrbits32(®s->proctl, PROCTL_DTW_4 | PROCTL_DTW_8); @@ -915,6 +944,77 @@ static int fsl_esdhc_reinit(struct udevice *dev) return esdhc_init_common(priv, &plat->mmc); } +#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(®s->esdhcctl, ESDHCCTL_FAF); + + time_out = 20; + while (esdhc_read32(®s->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(®s->tbctl, TBCTL_TB_EN); + else + esdhc_clrbits32(®s->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); + struct fsl_esdhc_priv *priv = dev_get_priv(dev); + struct fsl_esdhc *regs = priv->esdhc_regs; + u32 val, irqstaten; + int i; + + esdhc_tuning_block_enable(priv, true); + esdhc_setbits32(®s->autoc12err, EXECUTE_TUNING); + + irqstaten = esdhc_read32(®s->irqstaten); + esdhc_write32(®s->irqstaten, IRQSTATEN_BRR); + + for (i = 0; i < MAX_TUNING_LOOP; i++) { + mmc_send_tuning(&plat->mmc, opcode, NULL); + mdelay(1); + + val = esdhc_read32(®s->autoc12err); + if (!(val & EXECUTE_TUNING)) { + if (val & SMPCLKSEL) + break; + } + } + + esdhc_write32(®s->irqstaten, irqstaten); + + if (i != MAX_TUNING_LOOP) + return 0; + + printf("fsl_esdhc: tuning failed!\n"); + esdhc_clrbits32(®s->autoc12err, SMPCLKSEL); + esdhc_clrbits32(®s->autoc12err, EXECUTE_TUNING); + esdhc_tuning_block_enable(priv, false); + return -ETIMEDOUT; +} +#endif + static const struct dm_mmc_ops fsl_esdhc_ops = { .get_cd = fsl_esdhc_get_cd, .send_cmd = fsl_esdhc_send_cmd, |