From 1b0a082c4ccc5b8bc592fb803b5455ebfe328805 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Lothar=20Wa=C3=9Fmann?= Date: Wed, 8 Mar 2017 15:04:35 +0100 Subject: [PATCH] mmc: sdhci: fix mmc busy timeout handling With eMMC there is a good chance, that the mmc device remains busy for extended periods of time after certain operations. The current approach to handle this was to increase the timeout for all commands up to a maximum. Rather than this, do an extended wait for !busy in the relevant case only, not affecting the general mmc cmd busy timeout. --- drivers/mmc/mmc.c | 39 +++++++++++++++++++++++++-------------- drivers/mmc/sdhci.c | 32 +++++++++++++++----------------- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 0312da91af..a7a5134529 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -141,14 +141,16 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) int mmc_send_status(struct mmc *mmc, int timeout) { struct mmc_cmd cmd; - int err, retries = 5; + int err; + int retries = 0; + const int max_tries = 5000; cmd.cmdidx = MMC_CMD_SEND_STATUS; cmd.resp_type = MMC_RSP_R1; if (!mmc_host_is_spi(mmc)) cmd.cmdarg = mmc->rca << 16; - while (1) { + do { err = mmc_send_cmd(mmc, &cmd, NULL); if (!err) { if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) && @@ -162,21 +164,29 @@ int mmc_send_status(struct mmc *mmc, int timeout) #endif return -ECOMM; } - } else if (--retries < 0) - return err; + } + if (err == -EBUSY) { + if (retries == 0) + printf("(e)MMC is busy; please wait... "); + if (retries++ > max_tries) + break; + udelay(10000); + continue; + } if (timeout-- <= 0) break; udelay(1000); - } - + } while (err); + if (retries) + printf("\n"); mmc_trace_state(mmc, &cmd); - if (timeout <= 0) { + if (err) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("Timeout waiting card ready\n"); + printf("Timeout waiting for card ready\n"); #endif - return -ETIMEDOUT; + return err; } return 0; @@ -407,7 +417,7 @@ static int mmc_send_op_cond(struct mmc *mmc) /* Some cards seem to need this */ mmc_go_idle(mmc); - /* Asking to the card its capabilities */ + /* Asking the card for its capabilities */ for (i = 0; i < 2; i++) { err = mmc_send_op_cond_iter(mmc, i != 0); if (err) @@ -424,7 +434,7 @@ static int mmc_send_op_cond(struct mmc *mmc) static int mmc_complete_op_cond(struct mmc *mmc) { struct mmc_cmd cmd; - int timeout = 1000; + int timeout = 10 * CONFIG_SYS_HZ; uint start; int err; @@ -508,7 +518,6 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) ret = mmc_send_status(mmc, timeout); return ret; - } static int mmc_change_freq(struct mmc *mmc) @@ -1131,10 +1140,11 @@ static int mmc_startup(struct mmc *mmc) cmd.cmdarg = mmc->rca << 16; err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; /* Waiting for the ready status */ - mmc_send_status(mmc, timeout); - + err = mmc_send_status(mmc, timeout); if (err) return err; @@ -1231,6 +1241,7 @@ static int mmc_startup(struct mmc *mmc) mmc->part_config = MMCPART_NOAVAILABLE; if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) { /* check ext_csd version and capacity */ + err = mmc_send_ext_csd(mmc, ext_csd); if (err) return err; diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 1d93718813..ad72e97f84 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -139,7 +139,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, { #endif struct sdhci_host *host = mmc->priv; - unsigned int stat = 0; + unsigned int stat; int ret = 0; int trans_bytes = 0, is_aligned = 1; u32 mask, flags, mode; @@ -148,7 +148,9 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, unsigned start = get_timer(0); /* Timeout unit - ms */ - static unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT; + unsigned int cmd_timeout; + + cmd_timeout = cmd->cmdidx == MMC_CMD_SEND_STATUS ? 10 : 200; sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; @@ -158,19 +160,14 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) mask &= ~SDHCI_DATA_INHIBIT; - while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { - if (time >= cmd_timeout) { - printf("%s: MMC: %d busy ", __func__, mmc_dev); - if (2 * cmd_timeout <= SDHCI_CMD_MAX_TIMEOUT) { - cmd_timeout += cmd_timeout; - printf("timeout increasing to: %u ms.\n", - cmd_timeout); - } else { - puts("timeout.\n"); + while ((stat = sdhci_readl(host, SDHCI_PRESENT_STATE) & mask)) { + if (time++ >= cmd_timeout) { + debug("%s: MMC: %d busy\n", __func__, mmc_dev); + if (stat & (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) + return -EBUSY; + else return -ECOMM; - } } - time++; udelay(1000); } @@ -227,7 +224,6 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, if (data->flags != MMC_DATA_READ) memcpy(aligned_buffer, data->src, trans_bytes); #endif - sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); mode |= SDHCI_TRNS_DMA; #endif @@ -265,8 +261,9 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, if ((stat & (SDHCI_INT_ERROR | mask)) == mask) { sdhci_cmd_done(host, cmd); sdhci_writel(host, mask, SDHCI_INT_STATUS); - } else + } else { ret = -1; + } if (!ret && data) ret = sdhci_transfer_data(host, data, start_addr); @@ -294,14 +291,15 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) { struct sdhci_host *host = mmc->priv; - unsigned int div, clk = 0, timeout, reg; + unsigned int div, timeout, reg; + uint16_t clk = 0; /* Wait max 20 ms */ timeout = 200; while (sdhci_readl(host, SDHCI_PRESENT_STATE) & (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) { if (timeout == 0) { - printf("%s: Timeout to wait cmd & data inhibit\n", + printf("%s: Timeout waiting for cmd & data inhibit\n", __func__); return -1; } -- 2.39.2