summaryrefslogtreecommitdiff
path: root/drivers/mmc/host/sdhci.c
diff options
context:
space:
mode:
authorAdrian Hunter <adrian.hunter@intel.com>2016-06-29 16:24:20 +0300
committerUlf Hansson <ulf.hansson@linaro.org>2016-07-25 10:34:36 +0200
commit6bde868181dd98aef14ac3ba02a3216b40466867 (patch)
tree5d1e3004d60a141c07446eaa0bac910f602919e2 /drivers/mmc/host/sdhci.c
parent00884b61fb3968da088f7bf26eef9b90233b69df (diff)
mmc: sdhci: Move busy signal handling into sdhci_finish_cmd()
In order to support commands during data transfer, command and data handling needs to be untangled. That means sdhci_finish_cmd() must not be called from the data IRQ handler. It is being called because of busy signal handling, which is treating the command as not finished until the busy signal is released. Instead, move busy signal handling from sdhci_cmd_irq() into sdhci_finish_cmd(). Then the data IRQ handler does not need to call sdhci_finish_cmd() and can instead finish the request. What this means in practice for a command with busy signaling, is that the command response is read from the host controller when the command complete interrupt is received, thus freeing up the command circuit for other commands. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Diffstat (limited to 'drivers/mmc/host/sdhci.c')
-rw-r--r--drivers/mmc/host/sdhci.c54
1 files changed, 26 insertions, 28 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 44cdb6d035f2..9ca4253b0ab7 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -45,7 +45,6 @@ static unsigned int debug_quirks2;
static void sdhci_finish_data(struct sdhci_host *);
-static void sdhci_finish_command(struct sdhci_host *);
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
static void sdhci_dumpregs(struct sdhci_host *host)
@@ -1079,6 +1078,28 @@ static void sdhci_finish_command(struct sdhci_host *host)
}
}
+ /*
+ * The host can send and interrupt when the busy state has
+ * ended, allowing us to wait without wasting CPU cycles.
+ * The busy signal uses DAT0 so this is similar to waiting
+ * for data to complete.
+ *
+ * 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 if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) &&
+ !host->busy_handle) {
+ /* Mark that command complete before busy is ended */
+ host->busy_handle = 1;
+ host->cmd = NULL;
+ return;
+ }
+ }
+
/* Finished CMD23, now send actual command. */
if (host->cmd == host->mrq->sbc) {
host->cmd = NULL;
@@ -2295,33 +2316,10 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
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 if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ)
- && !host->busy_handle) {
- /* Mark that command complete before busy is ended */
- host->busy_handle = 1;
- return;
- }
-
- /* The controller does not support the end-of-busy IRQ,
- * fall through and take the SDHCI_INT_RESPONSE */
- } else if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) &&
- host->cmd->opcode == MMC_STOP_TRANSMISSION && !host->data) {
+ if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) &&
+ !(host->cmd->flags & MMC_RSP_BUSY) && !host->data &&
+ host->cmd->opcode == MMC_STOP_TRANSMISSION)
*mask &= ~SDHCI_INT_DATA_END;
- }
if (intmask & SDHCI_INT_RESPONSE)
sdhci_finish_command(host);
@@ -2395,7 +2393,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
* sure we do things in the proper order.
*/
if (host->busy_handle)
- sdhci_finish_command(host);
+ tasklet_schedule(&host->finish_tasklet);
else
host->busy_handle = 1;
return;