aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorDoug Anderson <dianders@chromium.org>2014-12-02 18:42:47 -0500
committerUlf Hansson <ulf.hansson@linaro.org>2015-01-19 03:56:05 -0500
commitf8c58c1136349fdfa9b605c501f2f911622d3a9a (patch)
tree3f214e01a5ff8a1715dc8835a44406116146ef60 /drivers/mmc
parentb24c8b260189fe21cca992d2f5175a33f6cc5477 (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.c13
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
760static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) 760static 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
807static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) 810static 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
1298static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) 1310static 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 /*