summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdrian Hunter <adrian.hunter@intel.com>2016-06-29 16:24:33 +0300
committerUlf Hansson <ulf.hansson@linaro.org>2016-07-25 10:34:43 +0200
commitd7422fb489eee5587d3eecdd1151f90bf78f93a4 (patch)
treee08f9bd274a759ad718bc12bf23059a78ed4f3e1
parent56a590dcdc973e05230e9af3bcd4dfefd40d4127 (diff)
downloadlinux-d7422fb489eee5587d3eecdd1151f90bf78f93a4.tar.bz2
mmc: sdhci: Separate timer timeout for command and data requests
In order to support commands during data transfer, there will have to be up to two active requests (mrqs) at a time, instead of just one. Provide two timers instead of just one. One of the timers is for requests that do not use the data lines, and the other one is for requests that do. Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
-rw-r--r--drivers/mmc/host/sdhci.c62
-rw-r--r--drivers/mmc/host/sdhci.h1
2 files changed, 53 insertions, 10 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index b7b68b31d36d..9580f76caf57 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1003,6 +1003,23 @@ static void sdhci_finish_data(struct sdhci_host *host)
}
}
+static void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
+ unsigned long timeout)
+{
+ if (sdhci_data_line_cmd(mrq->cmd))
+ mod_timer(&host->data_timer, timeout);
+ else
+ mod_timer(&host->timer, timeout);
+}
+
+static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq)
+{
+ if (sdhci_data_line_cmd(mrq->cmd))
+ del_timer(&host->data_timer);
+ else
+ del_timer(&host->timer);
+}
+
void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
{
int flags;
@@ -1044,7 +1061,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
else
timeout += 10 * HZ;
- mod_timer(&host->timer, timeout);
+ sdhci_mod_timer(host, cmd->mrq, timeout);
host->cmd = cmd;
if (sdhci_data_line_cmd(cmd)) {
@@ -2232,10 +2249,10 @@ static void sdhci_tasklet_finish(unsigned long param)
return;
}
- del_timer(&host->timer);
-
mrq = host->mrq;
+ sdhci_del_timer(host, mrq);
+
/*
* Always unmap the data buffers if they were mapped by
* sdhci_prepare_data() whenever we finish with a request.
@@ -2289,7 +2306,30 @@ static void sdhci_timeout_timer(unsigned long data)
spin_lock_irqsave(&host->lock, flags);
- if (host->mrq) {
+ if (host->cmd && !sdhci_data_line_cmd(host->cmd)) {
+ pr_err("%s: Timeout waiting for hardware cmd interrupt.\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+
+ host->cmd->error = -ETIMEDOUT;
+ sdhci_finish_mrq(host, host->cmd->mrq);
+ }
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void sdhci_timeout_data_timer(unsigned long data)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+
+ host = (struct sdhci_host *)data;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->data || host->data_cmd ||
+ (host->cmd && sdhci_data_line_cmd(host->cmd))) {
pr_err("%s: Timeout waiting for hardware interrupt.\n",
mmc_hostname(host->mmc));
sdhci_dumpregs(host);
@@ -2297,13 +2337,12 @@ static void sdhci_timeout_timer(unsigned long data)
if (host->data) {
host->data->error = -ETIMEDOUT;
sdhci_finish_data(host);
+ } else if (host->data_cmd) {
+ host->data_cmd->error = -ETIMEDOUT;
+ sdhci_finish_mrq(host, host->data_cmd->mrq);
} else {
- if (host->cmd)
- host->cmd->error = -ETIMEDOUT;
- else
- host->mrq->cmd->error = -ETIMEDOUT;
-
- sdhci_finish_mrq(host, host->mrq);
+ host->cmd->error = -ETIMEDOUT;
+ sdhci_finish_mrq(host, host->cmd->mrq);
}
}
@@ -3432,6 +3471,8 @@ int __sdhci_add_host(struct sdhci_host *host)
sdhci_tasklet_finish, (unsigned long)host);
setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
+ setup_timer(&host->data_timer, sdhci_timeout_data_timer,
+ (unsigned long)host);
init_waitqueue_head(&host->buf_ready_int);
@@ -3541,6 +3582,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
free_irq(host->irq, host);
del_timer_sync(&host->timer);
+ del_timer_sync(&host->data_timer);
tasklet_kill(&host->finish_tasklet);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 7301c90f8500..a1de42232439 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -490,6 +490,7 @@ struct sdhci_host {
struct tasklet_struct finish_tasklet; /* Tasklet structures */
struct timer_list timer; /* Timer for timeouts */
+ struct timer_list data_timer; /* Timer for data timeouts */
u32 caps; /* CAPABILITY_0 */
u32 caps1; /* CAPABILITY_1 */