aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/dw_mmc.c
diff options
context:
space:
mode:
authorJames Hogan <james.hogan@imgtec.com>2011-06-24 08:55:55 -0400
committerChris Ball <cjb@laptop.org>2011-07-20 17:20:58 -0400
commit1791b13ea4d97a6a7c162edd54485e932ad92f1b (patch)
tree0012ece1a9b0bf403f759d5f552fc34c6d8c82d1 /drivers/mmc/host/dw_mmc.c
parent7456caae37396fc1bc6f8e9461d07664b8c2f280 (diff)
mmc: dw_mmc: convert card tasklet to workqueue
Convert the card insert/remove tasklet to a workqueue, and call the setpower platform specific callback without the spinlock held. This means neither of the setpower or get_cd callbacks are called from atomic context which allows them to sleep. Signed-off-by: James Hogan <james.hogan@imgtec.com> Acked-by: Will Newton <will.newton@imgtec.com> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc/host/dw_mmc.c')
-rw-r--r--drivers/mmc/host/dw_mmc.c50
1 files changed, 31 insertions, 19 deletions
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index c4bddf6a5f1f..3f610d55f38e 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -33,6 +33,7 @@
33#include <linux/mmc/dw_mmc.h> 33#include <linux/mmc/dw_mmc.h>
34#include <linux/bitops.h> 34#include <linux/bitops.h>
35#include <linux/regulator/consumer.h> 35#include <linux/regulator/consumer.h>
36#include <linux/workqueue.h>
36 37
37#include "dw_mmc.h" 38#include "dw_mmc.h"
38 39
@@ -100,6 +101,8 @@ struct dw_mci_slot {
100 int last_detect_state; 101 int last_detect_state;
101}; 102};
102 103
104static struct workqueue_struct *dw_mci_card_workqueue;
105
103#if defined(CONFIG_DEBUG_FS) 106#if defined(CONFIG_DEBUG_FS)
104static int dw_mci_req_show(struct seq_file *s, void *v) 107static int dw_mci_req_show(struct seq_file *s, void *v)
105{ 108{
@@ -1255,7 +1258,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
1255 1258
1256 if (pending & SDMMC_INT_CD) { 1259 if (pending & SDMMC_INT_CD) {
1257 mci_writel(host, RINTSTS, SDMMC_INT_CD); 1260 mci_writel(host, RINTSTS, SDMMC_INT_CD);
1258 tasklet_schedule(&host->card_tasklet); 1261 queue_work(dw_mci_card_workqueue, &host->card_work);
1259 } 1262 }
1260 1263
1261 } while (pass_count++ < 5); 1264 } while (pass_count++ < 5);
@@ -1274,9 +1277,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
1274 return IRQ_HANDLED; 1277 return IRQ_HANDLED;
1275} 1278}
1276 1279
1277static void dw_mci_tasklet_card(unsigned long data) 1280static void dw_mci_work_routine_card(struct work_struct *work)
1278{ 1281{
1279 struct dw_mci *host = (struct dw_mci *)data; 1282 struct dw_mci *host = container_of(work, struct dw_mci, card_work);
1280 int i; 1283 int i;
1281 1284
1282 for (i = 0; i < host->num_slots; i++) { 1285 for (i = 0; i < host->num_slots; i++) {
@@ -1288,22 +1291,21 @@ static void dw_mci_tasklet_card(unsigned long data)
1288 1291
1289 present = dw_mci_get_cd(mmc); 1292 present = dw_mci_get_cd(mmc);
1290 while (present != slot->last_detect_state) { 1293 while (present != slot->last_detect_state) {
1291 spin_lock(&host->lock);
1292
1293 dev_dbg(&slot->mmc->class_dev, "card %s\n", 1294 dev_dbg(&slot->mmc->class_dev, "card %s\n",
1294 present ? "inserted" : "removed"); 1295 present ? "inserted" : "removed");
1295 1296
1297 /* Power up slot (before spin_lock, may sleep) */
1298 if (present != 0 && host->pdata->setpower)
1299 host->pdata->setpower(slot->id, mmc->ocr_avail);
1300
1301 spin_lock_bh(&host->lock);
1302
1296 /* Card change detected */ 1303 /* Card change detected */
1297 slot->last_detect_state = present; 1304 slot->last_detect_state = present;
1298 1305
1299 /* Power up slot */ 1306 /* Mark card as present if applicable */
1300 if (present != 0) { 1307 if (present != 0)
1301 if (host->pdata->setpower)
1302 host->pdata->setpower(slot->id,
1303 mmc->ocr_avail);
1304
1305 set_bit(DW_MMC_CARD_PRESENT, &slot->flags); 1308 set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
1306 }
1307 1309
1308 /* Clean up queue if present */ 1310 /* Clean up queue if present */
1309 mrq = slot->mrq; 1311 mrq = slot->mrq;
@@ -1353,8 +1355,6 @@ static void dw_mci_tasklet_card(unsigned long data)
1353 1355
1354 /* Power down slot */ 1356 /* Power down slot */
1355 if (present == 0) { 1357 if (present == 0) {
1356 if (host->pdata->setpower)
1357 host->pdata->setpower(slot->id, 0);
1358 clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); 1358 clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
1359 1359
1360 /* 1360 /*
@@ -1376,7 +1376,12 @@ static void dw_mci_tasklet_card(unsigned long data)
1376 1376
1377 } 1377 }
1378 1378
1379 spin_unlock(&host->lock); 1379 spin_unlock_bh(&host->lock);
1380
1381 /* Power down slot (after spin_unlock, may sleep) */
1382 if (present == 0 && host->pdata->setpower)
1383 host->pdata->setpower(slot->id, 0);
1384
1380 present = dw_mci_get_cd(mmc); 1385 present = dw_mci_get_cd(mmc);
1381 } 1386 }
1382 1387
@@ -1476,7 +1481,7 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
1476 * Card may have been plugged in prior to boot so we 1481 * Card may have been plugged in prior to boot so we
1477 * need to run the detect tasklet 1482 * need to run the detect tasklet
1478 */ 1483 */
1479 tasklet_schedule(&host->card_tasklet); 1484 queue_work(dw_mci_card_workqueue, &host->card_work);
1480 1485
1481 return 0; 1486 return 0;
1482} 1487}
@@ -1665,12 +1670,15 @@ static int dw_mci_probe(struct platform_device *pdev)
1665 mci_writel(host, CLKSRC, 0); 1670 mci_writel(host, CLKSRC, 0);
1666 1671
1667 tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); 1672 tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
1668 tasklet_init(&host->card_tasklet, 1673 dw_mci_card_workqueue = alloc_workqueue("dw-mci-card",
1669 dw_mci_tasklet_card, (unsigned long)host); 1674 WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1);
1675 if (!dw_mci_card_workqueue)
1676 goto err_dmaunmap;
1677 INIT_WORK(&host->card_work, dw_mci_work_routine_card);
1670 1678
1671 ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host); 1679 ret = request_irq(irq, dw_mci_interrupt, 0, "dw-mci", host);
1672 if (ret) 1680 if (ret)
1673 goto err_dmaunmap; 1681 goto err_workqueue;
1674 1682
1675 platform_set_drvdata(pdev, host); 1683 platform_set_drvdata(pdev, host);
1676 1684
@@ -1714,6 +1722,9 @@ err_init_slot:
1714 } 1722 }
1715 free_irq(irq, host); 1723 free_irq(irq, host);
1716 1724
1725err_workqueue:
1726 destroy_workqueue(dw_mci_card_workqueue);
1727
1717err_dmaunmap: 1728err_dmaunmap:
1718 if (host->use_dma && host->dma_ops->exit) 1729 if (host->use_dma && host->dma_ops->exit)
1719 host->dma_ops->exit(host); 1730 host->dma_ops->exit(host);
@@ -1753,6 +1764,7 @@ static int __exit dw_mci_remove(struct platform_device *pdev)
1753 mci_writel(host, CLKSRC, 0); 1764 mci_writel(host, CLKSRC, 0);
1754 1765
1755 free_irq(platform_get_irq(pdev, 0), host); 1766 free_irq(platform_get_irq(pdev, 0), host);
1767 destroy_workqueue(dw_mci_card_workqueue);
1756 dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); 1768 dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
1757 1769
1758 if (host->use_dma && host->dma_ops->exit) 1770 if (host->use_dma && host->dma_ops->exit)