summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Ossman <drzeus@drzeus.cx>2007-08-12 16:46:32 +0200
committerPierre Ossman <drzeus@drzeus.cx>2007-08-23 06:30:53 +0200
commite538fbe83e374a3521128c1f4642aca037661c9d (patch)
tree9a2abd920ed6767ae17747daa2e91fa9f1e5fd24
parent03f8590d90844f04d20488a80e75eaf4c4e0b35c (diff)
sdhci: handle data interrupts during command
It is fully legal for a controller to start issuing data related interrupts before it has signalled that the command has completed. Make sure the driver actually can handle this. Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
-rw-r--r--drivers/mmc/host/sdhci.c28
-rw-r--r--drivers/mmc/host/sdhci.h1
2 files changed, 22 insertions, 7 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2b327b40fa81..f8fc0a98b8c4 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -385,6 +385,9 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
BUG_ON(data->blksz > host->mmc->max_blk_size);
BUG_ON(data->blocks > 65535);
+ host->data = data;
+ host->data_early = 0;
+
/* timeout in us */
target_timeout = data->timeout_ns / 1000 +
data->timeout_clks / host->clock;
@@ -443,11 +446,11 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
{
u16 mode;
- WARN_ON(host->data);
-
if (data == NULL)
return;
+ WARN_ON(!host->data);
+
mode = SDHCI_TRNS_BLK_CNT_EN;
if (data->blocks > 1)
mode |= SDHCI_TRNS_MULTI;
@@ -600,9 +603,10 @@ static void sdhci_finish_command(struct sdhci_host *host)
host->cmd->error = MMC_ERR_NONE;
- if (host->cmd->data)
- host->data = host->cmd->data;
- else
+ if (host->data && host->data_early)
+ sdhci_finish_data(host);
+
+ if (!host->cmd->data)
tasklet_schedule(&host->finish_tasklet);
host->cmd = NULL;
@@ -991,8 +995,18 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
writel(readl(host->ioaddr + SDHCI_DMA_ADDRESS),
host->ioaddr + SDHCI_DMA_ADDRESS);
- if (intmask & SDHCI_INT_DATA_END)
- sdhci_finish_data(host);
+ if (intmask & SDHCI_INT_DATA_END) {
+ if (host->cmd) {
+ /*
+ * Data managed to finish before the
+ * command completed. Make sure we do
+ * things in the proper order.
+ */
+ host->data_early = 1;
+ } else {
+ sdhci_finish_data(host);
+ }
+ }
}
}
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index d157776c1149..e28987d6d2eb 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -182,6 +182,7 @@ struct sdhci_host {
struct mmc_request *mrq; /* Current request */
struct mmc_command *cmd; /* Current command */
struct mmc_data *data; /* Current data request */
+ int data_early:1; /* Data finished before cmd */
struct scatterlist *cur_sg; /* We're working on this */
int num_sg; /* Entries left */