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>
This commit is contained in:
Yangbo Lu 2020-09-01 16:58:01 +08:00 committed by Peng Fan
parent 1fdefd1d0d
commit b1a4247b41
2 changed files with 116 additions and 7 deletions

View File

@ -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(&regs->cmdarg, cmd->cmdarg);
esdhc_write32(&regs->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(&regs->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(&regs->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(&regs->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(&regs->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(&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);
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(&regs->autoc12err, EXECUTE_TUNING);
irqstaten = esdhc_read32(&regs->irqstaten);
esdhc_write32(&regs->irqstaten, IRQSTATEN_BRR);
for (i = 0; i < MAX_TUNING_LOOP; i++) {
mmc_send_tuning(&plat->mmc, opcode, NULL);
mdelay(1);
val = esdhc_read32(&regs->autoc12err);
if (!(val & EXECUTE_TUNING)) {
if (val & SMPCLKSEL)
break;
}
}
esdhc_write32(&regs->irqstaten, irqstaten);
if (i != MAX_TUNING_LOOP)
return 0;
printf("fsl_esdhc: tuning failed!\n");
esdhc_clrbits32(&regs->autoc12err, SMPCLKSEL);
esdhc_clrbits32(&regs->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,

View File

@ -74,8 +74,10 @@
#define IRQSTATEN_TC (0x00000002)
#define IRQSTATEN_CC (0x00000001)
/* eSDHC control register */
#define ESDHCCTL 0x0002e40c
#define ESDHCCTL_PCS (0x00080000)
#define ESDHCCTL_FAF (0x00040000)
#define PRSSTAT 0x0002e024
#define PRSSTAT_DAT0 (0x01000000)
@ -154,6 +156,12 @@
#define BLKATTR_SIZE(x) (x & 0x1fff)
#define MAX_BLK_CNT 0x7fff /* so malloc will have enough room with 32M */
/* Auto CMD error status register / system control 2 register */
#define EXECUTE_TUNING 0x00400000
#define SMPCLKSEL 0x00800000
#define UHSM_MASK 0x00070000
#define UHSM_SDR104_HS200 0x00030000
/* Host controller capabilities register */
#define HOSTCAPBLT_VS18 0x04000000
#define HOSTCAPBLT_VS30 0x02000000
@ -162,6 +170,11 @@
#define HOSTCAPBLT_DMAS 0x00400000
#define HOSTCAPBLT_HSS 0x00200000
/* Tuning block control register */
#define TBCTL_TB_EN 0x00000004
#define MAX_TUNING_LOOP 40
struct fsl_esdhc_cfg {
phys_addr_t esdhc_base;
u32 sdhc_clk;
@ -203,10 +216,6 @@ struct fsl_esdhc_cfg {
int fsl_esdhc_mmc_init(struct bd_info *bis);
int fsl_esdhc_initialize(struct bd_info *bis, struct fsl_esdhc_cfg *cfg);
void fdt_fixup_esdhc(void *blob, struct bd_info *bd);
#ifdef MMC_SUPPORTS_TUNING
static inline int fsl_esdhc_execute_tuning(struct udevice *dev,
uint32_t opcode) {return 0; }
#endif
#else
static inline int fsl_esdhc_mmc_init(struct bd_info *bis) { return -ENOSYS; }
static inline void fdt_fixup_esdhc(void *blob, struct bd_info *bd) {}