Commit e538fbe8 authored by Pierre Ossman's avatar Pierre Ossman

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: default avatarPierre Ossman <drzeus@drzeus.cx>
parent 03f8590d
...@@ -385,6 +385,9 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) ...@@ -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->blksz > host->mmc->max_blk_size);
BUG_ON(data->blocks > 65535); BUG_ON(data->blocks > 65535);
host->data = data;
host->data_early = 0;
/* timeout in us */ /* timeout in us */
target_timeout = data->timeout_ns / 1000 + target_timeout = data->timeout_ns / 1000 +
data->timeout_clks / host->clock; data->timeout_clks / host->clock;
...@@ -443,11 +446,11 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, ...@@ -443,11 +446,11 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
{ {
u16 mode; u16 mode;
WARN_ON(host->data);
if (data == NULL) if (data == NULL)
return; return;
WARN_ON(!host->data);
mode = SDHCI_TRNS_BLK_CNT_EN; mode = SDHCI_TRNS_BLK_CNT_EN;
if (data->blocks > 1) if (data->blocks > 1)
mode |= SDHCI_TRNS_MULTI; mode |= SDHCI_TRNS_MULTI;
...@@ -600,9 +603,10 @@ static void sdhci_finish_command(struct sdhci_host *host) ...@@ -600,9 +603,10 @@ static void sdhci_finish_command(struct sdhci_host *host)
host->cmd->error = MMC_ERR_NONE; host->cmd->error = MMC_ERR_NONE;
if (host->cmd->data) if (host->data && host->data_early)
host->data = host->cmd->data; sdhci_finish_data(host);
else
if (!host->cmd->data)
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
host->cmd = NULL; host->cmd = NULL;
...@@ -991,8 +995,18 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) ...@@ -991,8 +995,18 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
writel(readl(host->ioaddr + SDHCI_DMA_ADDRESS), writel(readl(host->ioaddr + SDHCI_DMA_ADDRESS),
host->ioaddr + SDHCI_DMA_ADDRESS); host->ioaddr + SDHCI_DMA_ADDRESS);
if (intmask & SDHCI_INT_DATA_END) if (intmask & SDHCI_INT_DATA_END) {
sdhci_finish_data(host); 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);
}
}
} }
} }
......
...@@ -182,6 +182,7 @@ struct sdhci_host { ...@@ -182,6 +182,7 @@ struct sdhci_host {
struct mmc_request *mrq; /* Current request */ struct mmc_request *mrq; /* Current request */
struct mmc_command *cmd; /* Current command */ struct mmc_command *cmd; /* Current command */
struct mmc_data *data; /* Current data request */ struct mmc_data *data; /* Current data request */
int data_early:1; /* Data finished before cmd */
struct scatterlist *cur_sg; /* We're working on this */ struct scatterlist *cur_sg; /* We're working on this */
int num_sg; /* Entries left */ int num_sg; /* Entries left */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment