aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>2011-05-05 12:13:12 -0400
committerChris Ball <cjb@laptop.org>2011-05-24 23:53:51 -0400
commite6ee7182c3b22afe0b983eac89dc020a93a13179 (patch)
treeed40843e6e306917818f00aa00dc0a3c37d98d31 /drivers/mmc
parent3b0beafc92406b98cbc2749b8db736f313d390b1 (diff)
mmc: Add runtime and system-wide PM to the TMIO MMC driver
Add runtime and system-wide power management to the TMIO MMC driver in PIO and DMA modes, allowing it to properly save and restore its state during system suspend. Runtime PM is very crude ATM, because the controller has to be powered on all the time to detect card hotplug events. Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-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)
143 return 0; 143 return 0;
144} 144}
145 145
146static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
147 .suspend = tmio_mmc_host_suspend,
148 .resume = tmio_mmc_host_resume,
149};
150
146static struct platform_driver sh_mobile_sdhi_driver = { 151static struct platform_driver sh_mobile_sdhi_driver = {
147 .driver = { 152 .driver = {
148 .name = "sh_mobile_sdhi", 153 .name = "sh_mobile_sdhi",
149 .owner = THIS_MODULE, 154 .owner = THIS_MODULE,
155 .pm = &tmio_mmc_dev_pm_ops,
150 }, 156 },
151 .probe = sh_mobile_sdhi_probe, 157 .probe = sh_mobile_sdhi_probe,
152 .remove = __devexit_p(sh_mobile_sdhi_remove), 158 .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)
30 struct mmc_host *mmc = platform_get_drvdata(dev); 30 struct mmc_host *mmc = platform_get_drvdata(dev);
31 int ret; 31 int ret;
32 32
33 ret = mmc_suspend_host(mmc); 33 ret = tmio_mmc_host_suspend(&dev->dev);
34 34
35 /* Tell MFD core it can disable us now.*/ 35 /* Tell MFD core it can disable us now.*/
36 if (!ret && cell->disable) 36 if (!ret && cell->disable)
@@ -46,15 +46,12 @@ static int tmio_mmc_resume(struct platform_device *dev)
46 int ret = 0; 46 int ret = 0;
47 47
48 /* Tell the MFD core we are ready to be enabled */ 48 /* Tell the MFD core we are ready to be enabled */
49 if (cell->resume) { 49 if (cell->resume)
50 ret = cell->resume(dev); 50 ret = cell->resume(dev);
51 if (ret)
52 goto out;
53 }
54 51
55 mmc_resume_host(mmc); 52 if (!ret)
53 ret = tmio_mmc_host_resume(&dev->dev);
56 54
57out:
58 return ret; 55 return ret;
59} 56}
60#else 57#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 {
51 void (*set_pwr)(struct platform_device *host, int state); 51 void (*set_pwr)(struct platform_device *host, int state);
52 void (*set_clk_div)(struct platform_device *host, int state); 52 void (*set_clk_div)(struct platform_device *host, int state);
53 53
54 int pm_error;
55
54 /* pio related stuff */ 56 /* pio related stuff */
55 struct scatterlist *sg_ptr; 57 struct scatterlist *sg_ptr;
56 struct scatterlist *sg_orig; 58 struct scatterlist *sg_orig;
@@ -120,4 +122,12 @@ static inline void tmio_mmc_release_dma(struct tmio_mmc_host *host)
120} 122}
121#endif 123#endif
122 124
125#ifdef CONFIG_PM
126int tmio_mmc_host_suspend(struct device *dev);
127int tmio_mmc_host_resume(struct device *dev);
128#else
129#define tmio_mmc_host_suspend NULL
130#define tmio_mmc_host_resume NULL
131#endif
132
123#endif 133#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)
256void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata) 256void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata)
257{ 257{
258 /* We can only either use DMA for both Tx and Rx or not use it at all */ 258 /* We can only either use DMA for both Tx and Rx or not use it at all */
259 if (pdata->dma) { 259 if (!pdata->dma)
260 return;
261
262 if (!host->chan_tx && !host->chan_rx) {
260 dma_cap_mask_t mask; 263 dma_cap_mask_t mask;
261 264
262 dma_cap_zero(mask); 265 dma_cap_zero(mask);
@@ -284,18 +287,18 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
284 287
285 tasklet_init(&host->dma_complete, tmio_mmc_tasklet_fn, (unsigned long)host); 288 tasklet_init(&host->dma_complete, tmio_mmc_tasklet_fn, (unsigned long)host);
286 tasklet_init(&host->dma_issue, tmio_mmc_issue_tasklet_fn, (unsigned long)host); 289 tasklet_init(&host->dma_issue, tmio_mmc_issue_tasklet_fn, (unsigned long)host);
290 }
287 291
288 tmio_mmc_enable_dma(host, true); 292 tmio_mmc_enable_dma(host, true);
293
294 return;
289 295
290 return;
291ebouncebuf: 296ebouncebuf:
292 dma_release_channel(host->chan_rx); 297 dma_release_channel(host->chan_rx);
293 host->chan_rx = NULL; 298 host->chan_rx = NULL;
294ereqrx: 299ereqrx:
295 dma_release_channel(host->chan_tx); 300 dma_release_channel(host->chan_tx);
296 host->chan_tx = NULL; 301 host->chan_tx = NULL;
297 return;
298 }
299} 302}
300 303
301void tmio_mmc_release_dma(struct tmio_mmc_host *host) 304void 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 @@
39#include <linux/module.h> 39#include <linux/module.h>
40#include <linux/pagemap.h> 40#include <linux/pagemap.h>
41#include <linux/platform_device.h> 41#include <linux/platform_device.h>
42#include <linux/pm_runtime.h>
42#include <linux/scatterlist.h> 43#include <linux/scatterlist.h>
43#include <linux/workqueue.h> 44#include <linux/workqueue.h>
44#include <linux/spinlock.h> 45#include <linux/spinlock.h>
@@ -834,12 +835,17 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
834 else 835 else
835 mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 836 mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
836 837
838 pm_runtime_enable(&pdev->dev);
839 ret = pm_runtime_resume(&pdev->dev);
840 if (ret < 0)
841 goto pm_disable;
842
837 tmio_mmc_clk_stop(_host); 843 tmio_mmc_clk_stop(_host);
838 tmio_mmc_reset(_host); 844 tmio_mmc_reset(_host);
839 845
840 ret = platform_get_irq(pdev, 0); 846 ret = platform_get_irq(pdev, 0);
841 if (ret < 0) 847 if (ret < 0)
842 goto unmap_ctl; 848 goto pm_suspend;
843 849
844 _host->irq = ret; 850 _host->irq = ret;
845 851
@@ -850,7 +856,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
850 ret = request_irq(_host->irq, tmio_mmc_irq, IRQF_DISABLED | 856 ret = request_irq(_host->irq, tmio_mmc_irq, IRQF_DISABLED |
851 IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), _host); 857 IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), _host);
852 if (ret) 858 if (ret)
853 goto unmap_ctl; 859 goto pm_suspend;
854 860
855 spin_lock_init(&_host->lock); 861 spin_lock_init(&_host->lock);
856 862
@@ -860,6 +866,9 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
860 /* See if we also get DMA */ 866 /* See if we also get DMA */
861 tmio_mmc_request_dma(_host, pdata); 867 tmio_mmc_request_dma(_host, pdata);
862 868
869 /* We have to keep the device powered for its card detection to work */
870 pm_runtime_get_noresume(&pdev->dev);
871
863 mmc_add_host(mmc); 872 mmc_add_host(mmc);
864 873
865 /* Unmask the IRQs we want to know about */ 874 /* Unmask the IRQs we want to know about */
@@ -874,7 +883,10 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
874 883
875 return 0; 884 return 0;
876 885
877unmap_ctl: 886pm_suspend:
887 pm_runtime_suspend(&pdev->dev);
888pm_disable:
889 pm_runtime_disable(&pdev->dev);
878 iounmap(_host->ctl); 890 iounmap(_host->ctl);
879host_free: 891host_free:
880 mmc_free_host(mmc); 892 mmc_free_host(mmc);
@@ -885,13 +897,52 @@ EXPORT_SYMBOL(tmio_mmc_host_probe);
885 897
886void tmio_mmc_host_remove(struct tmio_mmc_host *host) 898void tmio_mmc_host_remove(struct tmio_mmc_host *host)
887{ 899{
900 struct platform_device *pdev = host->pdev;
901
888 mmc_remove_host(host->mmc); 902 mmc_remove_host(host->mmc);
889 cancel_delayed_work_sync(&host->delayed_reset_work); 903 cancel_delayed_work_sync(&host->delayed_reset_work);
890 tmio_mmc_release_dma(host); 904 tmio_mmc_release_dma(host);
891 free_irq(host->irq, host); 905 free_irq(host->irq, host);
892 iounmap(host->ctl); 906 iounmap(host->ctl);
893 mmc_free_host(host->mmc); 907 mmc_free_host(host->mmc);
908
909 /* Compensate for pm_runtime_get_sync() in probe() above */
910 pm_runtime_put_sync(&pdev->dev);
911 pm_runtime_disable(&pdev->dev);
894} 912}
895EXPORT_SYMBOL(tmio_mmc_host_remove); 913EXPORT_SYMBOL(tmio_mmc_host_remove);
896 914
915#ifdef CONFIG_PM
916int tmio_mmc_host_suspend(struct device *dev)
917{
918 struct mmc_host *mmc = dev_get_drvdata(dev);
919 struct tmio_mmc_host *host = mmc_priv(mmc);
920 int ret = mmc_suspend_host(mmc);
921
922 if (!ret)
923 tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
924
925 host->pm_error = pm_runtime_put_sync(dev);
926
927 return ret;
928}
929EXPORT_SYMBOL(tmio_mmc_host_suspend);
930
931int tmio_mmc_host_resume(struct device *dev)
932{
933 struct mmc_host *mmc = dev_get_drvdata(dev);
934 struct tmio_mmc_host *host = mmc_priv(mmc);
935
936 if (!host->pm_error)
937 pm_runtime_get_sync(dev);
938
939 tmio_mmc_reset(mmc_priv(mmc));
940 tmio_mmc_request_dma(host, host->pdata);
941
942 return mmc_resume_host(mmc);
943}
944EXPORT_SYMBOL(tmio_mmc_host_resume);
945
946#endif /* CONFIG_PM */
947
897MODULE_LICENSE("GPL v2"); 948MODULE_LICENSE("GPL v2");