diff options
author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2011-05-05 12:13:12 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2011-05-24 23:53:51 -0400 |
commit | e6ee7182c3b22afe0b983eac89dc020a93a13179 (patch) | |
tree | ed40843e6e306917818f00aa00dc0a3c37d98d31 /drivers/mmc | |
parent | 3b0beafc92406b98cbc2749b8db736f313d390b1 (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.c | 6 | ||||
-rw-r--r-- | drivers/mmc/host/tmio_mmc.c | 11 | ||||
-rw-r--r-- | drivers/mmc/host/tmio_mmc.h | 10 | ||||
-rw-r--r-- | drivers/mmc/host/tmio_mmc_dma.c | 21 | ||||
-rw-r--r-- | drivers/mmc/host/tmio_mmc_pio.c | 57 |
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 | ||
146 | static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { | ||
147 | .suspend = tmio_mmc_host_suspend, | ||
148 | .resume = tmio_mmc_host_resume, | ||
149 | }; | ||
150 | |||
146 | static struct platform_driver sh_mobile_sdhi_driver = { | 151 | static 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 | ||
57 | out: | ||
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 | ||
126 | int tmio_mmc_host_suspend(struct device *dev); | ||
127 | int 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) | |||
256 | void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata) | 256 | void 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; | ||
291 | ebouncebuf: | 296 | ebouncebuf: |
292 | dma_release_channel(host->chan_rx); | 297 | dma_release_channel(host->chan_rx); |
293 | host->chan_rx = NULL; | 298 | host->chan_rx = NULL; |
294 | ereqrx: | 299 | ereqrx: |
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 | ||
301 | void tmio_mmc_release_dma(struct tmio_mmc_host *host) | 304 | 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 @@ | |||
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 | ||
877 | unmap_ctl: | 886 | pm_suspend: |
887 | pm_runtime_suspend(&pdev->dev); | ||
888 | pm_disable: | ||
889 | pm_runtime_disable(&pdev->dev); | ||
878 | iounmap(_host->ctl); | 890 | iounmap(_host->ctl); |
879 | host_free: | 891 | host_free: |
880 | mmc_free_host(mmc); | 892 | mmc_free_host(mmc); |
@@ -885,13 +897,52 @@ EXPORT_SYMBOL(tmio_mmc_host_probe); | |||
885 | 897 | ||
886 | void tmio_mmc_host_remove(struct tmio_mmc_host *host) | 898 | void 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 | } |
895 | EXPORT_SYMBOL(tmio_mmc_host_remove); | 913 | EXPORT_SYMBOL(tmio_mmc_host_remove); |
896 | 914 | ||
915 | #ifdef CONFIG_PM | ||
916 | int 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 | } | ||
929 | EXPORT_SYMBOL(tmio_mmc_host_suspend); | ||
930 | |||
931 | int 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 | } | ||
944 | EXPORT_SYMBOL(tmio_mmc_host_resume); | ||
945 | |||
946 | #endif /* CONFIG_PM */ | ||
947 | |||
897 | MODULE_LICENSE("GPL v2"); | 948 | MODULE_LICENSE("GPL v2"); |