aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/ufs
diff options
context:
space:
mode:
authorSujit Reddy Thumma <sthumma@codeaurora.org>2013-07-29 15:06:00 -0400
committerJames Bottomley <JBottomley@Parallels.com>2013-08-26 04:51:26 -0400
commit62694735ca95c74dac4eb9068d59801ac0ddebaf (patch)
tree513e9886fe0edb36b7c4722d3657bd8da85ff11b /drivers/scsi/ufs
parent66ec6d59407baf7c7a1f99c79d632403c1064266 (diff)
[SCSI] ufs: Add runtime PM support for UFS host controller driver
Add runtime PM helpers to suspend/resume UFS controller at runtime. Enable runtime PM by default for pci and platform drivers as the initialized hardware can suspend if it is not used after bootup. Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org> Signed-off-by: Santosh Y <santoshsy@gmail.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/ufs')
-rw-r--r--drivers/scsi/ufs/ufshcd-pci.c62
-rw-r--r--drivers/scsi/ufs/ufshcd-pltfrm.c50
-rw-r--r--drivers/scsi/ufs/ufshcd.c8
3 files changed, 113 insertions, 7 deletions
diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c
index 48be39a6f6d7..57ea9dd40bf4 100644
--- a/drivers/scsi/ufs/ufshcd-pci.c
+++ b/drivers/scsi/ufs/ufshcd-pci.c
@@ -35,6 +35,7 @@
35 35
36#include "ufshcd.h" 36#include "ufshcd.h"
37#include <linux/pci.h> 37#include <linux/pci.h>
38#include <linux/pm_runtime.h>
38 39
39#ifdef CONFIG_PM 40#ifdef CONFIG_PM
40/** 41/**
@@ -44,7 +45,7 @@
44 * 45 *
45 * Returns -ENOSYS 46 * Returns -ENOSYS
46 */ 47 */
47static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state) 48static int ufshcd_pci_suspend(struct device *dev)
48{ 49{
49 /* 50 /*
50 * TODO: 51 * TODO:
@@ -61,7 +62,7 @@ static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state)
61 * 62 *
62 * Returns -ENOSYS 63 * Returns -ENOSYS
63 */ 64 */
64static int ufshcd_pci_resume(struct pci_dev *pdev) 65static int ufshcd_pci_resume(struct device *dev)
65{ 66{
66 /* 67 /*
67 * TODO: 68 * TODO:
@@ -71,8 +72,45 @@ static int ufshcd_pci_resume(struct pci_dev *pdev)
71 72
72 return -ENOSYS; 73 return -ENOSYS;
73} 74}
75#else
76#define ufshcd_pci_suspend NULL
77#define ufshcd_pci_resume NULL
74#endif /* CONFIG_PM */ 78#endif /* CONFIG_PM */
75 79
80#ifdef CONFIG_PM_RUNTIME
81static int ufshcd_pci_runtime_suspend(struct device *dev)
82{
83 struct ufs_hba *hba = dev_get_drvdata(dev);
84
85 if (!hba)
86 return 0;
87
88 return ufshcd_runtime_suspend(hba);
89}
90static int ufshcd_pci_runtime_resume(struct device *dev)
91{
92 struct ufs_hba *hba = dev_get_drvdata(dev);
93
94 if (!hba)
95 return 0;
96
97 return ufshcd_runtime_resume(hba);
98}
99static int ufshcd_pci_runtime_idle(struct device *dev)
100{
101 struct ufs_hba *hba = dev_get_drvdata(dev);
102
103 if (!hba)
104 return 0;
105
106 return ufshcd_runtime_idle(hba);
107}
108#else /* !CONFIG_PM_RUNTIME */
109#define ufshcd_pci_runtime_suspend NULL
110#define ufshcd_pci_runtime_resume NULL
111#define ufshcd_pci_runtime_idle NULL
112#endif /* CONFIG_PM_RUNTIME */
113
76/** 114/**
77 * ufshcd_pci_shutdown - main function to put the controller in reset state 115 * ufshcd_pci_shutdown - main function to put the controller in reset state
78 * @pdev: pointer to PCI device handle 116 * @pdev: pointer to PCI device handle
@@ -91,6 +129,9 @@ static void ufshcd_pci_remove(struct pci_dev *pdev)
91{ 129{
92 struct ufs_hba *hba = pci_get_drvdata(pdev); 130 struct ufs_hba *hba = pci_get_drvdata(pdev);
93 131
132 pm_runtime_forbid(&pdev->dev);
133 pm_runtime_get_noresume(&pdev->dev);
134
94 disable_irq(pdev->irq); 135 disable_irq(pdev->irq);
95 ufshcd_remove(hba); 136 ufshcd_remove(hba);
96 pci_release_regions(pdev); 137 pci_release_regions(pdev);
@@ -168,6 +209,8 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
168 } 209 }
169 210
170 pci_set_drvdata(pdev, hba); 211 pci_set_drvdata(pdev, hba);
212 pm_runtime_put_noidle(&pdev->dev);
213 pm_runtime_allow(&pdev->dev);
171 214
172 return 0; 215 return 0;
173 216
@@ -182,6 +225,14 @@ out_error:
182 return err; 225 return err;
183} 226}
184 227
228static const struct dev_pm_ops ufshcd_pci_pm_ops = {
229 .suspend = ufshcd_pci_suspend,
230 .resume = ufshcd_pci_resume,
231 .runtime_suspend = ufshcd_pci_runtime_suspend,
232 .runtime_resume = ufshcd_pci_runtime_resume,
233 .runtime_idle = ufshcd_pci_runtime_idle,
234};
235
185static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = { 236static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = {
186 { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 237 { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
187 { } /* terminate list */ 238 { } /* terminate list */
@@ -195,10 +246,9 @@ static struct pci_driver ufshcd_pci_driver = {
195 .probe = ufshcd_pci_probe, 246 .probe = ufshcd_pci_probe,
196 .remove = ufshcd_pci_remove, 247 .remove = ufshcd_pci_remove,
197 .shutdown = ufshcd_pci_shutdown, 248 .shutdown = ufshcd_pci_shutdown,
198#ifdef CONFIG_PM 249 .driver = {
199 .suspend = ufshcd_pci_suspend, 250 .pm = &ufshcd_pci_pm_ops
200 .resume = ufshcd_pci_resume, 251 },
201#endif
202}; 252};
203 253
204module_pci_driver(ufshcd_pci_driver); 254module_pci_driver(ufshcd_pci_driver);
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index c42db40d4e51..c5c28357fb63 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -34,6 +34,7 @@
34 */ 34 */
35 35
36#include <linux/platform_device.h> 36#include <linux/platform_device.h>
37#include <linux/pm_runtime.h>
37 38
38#include "ufshcd.h" 39#include "ufshcd.h"
39 40
@@ -87,6 +88,40 @@ static int ufshcd_pltfrm_resume(struct device *dev)
87#define ufshcd_pltfrm_resume NULL 88#define ufshcd_pltfrm_resume NULL
88#endif 89#endif
89 90
91#ifdef CONFIG_PM_RUNTIME
92static int ufshcd_pltfrm_runtime_suspend(struct device *dev)
93{
94 struct ufs_hba *hba = dev_get_drvdata(dev);
95
96 if (!hba)
97 return 0;
98
99 return ufshcd_runtime_suspend(hba);
100}
101static int ufshcd_pltfrm_runtime_resume(struct device *dev)
102{
103 struct ufs_hba *hba = dev_get_drvdata(dev);
104
105 if (!hba)
106 return 0;
107
108 return ufshcd_runtime_resume(hba);
109}
110static int ufshcd_pltfrm_runtime_idle(struct device *dev)
111{
112 struct ufs_hba *hba = dev_get_drvdata(dev);
113
114 if (!hba)
115 return 0;
116
117 return ufshcd_runtime_idle(hba);
118}
119#else /* !CONFIG_PM_RUNTIME */
120#define ufshcd_pltfrm_runtime_suspend NULL
121#define ufshcd_pltfrm_runtime_resume NULL
122#define ufshcd_pltfrm_runtime_idle NULL
123#endif /* CONFIG_PM_RUNTIME */
124
90/** 125/**
91 * ufshcd_pltfrm_probe - probe routine of the driver 126 * ufshcd_pltfrm_probe - probe routine of the driver
92 * @pdev: pointer to Platform device handle 127 * @pdev: pointer to Platform device handle
@@ -122,14 +157,22 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev)
122 goto out; 157 goto out;
123 } 158 }
124 159
160 pm_runtime_set_active(&pdev->dev);
161 pm_runtime_enable(&pdev->dev);
162
125 err = ufshcd_init(dev, &hba, mmio_base, irq); 163 err = ufshcd_init(dev, &hba, mmio_base, irq);
126 if (err) { 164 if (err) {
127 dev_err(dev, "Intialization failed\n"); 165 dev_err(dev, "Intialization failed\n");
128 goto out; 166 goto out_disable_rpm;
129 } 167 }
130 168
131 platform_set_drvdata(pdev, hba); 169 platform_set_drvdata(pdev, hba);
132 170
171 return 0;
172
173out_disable_rpm:
174 pm_runtime_disable(&pdev->dev);
175 pm_runtime_set_suspended(&pdev->dev);
133out: 176out:
134 return err; 177 return err;
135} 178}
@@ -144,6 +187,8 @@ static int ufshcd_pltfrm_remove(struct platform_device *pdev)
144{ 187{
145 struct ufs_hba *hba = platform_get_drvdata(pdev); 188 struct ufs_hba *hba = platform_get_drvdata(pdev);
146 189
190 pm_runtime_get_sync(&(pdev)->dev);
191
147 disable_irq(hba->irq); 192 disable_irq(hba->irq);
148 ufshcd_remove(hba); 193 ufshcd_remove(hba);
149 return 0; 194 return 0;
@@ -157,6 +202,9 @@ static const struct of_device_id ufs_of_match[] = {
157static const struct dev_pm_ops ufshcd_dev_pm_ops = { 202static const struct dev_pm_ops ufshcd_dev_pm_ops = {
158 .suspend = ufshcd_pltfrm_suspend, 203 .suspend = ufshcd_pltfrm_suspend,
159 .resume = ufshcd_pltfrm_resume, 204 .resume = ufshcd_pltfrm_resume,
205 .runtime_suspend = ufshcd_pltfrm_runtime_suspend,
206 .runtime_resume = ufshcd_pltfrm_runtime_resume,
207 .runtime_idle = ufshcd_pltfrm_runtime_idle,
160}; 208};
161 209
162static struct platform_driver ufshcd_pltfrm_driver = { 210static struct platform_driver ufshcd_pltfrm_driver = {
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 4267246df516..1f2caa01ec92 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2169,6 +2169,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work)
2169 u32 status = 0; 2169 u32 status = 0;
2170 hba = container_of(work, struct ufs_hba, eeh_work); 2170 hba = container_of(work, struct ufs_hba, eeh_work);
2171 2171
2172 pm_runtime_get_sync(hba->dev);
2172 err = ufshcd_get_ee_status(hba, &status); 2173 err = ufshcd_get_ee_status(hba, &status);
2173 if (err) { 2174 if (err) {
2174 dev_err(hba->dev, "%s: failed to get exception status %d\n", 2175 dev_err(hba->dev, "%s: failed to get exception status %d\n",
@@ -2184,6 +2185,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work)
2184 __func__, err); 2185 __func__, err);
2185 } 2186 }
2186out: 2187out:
2188 pm_runtime_put_sync(hba->dev);
2187 return; 2189 return;
2188} 2190}
2189 2191
@@ -2196,9 +2198,11 @@ static void ufshcd_fatal_err_handler(struct work_struct *work)
2196 struct ufs_hba *hba; 2198 struct ufs_hba *hba;
2197 hba = container_of(work, struct ufs_hba, feh_workq); 2199 hba = container_of(work, struct ufs_hba, feh_workq);
2198 2200
2201 pm_runtime_get_sync(hba->dev);
2199 /* check if reset is already in progress */ 2202 /* check if reset is already in progress */
2200 if (hba->ufshcd_state != UFSHCD_STATE_RESET) 2203 if (hba->ufshcd_state != UFSHCD_STATE_RESET)
2201 ufshcd_do_reset(hba); 2204 ufshcd_do_reset(hba);
2205 pm_runtime_put_sync(hba->dev);
2202} 2206}
2203 2207
2204/** 2208/**
@@ -2500,6 +2504,7 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
2500 2504
2501 ufshcd_force_reset_auto_bkops(hba); 2505 ufshcd_force_reset_auto_bkops(hba);
2502 scsi_scan_host(hba->host); 2506 scsi_scan_host(hba->host);
2507 pm_runtime_put_sync(hba->dev);
2503out: 2508out:
2504 return; 2509 return;
2505} 2510}
@@ -2721,6 +2726,9 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle,
2721 2726
2722 *hba_handle = hba; 2727 *hba_handle = hba;
2723 2728
2729 /* Hold auto suspend until async scan completes */
2730 pm_runtime_get_sync(dev);
2731
2724 async_schedule(ufshcd_async_scan, hba); 2732 async_schedule(ufshcd_async_scan, hba);
2725 2733
2726 return 0; 2734 return 0;