diff options
author | Sujit Reddy Thumma <sthumma@codeaurora.org> | 2013-07-29 15:06:00 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2013-08-26 04:51:26 -0400 |
commit | 62694735ca95c74dac4eb9068d59801ac0ddebaf (patch) | |
tree | 513e9886fe0edb36b7c4722d3657bd8da85ff11b /drivers/scsi/ufs | |
parent | 66ec6d59407baf7c7a1f99c79d632403c1064266 (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.c | 62 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufshcd-pltfrm.c | 50 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 8 |
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 | */ |
47 | static int ufshcd_pci_suspend(struct pci_dev *pdev, pm_message_t state) | 48 | static 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 | */ |
64 | static int ufshcd_pci_resume(struct pci_dev *pdev) | 65 | static 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 | ||
81 | static 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 | } | ||
90 | static 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 | } | ||
99 | static 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 | ||
228 | static 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 | |||
185 | static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = { | 236 | static 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 | ||
204 | module_pci_driver(ufshcd_pci_driver); | 254 | module_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 | ||
92 | static 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 | } | ||
101 | static 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 | } | ||
110 | static 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 | |||
173 | out_disable_rpm: | ||
174 | pm_runtime_disable(&pdev->dev); | ||
175 | pm_runtime_set_suspended(&pdev->dev); | ||
133 | out: | 176 | out: |
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[] = { | |||
157 | static const struct dev_pm_ops ufshcd_dev_pm_ops = { | 202 | static 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 | ||
162 | static struct platform_driver ufshcd_pltfrm_driver = { | 210 | static 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 | } |
2186 | out: | 2187 | out: |
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); | ||
2503 | out: | 2508 | out: |
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; |