diff options
author | James Hogan <james.hogan@imgtec.com> | 2011-06-24 08:55:55 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2011-07-20 17:20:58 -0400 |
commit | 1791b13ea4d97a6a7c162edd54485e932ad92f1b (patch) | |
tree | 0012ece1a9b0bf403f759d5f552fc34c6d8c82d1 /drivers/mmc/host/dw_mmc.c | |
parent | 7456caae37396fc1bc6f8e9461d07664b8c2f280 (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.c | 50 |
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 | ||
104 | static struct workqueue_struct *dw_mci_card_workqueue; | ||
105 | |||
103 | #if defined(CONFIG_DEBUG_FS) | 106 | #if defined(CONFIG_DEBUG_FS) |
104 | static int dw_mci_req_show(struct seq_file *s, void *v) | 107 | static 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 | ||
1277 | static void dw_mci_tasklet_card(unsigned long data) | 1280 | static 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 | ||
1725 | err_workqueue: | ||
1726 | destroy_workqueue(dw_mci_card_workqueue); | ||
1727 | |||
1717 | err_dmaunmap: | 1728 | err_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) |