diff options
author | Doug Anderson <dianders@chromium.org> | 2015-04-03 14:13:07 -0400 |
---|---|---|
committer | Ulf Hansson <ulf.hansson@linaro.org> | 2015-04-09 03:08:32 -0400 |
commit | 49ba030221d23ad8e35deb66b74873b852f4d7bf (patch) | |
tree | 5278c1a485d331df11679c9b76bca0e09257d286 /drivers/mmc | |
parent | fd6741983386a7ec1c707a4c93e7a26e383cc571 (diff) |
mmc: dw_mmc: Add locking around cmd11 timer
It is possible for the cmd11 interrupt to fire and delete the
cmd11_timer before the cmd11_timer was actually setup. Let's fix this
race by adding a few spinlocks. Note that the race wasn't seen in
practice without adding some printk statements, but it still seems
wise to fix.
Fixes: 5c935165da79 ("mmc: dw_mmc: Add a timeout for sending CMD11")
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 | 24 |
1 files changed, 21 insertions, 3 deletions
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index c2b568352ce3..38b29265cc7c 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c | |||
@@ -1023,14 +1023,23 @@ static void __dw_mci_start_request(struct dw_mci *host, | |||
1023 | dw_mci_start_command(host, cmd, cmdflags); | 1023 | dw_mci_start_command(host, cmd, cmdflags); |
1024 | 1024 | ||
1025 | if (cmd->opcode == SD_SWITCH_VOLTAGE) { | 1025 | if (cmd->opcode == SD_SWITCH_VOLTAGE) { |
1026 | unsigned long irqflags; | ||
1027 | |||
1026 | /* | 1028 | /* |
1027 | * Databook says to fail after 2ms w/ no response, but evidence | 1029 | * Databook says to fail after 2ms w/ no response, but evidence |
1028 | * shows that sometimes the cmd11 interrupt takes over 130ms. | 1030 | * shows that sometimes the cmd11 interrupt takes over 130ms. |
1029 | * We'll set to 500ms, plus an extra jiffy just in case jiffies | 1031 | * We'll set to 500ms, plus an extra jiffy just in case jiffies |
1030 | * is just about to roll over. | 1032 | * is just about to roll over. |
1033 | * | ||
1034 | * We do this whole thing under spinlock and only if the | ||
1035 | * command hasn't already completed (indicating the the irq | ||
1036 | * already ran so we don't want the timeout). | ||
1031 | */ | 1037 | */ |
1032 | mod_timer(&host->cmd11_timer, | 1038 | spin_lock_irqsave(&host->irq_lock, irqflags); |
1033 | jiffies + msecs_to_jiffies(500) + 1); | 1039 | if (!test_bit(EVENT_CMD_COMPLETE, &host->pending_events)) |
1040 | mod_timer(&host->cmd11_timer, | ||
1041 | jiffies + msecs_to_jiffies(500) + 1); | ||
1042 | spin_unlock_irqrestore(&host->irq_lock, irqflags); | ||
1034 | } | 1043 | } |
1035 | 1044 | ||
1036 | if (mrq->stop) | 1045 | if (mrq->stop) |
@@ -2160,11 +2169,20 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) | |||
2160 | /* Check volt switch first, since it can look like an error */ | 2169 | /* Check volt switch first, since it can look like an error */ |
2161 | if ((host->state == STATE_SENDING_CMD11) && | 2170 | if ((host->state == STATE_SENDING_CMD11) && |
2162 | (pending & SDMMC_INT_VOLT_SWITCH)) { | 2171 | (pending & SDMMC_INT_VOLT_SWITCH)) { |
2163 | del_timer(&host->cmd11_timer); | 2172 | unsigned long irqflags; |
2164 | 2173 | ||
2165 | mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH); | 2174 | mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH); |
2166 | pending &= ~SDMMC_INT_VOLT_SWITCH; | 2175 | pending &= ~SDMMC_INT_VOLT_SWITCH; |
2176 | |||
2177 | /* | ||
2178 | * Hold the lock; we know cmd11_timer can't be kicked | ||
2179 | * off after the lock is released, so safe to delete. | ||
2180 | */ | ||
2181 | spin_lock_irqsave(&host->irq_lock, irqflags); | ||
2167 | dw_mci_cmd_interrupt(host, pending); | 2182 | dw_mci_cmd_interrupt(host, pending); |
2183 | spin_unlock_irqrestore(&host->irq_lock, irqflags); | ||
2184 | |||
2185 | del_timer(&host->cmd11_timer); | ||
2168 | } | 2186 | } |
2169 | 2187 | ||
2170 | if (pending & DW_MCI_CMD_ERROR_FLAGS) { | 2188 | if (pending & DW_MCI_CMD_ERROR_FLAGS) { |