mmc: sdhci: Fix CMD line reset interfering with ongoing data transfer
authorAdrian Hunter <adrian.hunter@intel.com>
Wed, 2 Nov 2016 13:49:08 +0000 (15:49 +0200)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 7 Nov 2016 12:26:42 +0000 (13:26 +0100)
CMD line reset during an ongoing data transfer can cause the data transfer
to hang.  Fix by delaying the reset until the data transfer is finished.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: stable@vger.kernel.org # v4.8+
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/sdhci.c

index 71654b90227f6c1eac7115c84276d8490e02e550..fd0f24cddad4e9a5e2573e921cf58124d3913c04 100644 (file)
@@ -2286,10 +2286,8 @@ static bool sdhci_request_done(struct sdhci_host *host)
 
        for (i = 0; i < SDHCI_MAX_MRQS; i++) {
                mrq = host->mrqs_done[i];
-               if (mrq) {
-                       host->mrqs_done[i] = NULL;
+               if (mrq)
                        break;
-               }
        }
 
        if (!mrq) {
@@ -2320,6 +2318,17 @@ static bool sdhci_request_done(struct sdhci_host *host)
         * upon error conditions.
         */
        if (sdhci_needs_reset(host, mrq)) {
+               /*
+                * Do not finish until command and data lines are available for
+                * reset. Note there can only be one other mrq, so it cannot
+                * also be in mrqs_done, otherwise host->cmd and host->data_cmd
+                * would both be null.
+                */
+               if (host->cmd || host->data_cmd) {
+                       spin_unlock_irqrestore(&host->lock, flags);
+                       return true;
+               }
+
                /* Some controllers need this kick or reset won't work here */
                if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)
                        /* This is to force an update */
@@ -2327,10 +2336,8 @@ static bool sdhci_request_done(struct sdhci_host *host)
 
                /* Spec says we should do both at the same time, but Ricoh
                   controllers do not like that. */
-               if (!host->cmd)
-                       sdhci_do_reset(host, SDHCI_RESET_CMD);
-               if (!host->data_cmd)
-                       sdhci_do_reset(host, SDHCI_RESET_DATA);
+               sdhci_do_reset(host, SDHCI_RESET_CMD);
+               sdhci_do_reset(host, SDHCI_RESET_DATA);
 
                host->pending_reset = false;
        }
@@ -2338,6 +2345,8 @@ static bool sdhci_request_done(struct sdhci_host *host)
        if (!sdhci_has_requests(host))
                sdhci_led_deactivate(host);
 
+       host->mrqs_done[i] = NULL;
+
        mmiowb();
        spin_unlock_irqrestore(&host->lock, flags);