summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/sdhci.c39
1 files changed, 33 insertions, 6 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index e3a8133560a2..ef461274397b 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1266,9 +1266,31 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
SDHCI_INT_INDEX))
host->cmd->error = -EILSEQ;
- if (host->cmd->error)
+ if (host->cmd->error) {
tasklet_schedule(&host->finish_tasklet);
- else if (intmask & SDHCI_INT_RESPONSE)
+ return;
+ }
+
+ /*
+ * The host can send and interrupt when the busy state has
+ * ended, allowing us to wait without wasting CPU cycles.
+ * Unfortunately this is overloaded on the "data complete"
+ * interrupt, so we need to take some care when handling
+ * it.
+ *
+ * Note: The 1.0 specification is a bit ambiguous about this
+ * feature so there might be some problems with older
+ * controllers.
+ */
+ if (host->cmd->flags & MMC_RSP_BUSY) {
+ if (host->cmd->data)
+ DBG("Cannot wait for busy signal when also "
+ "doing a data transfer");
+ else
+ return;
+ }
+
+ if (intmask & SDHCI_INT_RESPONSE)
sdhci_finish_command(host);
}
@@ -1278,11 +1300,16 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
if (!host->data) {
/*
- * A data end interrupt is sent together with the response
- * for the stop command.
+ * The "data complete" interrupt is also used to
+ * indicate that a busy state has ended. See comment
+ * above in sdhci_cmd_irq().
*/
- if (intmask & SDHCI_INT_DATA_END)
- return;
+ if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) {
+ if (intmask & SDHCI_INT_DATA_END) {
+ sdhci_finish_command(host);
+ return;
+ }
+ }
printk(KERN_ERR "%s: Got data interrupt 0x%08x even "
"though no data operation was in progress.\n",