]> git.kernelconcepts.de Git - karo-tx-uboot.git/commitdiff
mmc: sdhci: fix mmc busy timeout handling
authorLothar Waßmann <LW@KARO-electronics.de>
Wed, 8 Mar 2017 14:04:35 +0000 (15:04 +0100)
committerLothar Waßmann <LW@KARO-electronics.de>
Mon, 13 Mar 2017 15:36:05 +0000 (16:36 +0100)
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
drivers/mmc/sdhci.c

index 0312da91af7736db2ee2845cbfea7cfbdb115dd0..a7a5134529eb5d0f5e003671567d5ce0f942616f 100644 (file)
@@ -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;
index 1d93718813b1d4a6191c4e2c349631808dc3194d..ad72e97f84773d4a7c8f2bf7a28e374dc69d51fa 100644 (file)
@@ -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;
                }