diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index cb3e75bd5b37dd..3317c6a2f0f9cd 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1332,14 +1332,14 @@ static void sdhci_finish_data(struct sdhci_host *host) * responsibility to send the stop command if required. */ if (data->mrq->cap_cmd_during_tfr) { - sdhci_finish_mrq(host, data->mrq); + __sdhci_finish_mrq(host, data->mrq); } else { /* Avoid triggering warning in sdhci_send_command() */ host->cmd = NULL; sdhci_send_command(host, data->stop); } } else { - sdhci_finish_mrq(host, data->mrq); + __sdhci_finish_mrq(host, data->mrq); } } @@ -1502,7 +1502,7 @@ static void sdhci_finish_command(struct sdhci_host *host) sdhci_finish_data(host); if (!cmd->data) - sdhci_finish_mrq(host, cmd->mrq); + __sdhci_finish_mrq(host, cmd->mrq); } } @@ -2761,6 +2761,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t) if (host->data) { host->data->error = -ETIMEDOUT; sdhci_finish_data(host); + tasklet_schedule(&host->finish_tasklet); } else if (host->data_cmd) { host->data_cmd->error = -ETIMEDOUT; sdhci_finish_mrq(host, host->data_cmd->mrq); @@ -2827,7 +2828,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p) return; } - sdhci_finish_mrq(host, host->cmd->mrq); + __sdhci_finish_mrq(host, host->cmd->mrq); return; } @@ -2841,7 +2842,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p) if (mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) { mrq->sbc->error = err; - sdhci_finish_mrq(host, mrq); + __sdhci_finish_mrq(host, mrq); return; } } @@ -2905,7 +2906,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) if (intmask & SDHCI_INT_DATA_TIMEOUT) { host->data_cmd = NULL; data_cmd->error = -ETIMEDOUT; - sdhci_finish_mrq(host, data_cmd->mrq); + __sdhci_finish_mrq(host, data_cmd->mrq); return; } if (intmask & SDHCI_INT_DATA_END) { @@ -2918,7 +2919,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) if (host->cmd == data_cmd) return; - sdhci_finish_mrq(host, data_cmd->mrq); + __sdhci_finish_mrq(host, data_cmd->mrq); return; } } @@ -3001,12 +3002,24 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) } } +static inline bool sdhci_defer_done(struct sdhci_host *host, + struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + + return host->pending_reset || + ((host->flags & SDHCI_REQ_USE_DMA) && data && + data->host_cookie == COOKIE_MAPPED); +} + static irqreturn_t sdhci_irq(int irq, void *dev_id) { + struct mmc_request *mrqs_done[SDHCI_MAX_MRQS] = {0}; irqreturn_t result = IRQ_NONE; struct sdhci_host *host = dev_id; u32 intmask, mask, unexpected = 0; int max_loops = 16; + int i; spin_lock(&host->lock); @@ -3100,9 +3113,30 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) intmask = sdhci_readl(host, SDHCI_INT_STATUS); } while (intmask && --max_loops); + + /* Determine if mrqs can be completed immediately */ + for (i = 0; i < SDHCI_MAX_MRQS; i++) { + struct mmc_request *mrq = host->mrqs_done[i]; + + if (!mrq) + continue; + + if (sdhci_defer_done(host, mrq)) { + tasklet_schedule(&host->finish_tasklet); + } else { + mrqs_done[i] = mrq; + host->mrqs_done[i] = NULL; + } + } out: spin_unlock(&host->lock); + /* Process mrqs ready for immediate completion */ + for (i = 0; i < SDHCI_MAX_MRQS; i++) { + if (mrqs_done[i]) + mmc_request_done(host->mmc, mrqs_done[i]); + } + if (unexpected) { pr_err("%s: Unexpected interrupt 0x%08x.\n", mmc_hostname(host->mmc), unexpected);