MMC framework has already implemented hs200 mode for eMMC devices, moreover the standard SDHC3.0 controller support tuning. We can set the corresponding flag in host->host_cpas.
Host driver issue tuning command repeatedly until the host controller resets Execute Tuning to 0. Host controller resets Execute Tuning to 0 when tuning is completed or tuning is not completed within 40 times. Host driver can abort this loop by 40 times CMD19/CMD21 issue or 150ms time-out. If tuning is completed successfully, driver set Sampling Clock Select to 1 and this means the host contorller start to use tuned sampling clcok. If tuning is failed, host controller keeps Sampling Clock Select to 0. Signed-off-by: Ziyuan Xu <xzy...@rock-chips.com> --- drivers/mmc/sdhci.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index b9cd13a..e346820 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -158,7 +158,10 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, static unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT; sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); - mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; + mask = SDHCI_CMD_INHIBIT; + + if (data) + mask |= SDHCI_DATA_INHIBIT; /* We shouldn't wait for data inihibit for stop commands, even though they might use busy signaling */ @@ -200,6 +203,13 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, if (data) flags |= SDHCI_CMD_DATA; + if (cmd->cmdidx == MMC_SEND_TUNING_BLOCK || + cmd->cmdidx == MMC_SEND_TUNING_BLOCK_HS200) { + mask &= ~SDHCI_INT_RESPONSE; + mask |= SDHCI_INT_DATA_AVAIL; + flags |= SDHCI_CMD_DATA; + } + /* Set Transfer mode regarding to data flag */ if (data != 0) { sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); @@ -559,6 +569,108 @@ static int sdhci_init(struct mmc *mmc) return 0; } +static int sdhci_send_tuning(struct sdhci_host *host, u32 opcode) +{ + struct mmc_cmd cmd; + + cmd.cmdidx = opcode; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0; + /* + * In response to CMD19, the card sends 64 bytes of tuning + * block to the Host Controller. So we set the block size + * to 64 here. + */ + if (opcode == MMC_SEND_TUNING_BLOCK_HS200 && + host->mmc->bus_width == MMC_BUS_WIDTH_8BIT) + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128), SDHCI_BLOCK_SIZE); + else + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE); + + /* + * The tuning block is sent by the card to the host controller. + * So we set the TRNS_READ bit in the Transfer Mode register. + * This also takes care of setting DMA Enable and Multi Block + * Select in the same register to 0. + */ + sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); + +#ifdef CONFIG_DM_MMC_OPS + return sdhci_send_command(host->mmc->dev, &cmd, NULL); +#else + return sdhci_send_command(host->mmc, &cmd, NULL); +#endif +} + +#define MAX_TUNING_LOOP 40 +static int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) +{ + int i; + int ret; + + /* + * Issue opcode repeatedly till Execute Tuning is set to 0 or the number + * of loops reaches 40 times. + */ + for (i = 0; i < MAX_TUNING_LOOP; i++) { + u16 ctrl; + + ret = sdhci_send_tuning(host, opcode); + + if (ret) + return ret; + + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) { + if (ctrl & SDHCI_CTRL_TUNED_CLK) + /* Tuning successfully */ + return 0; + break; + } + } + + return -ETIMEDOUT; +} + +#ifdef CONFIG_DM_MMC_OPS +static int sdhci_execute_tuning(struct udevice *dev, u32 opcode) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); +#else +static int sdhci_execute_tuning(struct mmc *mmc, u32 opcode) +{ +#endif + struct sdhci_host *host = mmc->priv; + u16 ctrl; + + /* + * The Host Controller needs tuning in case of SDR104 and DDR50 + * mode, and for SDR50 mode when Use Tuning for SDR50 is set in + * the Capabilities register. + * If the Host Controller supports the HS200 mode then the + * tuning function has to be executed. + */ + switch (mmc->timing) { + /* HS400 tuning is done in HS200 mode */ + case MMC_TIMING_MMC_HS400: + return -EINVAL; + case MMC_TIMING_MMC_HS200: + /* + * Periodic re-tuning for HS400 is not expected to be needed, so + * disable it here. + */ + break; + default: + return -EINVAL; + } + + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + ctrl |= SDHCI_CTRL_EXEC_TUNING; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + + return __sdhci_execute_tuning(host, opcode); +} + #ifdef CONFIG_DM_MMC_OPS int sdhci_probe(struct udevice *dev) { @@ -571,6 +683,7 @@ const struct dm_mmc_ops sdhci_ops = { .card_busy = sdhci_card_busy, .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, + .execute_tuning = sdhci_execute_tuning, }; #else static const struct mmc_ops sdhci_ops = { @@ -578,6 +691,7 @@ static const struct mmc_ops sdhci_ops = { .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, .init = sdhci_init, + .execute_tuning = sdhci_execute_tuning, }; #endif -- 2.7.4 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot