diff options
author | Doug Anderson <dianders@chromium.org> | 2014-12-02 18:42:47 -0500 |
---|---|---|
committer | Ulf Hansson <ulf.hansson@linaro.org> | 2015-01-19 03:56:05 -0500 |
commit | f8c58c1136349fdfa9b605c501f2f911622d3a9a (patch) | |
tree | 3f214e01a5ff8a1715dc8835a44406116146ef60 /drivers/mmc | |
parent | b24c8b260189fe21cca992d2f5175a33f6cc5477 (diff) |
mmc: dw_mmc: Protect read-modify-write of INTMASK with a lock
We're running into cases where our enabling of the SDIO interrupt in
dw_mmc doesn't actually take effect. Specifically, adding patch like
this:
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1076,6 +1076,9 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
mci_writel(host, INTMASK,
(int_mask | SDMMC_INT_SDIO(slot->id)));
+ int_mask = mci_readl(host, INTMASK);
+ if (!(int_mask & SDMMC_INT_SDIO(slot->id)))
+ dev_err(&mmc->class_dev, "failed to enable sdio irq\n");
} else {
...actually triggers the error message. That's because the
dw_mci_enable_sdio_irq() unsafely does a read-modify-write of the
INTMASK register.
We can't just use the standard host->lock since that lock is not irq
safe and mmc_signal_sdio_irq() (called from interrupt context) calls
dw_mci_enable_sdio_irq(). Add a new irq-safe lock to protect INTMASK.
An alternate solution to this is to punt mmc_signal_sdio_irq() to the
tasklet and then protect INTMASK modifications by the standard host
lock. This seemed like a bit more of a high-latency change.
Reported-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: Doug Anderson <dianders@chromium.org>
Reviewed-by: James Hogan <james.hogan@imgtec.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/dw_mmc.c | 13 |
1 files changed, 13 insertions, 0 deletions
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index ae10a021765c..64ea04274913 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c | |||
@@ -759,6 +759,7 @@ disable: | |||
759 | 759 | ||
760 | static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) | 760 | static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) |
761 | { | 761 | { |
762 | unsigned long irqflags; | ||
762 | int sg_len; | 763 | int sg_len; |
763 | u32 temp; | 764 | u32 temp; |
764 | 765 | ||
@@ -795,9 +796,11 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) | |||
795 | mci_writel(host, CTRL, temp); | 796 | mci_writel(host, CTRL, temp); |
796 | 797 | ||
797 | /* Disable RX/TX IRQs, let DMA handle it */ | 798 | /* Disable RX/TX IRQs, let DMA handle it */ |
799 | spin_lock_irqsave(&host->irq_lock, irqflags); | ||
798 | temp = mci_readl(host, INTMASK); | 800 | temp = mci_readl(host, INTMASK); |
799 | temp &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR); | 801 | temp &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR); |
800 | mci_writel(host, INTMASK, temp); | 802 | mci_writel(host, INTMASK, temp); |
803 | spin_unlock_irqrestore(&host->irq_lock, irqflags); | ||
801 | 804 | ||
802 | host->dma_ops->start(host, sg_len); | 805 | host->dma_ops->start(host, sg_len); |
803 | 806 | ||
@@ -806,6 +809,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) | |||
806 | 809 | ||
807 | static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) | 810 | static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) |
808 | { | 811 | { |
812 | unsigned long irqflags; | ||
809 | u32 temp; | 813 | u32 temp; |
810 | 814 | ||
811 | data->error = -EINPROGRESS; | 815 | data->error = -EINPROGRESS; |
@@ -834,9 +838,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) | |||
834 | host->part_buf_count = 0; | 838 | host->part_buf_count = 0; |
835 | 839 | ||
836 | mci_writel(host, RINTSTS, SDMMC_INT_TXDR | SDMMC_INT_RXDR); | 840 | mci_writel(host, RINTSTS, SDMMC_INT_TXDR | SDMMC_INT_RXDR); |
841 | |||
842 | spin_lock_irqsave(&host->irq_lock, irqflags); | ||
837 | temp = mci_readl(host, INTMASK); | 843 | temp = mci_readl(host, INTMASK); |
838 | temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR; | 844 | temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR; |
839 | mci_writel(host, INTMASK, temp); | 845 | mci_writel(host, INTMASK, temp); |
846 | spin_unlock_irqrestore(&host->irq_lock, irqflags); | ||
840 | 847 | ||
841 | temp = mci_readl(host, CTRL); | 848 | temp = mci_readl(host, CTRL); |
842 | temp &= ~SDMMC_CTRL_DMA_ENABLE; | 849 | temp &= ~SDMMC_CTRL_DMA_ENABLE; |
@@ -1284,8 +1291,11 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) | |||
1284 | { | 1291 | { |
1285 | struct dw_mci_slot *slot = mmc_priv(mmc); | 1292 | struct dw_mci_slot *slot = mmc_priv(mmc); |
1286 | struct dw_mci *host = slot->host; | 1293 | struct dw_mci *host = slot->host; |
1294 | unsigned long irqflags; | ||
1287 | u32 int_mask; | 1295 | u32 int_mask; |
1288 | 1296 | ||
1297 | spin_lock_irqsave(&host->irq_lock, irqflags); | ||
1298 | |||
1289 | /* Enable/disable Slot Specific SDIO interrupt */ | 1299 | /* Enable/disable Slot Specific SDIO interrupt */ |
1290 | int_mask = mci_readl(host, INTMASK); | 1300 | int_mask = mci_readl(host, INTMASK); |
1291 | if (enb) | 1301 | if (enb) |
@@ -1293,6 +1303,8 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) | |||
1293 | else | 1303 | else |
1294 | int_mask &= ~SDMMC_INT_SDIO(slot->sdio_id); | 1304 | int_mask &= ~SDMMC_INT_SDIO(slot->sdio_id); |
1295 | mci_writel(host, INTMASK, int_mask); | 1305 | mci_writel(host, INTMASK, int_mask); |
1306 | |||
1307 | spin_unlock_irqrestore(&host->irq_lock, irqflags); | ||
1296 | } | 1308 | } |
1297 | 1309 | ||
1298 | static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) | 1310 | static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) |
@@ -2661,6 +2673,7 @@ int dw_mci_probe(struct dw_mci *host) | |||
2661 | host->quirks = host->pdata->quirks; | 2673 | host->quirks = host->pdata->quirks; |
2662 | 2674 | ||
2663 | spin_lock_init(&host->lock); | 2675 | spin_lock_init(&host->lock); |
2676 | spin_lock_init(&host->irq_lock); | ||
2664 | INIT_LIST_HEAD(&host->queue); | 2677 | INIT_LIST_HEAD(&host->queue); |
2665 | 2678 | ||
2666 | /* | 2679 | /* |