diff options
author | Doug Anderson <dianders@chromium.org> | 2015-03-09 19:18:21 -0400 |
---|---|---|
committer | Ulf Hansson <ulf.hansson@linaro.org> | 2015-03-31 10:50:43 -0400 |
commit | 5c935165da79644df90a647ecc140fb77b40dee5 (patch) | |
tree | 0dd8f9bbc1aaa9690fa8f8257d79a6d586b4d785 /drivers/mmc | |
parent | 488b8d63a63400e2839062a99358815ca21a2860 (diff) |
mmc: dw_mmc: Add a timeout for sending CMD11
In the Designware databook's description of the "Voltage Switch Normal
Scenario" it instructs us to set a timer and fail the voltage change
if we don't see the voltage change interrupt within 2ms. Let's
implement that. Without implementing this I have often been able to
reproduce a hang while trying to send CMD11 on an rk3288-based board
while constantly ejecting and inserting UHS cards.
Signed-off-by: Doug Anderson <dianders@chromium.org>
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/dw_mmc.c | 26 |
1 files changed, 26 insertions, 0 deletions
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index f68ea4d57b18..8e0836d39081 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c | |||
@@ -1021,6 +1021,15 @@ static void __dw_mci_start_request(struct dw_mci *host, | |||
1021 | 1021 | ||
1022 | dw_mci_start_command(host, cmd, cmdflags); | 1022 | dw_mci_start_command(host, cmd, cmdflags); |
1023 | 1023 | ||
1024 | if (cmd->opcode == SD_SWITCH_VOLTAGE) { | ||
1025 | /* | ||
1026 | * Databook says to fail after 2ms w/ no response; give an | ||
1027 | * extra jiffy just in case we're about to roll over. | ||
1028 | */ | ||
1029 | mod_timer(&host->cmd11_timer, | ||
1030 | jiffies + msecs_to_jiffies(2) + 1); | ||
1031 | } | ||
1032 | |||
1024 | if (mrq->stop) | 1033 | if (mrq->stop) |
1025 | host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop); | 1034 | host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop); |
1026 | else | 1035 | else |
@@ -2159,6 +2168,8 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) | |||
2159 | /* Check volt switch first, since it can look like an error */ | 2168 | /* Check volt switch first, since it can look like an error */ |
2160 | if ((host->state == STATE_SENDING_CMD11) && | 2169 | if ((host->state == STATE_SENDING_CMD11) && |
2161 | (pending & SDMMC_INT_VOLT_SWITCH)) { | 2170 | (pending & SDMMC_INT_VOLT_SWITCH)) { |
2171 | del_timer(&host->cmd11_timer); | ||
2172 | |||
2162 | mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH); | 2173 | mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH); |
2163 | pending &= ~SDMMC_INT_VOLT_SWITCH; | 2174 | pending &= ~SDMMC_INT_VOLT_SWITCH; |
2164 | dw_mci_cmd_interrupt(host, pending); | 2175 | dw_mci_cmd_interrupt(host, pending); |
@@ -2572,6 +2583,18 @@ ciu_out: | |||
2572 | return ret; | 2583 | return ret; |
2573 | } | 2584 | } |
2574 | 2585 | ||
2586 | static void dw_mci_cmd11_timer(unsigned long arg) | ||
2587 | { | ||
2588 | struct dw_mci *host = (struct dw_mci *)arg; | ||
2589 | |||
2590 | if (host->state != STATE_SENDING_CMD11) | ||
2591 | dev_info(host->dev, "Unexpected CMD11 timeout\n"); | ||
2592 | |||
2593 | host->cmd_status = SDMMC_INT_RTO; | ||
2594 | set_bit(EVENT_CMD_COMPLETE, &host->pending_events); | ||
2595 | tasklet_schedule(&host->tasklet); | ||
2596 | } | ||
2597 | |||
2575 | #ifdef CONFIG_OF | 2598 | #ifdef CONFIG_OF |
2576 | static struct dw_mci_of_quirks { | 2599 | static struct dw_mci_of_quirks { |
2577 | char *quirk; | 2600 | char *quirk; |
@@ -2746,6 +2769,9 @@ int dw_mci_probe(struct dw_mci *host) | |||
2746 | } | 2769 | } |
2747 | } | 2770 | } |
2748 | 2771 | ||
2772 | setup_timer(&host->cmd11_timer, | ||
2773 | dw_mci_cmd11_timer, (unsigned long)host); | ||
2774 | |||
2749 | host->quirks = host->pdata->quirks; | 2775 | host->quirks = host->pdata->quirks; |
2750 | 2776 | ||
2751 | spin_lock_init(&host->lock); | 2777 | spin_lock_init(&host->lock); |