summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/host/sh_mobile_sdhi.c6
-rw-r--r--drivers/mmc/host/tmio_mmc.c11
-rw-r--r--drivers/mmc/host/tmio_mmc.h10
-rw-r--r--drivers/mmc/host/tmio_mmc_dma.c21
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c57
5 files changed, 86 insertions, 19 deletions
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index cc701236d16f..f60e954dec64 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -143,10 +143,16 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
return 0;
}
+static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
+ .suspend = tmio_mmc_host_suspend,
+ .resume = tmio_mmc_host_resume,
+};
+
static struct platform_driver sh_mobile_sdhi_driver = {
.driver = {
.name = "sh_mobile_sdhi",
.owner = THIS_MODULE,
+ .pm = &tmio_mmc_dev_pm_ops,
},
.probe = sh_mobile_sdhi_probe,
.remove = __devexit_p(sh_mobile_sdhi_remove),
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index 79c568461d59..be739f7ef422 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -30,7 +30,7 @@ static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state)
struct mmc_host *mmc = platform_get_drvdata(dev);
int ret;
- ret = mmc_suspend_host(mmc);
+ ret = tmio_mmc_host_suspend(&dev->dev);
/* Tell MFD core it can disable us now.*/
if (!ret && cell->disable)
@@ -46,15 +46,12 @@ static int tmio_mmc_resume(struct platform_device *dev)
int ret = 0;
/* Tell the MFD core we are ready to be enabled */
- if (cell->resume) {
+ if (cell->resume)
ret = cell->resume(dev);
- if (ret)
- goto out;
- }
- mmc_resume_host(mmc);
+ if (!ret)
+ ret = tmio_mmc_host_resume(&dev->dev);
-out:
return ret;
}
#else
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 099ed49a259b..c1470e6b138d 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -51,6 +51,8 @@ struct tmio_mmc_host {
void (*set_pwr)(struct platform_device *host, int state);
void (*set_clk_div)(struct platform_device *host, int state);
+ int pm_error;
+
/* pio related stuff */
struct scatterlist *sg_ptr;
struct scatterlist *sg_orig;
@@ -120,4 +122,12 @@ static inline void tmio_mmc_release_dma(struct tmio_mmc_host *host)
}
#endif
+#ifdef CONFIG_PM
+int tmio_mmc_host_suspend(struct device *dev);
+int tmio_mmc_host_resume(struct device *dev);
+#else
+#define tmio_mmc_host_suspend NULL
+#define tmio_mmc_host_resume NULL
+#endif
+
#endif
diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c
index d3de74ab633e..25f1ad6cbe09 100644
--- a/drivers/mmc/host/tmio_mmc_dma.c
+++ b/drivers/mmc/host/tmio_mmc_dma.c
@@ -256,7 +256,10 @@ static bool tmio_mmc_filter(struct dma_chan *chan, void *arg)
void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata)
{
/* We can only either use DMA for both Tx and Rx or not use it at all */
- if (pdata->dma) {
+ if (!pdata->dma)
+ return;
+
+ if (!host->chan_tx && !host->chan_rx) {
dma_cap_mask_t mask;
dma_cap_zero(mask);
@@ -284,18 +287,18 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
tasklet_init(&host->dma_complete, tmio_mmc_tasklet_fn, (unsigned long)host);
tasklet_init(&host->dma_issue, tmio_mmc_issue_tasklet_fn, (unsigned long)host);
+ }
- tmio_mmc_enable_dma(host, true);
+ tmio_mmc_enable_dma(host, true);
+
+ return;
- return;
ebouncebuf:
- dma_release_channel(host->chan_rx);
- host->chan_rx = NULL;
+ dma_release_channel(host->chan_rx);
+ host->chan_rx = NULL;
ereqrx:
- dma_release_channel(host->chan_tx);
- host->chan_tx = NULL;
- return;
- }
+ dma_release_channel(host->chan_tx);
+ host->chan_tx = NULL;
}
void tmio_mmc_release_dma(struct tmio_mmc_host *host)
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index 710339a85c84..8b05d3ceb29c 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -39,6 +39,7 @@
#include <linux/module.h>
#include <linux/pagemap.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/scatterlist.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h>
@@ -834,12 +835,17 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
else
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ pm_runtime_enable(&pdev->dev);
+ ret = pm_runtime_resume(&pdev->dev);
+ if (ret < 0)
+ goto pm_disable;
+
tmio_mmc_clk_stop(_host);
tmio_mmc_reset(_host);
ret = platform_get_irq(pdev, 0);
if (ret < 0)
- goto unmap_ctl;
+ goto pm_suspend;
_host->irq = ret;
@@ -850,7 +856,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
ret = request_irq(_host->irq, tmio_mmc_irq, IRQF_DISABLED |
IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), _host);
if (ret)
- goto unmap_ctl;
+ goto pm_suspend;
spin_lock_init(&_host->lock);
@@ -860,6 +866,9 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
/* See if we also get DMA */
tmio_mmc_request_dma(_host, pdata);
+ /* We have to keep the device powered for its card detection to work */
+ pm_runtime_get_noresume(&pdev->dev);
+
mmc_add_host(mmc);
/* Unmask the IRQs we want to know about */
@@ -874,7 +883,10 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
return 0;
-unmap_ctl:
+pm_suspend:
+ pm_runtime_suspend(&pdev->dev);
+pm_disable:
+ pm_runtime_disable(&pdev->dev);
iounmap(_host->ctl);
host_free:
mmc_free_host(mmc);
@@ -885,13 +897,52 @@ EXPORT_SYMBOL(tmio_mmc_host_probe);
void tmio_mmc_host_remove(struct tmio_mmc_host *host)
{
+ struct platform_device *pdev = host->pdev;
+
mmc_remove_host(host->mmc);
cancel_delayed_work_sync(&host->delayed_reset_work);
tmio_mmc_release_dma(host);
free_irq(host->irq, host);
iounmap(host->ctl);
mmc_free_host(host->mmc);
+
+ /* Compensate for pm_runtime_get_sync() in probe() above */
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
}
EXPORT_SYMBOL(tmio_mmc_host_remove);
+#ifdef CONFIG_PM
+int tmio_mmc_host_suspend(struct device *dev)
+{
+ struct mmc_host *mmc = dev_get_drvdata(dev);
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ int ret = mmc_suspend_host(mmc);
+
+ if (!ret)
+ tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
+
+ host->pm_error = pm_runtime_put_sync(dev);
+
+ return ret;
+}
+EXPORT_SYMBOL(tmio_mmc_host_suspend);
+
+int tmio_mmc_host_resume(struct device *dev)
+{
+ struct mmc_host *mmc = dev_get_drvdata(dev);
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+
+ if (!host->pm_error)
+ pm_runtime_get_sync(dev);
+
+ tmio_mmc_reset(mmc_priv(mmc));
+ tmio_mmc_request_dma(host, host->pdata);
+
+ return mmc_resume_host(mmc);
+}
+EXPORT_SYMBOL(tmio_mmc_host_resume);
+
+#endif /* CONFIG_PM */
+
MODULE_LICENSE("GPL v2");
OpenPOWER on IntegriCloud