diff options
author | Peter Chen <peter.chen@freescale.com> | 2015-02-10 23:44:47 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-03-18 11:19:09 -0400 |
commit | e14db48dfcf3ab6ebea212e82dc56036a00b0d6b (patch) | |
tree | bda2b70b7393c34be3cc871e1326019a98b968e2 | |
parent | f636cec559c6c01d6a262123446a142e1ae66890 (diff) |
usb: chipidea: imx: add runtime power management support
Add runtime pm support for imx, only imx6 series are supported and tested.
Signed-off-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.c | 106 |
1 files changed, 100 insertions, 6 deletions
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 353989e5675b..5ad85fe84411 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c | |||
@@ -25,6 +25,7 @@ | |||
25 | 25 | ||
26 | struct ci_hdrc_imx_platform_flag { | 26 | struct ci_hdrc_imx_platform_flag { |
27 | unsigned int flags; | 27 | unsigned int flags; |
28 | bool runtime_pm; | ||
28 | }; | 29 | }; |
29 | 30 | ||
30 | static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { | 31 | static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { |
@@ -34,9 +35,24 @@ static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { | |||
34 | .flags = CI_HDRC_IMX28_WRITE_FIX, | 35 | .flags = CI_HDRC_IMX28_WRITE_FIX, |
35 | }; | 36 | }; |
36 | 37 | ||
38 | static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = { | ||
39 | .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, | ||
40 | }; | ||
41 | |||
42 | static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = { | ||
43 | .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, | ||
44 | }; | ||
45 | |||
46 | static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { | ||
47 | .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, | ||
48 | }; | ||
49 | |||
37 | static const struct of_device_id ci_hdrc_imx_dt_ids[] = { | 50 | static const struct of_device_id ci_hdrc_imx_dt_ids[] = { |
38 | { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, | 51 | { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, |
39 | { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, | 52 | { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, |
53 | { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, | ||
54 | { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, | ||
55 | { .compatible = "fsl,imx6sx-usb", .data = &imx6sl_usb_data}, | ||
40 | { /* sentinel */ } | 56 | { /* sentinel */ } |
41 | }; | 57 | }; |
42 | MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); | 58 | MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); |
@@ -46,6 +62,8 @@ struct ci_hdrc_imx_data { | |||
46 | struct platform_device *ci_pdev; | 62 | struct platform_device *ci_pdev; |
47 | struct clk *clk; | 63 | struct clk *clk; |
48 | struct imx_usbmisc_data *usbmisc_data; | 64 | struct imx_usbmisc_data *usbmisc_data; |
65 | bool supports_runtime_pm; | ||
66 | bool in_lpm; | ||
49 | }; | 67 | }; |
50 | 68 | ||
51 | /* Common functions shared by usbmisc drivers */ | 69 | /* Common functions shared by usbmisc drivers */ |
@@ -144,6 +162,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) | |||
144 | 162 | ||
145 | pdata.usb_phy = data->phy; | 163 | pdata.usb_phy = data->phy; |
146 | pdata.flags |= imx_platform_flag->flags; | 164 | pdata.flags |= imx_platform_flag->flags; |
165 | if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) | ||
166 | data->supports_runtime_pm = true; | ||
147 | 167 | ||
148 | ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); | 168 | ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); |
149 | if (ret) | 169 | if (ret) |
@@ -174,8 +194,10 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) | |||
174 | 194 | ||
175 | platform_set_drvdata(pdev, data); | 195 | platform_set_drvdata(pdev, data); |
176 | 196 | ||
177 | pm_runtime_no_callbacks(&pdev->dev); | 197 | if (data->supports_runtime_pm) { |
178 | pm_runtime_enable(&pdev->dev); | 198 | pm_runtime_set_active(&pdev->dev); |
199 | pm_runtime_enable(&pdev->dev); | ||
200 | } | ||
179 | 201 | ||
180 | return 0; | 202 | return 0; |
181 | 203 | ||
@@ -190,14 +212,18 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) | |||
190 | { | 212 | { |
191 | struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); | 213 | struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); |
192 | 214 | ||
193 | pm_runtime_disable(&pdev->dev); | 215 | if (data->supports_runtime_pm) { |
216 | pm_runtime_get_sync(&pdev->dev); | ||
217 | pm_runtime_disable(&pdev->dev); | ||
218 | pm_runtime_put_noidle(&pdev->dev); | ||
219 | } | ||
194 | ci_hdrc_remove_device(data->ci_pdev); | 220 | ci_hdrc_remove_device(data->ci_pdev); |
195 | clk_disable_unprepare(data->clk); | 221 | clk_disable_unprepare(data->clk); |
196 | 222 | ||
197 | return 0; | 223 | return 0; |
198 | } | 224 | } |
199 | 225 | ||
200 | #ifdef CONFIG_PM_SLEEP | 226 | #ifdef CONFIG_PM |
201 | static int imx_controller_suspend(struct device *dev) | 227 | static int imx_controller_suspend(struct device *dev) |
202 | { | 228 | { |
203 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); | 229 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
@@ -205,6 +231,7 @@ static int imx_controller_suspend(struct device *dev) | |||
205 | dev_dbg(dev, "at %s\n", __func__); | 231 | dev_dbg(dev, "at %s\n", __func__); |
206 | 232 | ||
207 | clk_disable_unprepare(data->clk); | 233 | clk_disable_unprepare(data->clk); |
234 | data->in_lpm = true; | ||
208 | 235 | ||
209 | return 0; | 236 | return 0; |
210 | } | 237 | } |
@@ -212,25 +239,92 @@ static int imx_controller_suspend(struct device *dev) | |||
212 | static int imx_controller_resume(struct device *dev) | 239 | static int imx_controller_resume(struct device *dev) |
213 | { | 240 | { |
214 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); | 241 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
242 | int ret = 0; | ||
215 | 243 | ||
216 | dev_dbg(dev, "at %s\n", __func__); | 244 | dev_dbg(dev, "at %s\n", __func__); |
217 | 245 | ||
218 | return clk_prepare_enable(data->clk); | 246 | if (!data->in_lpm) { |
247 | WARN_ON(1); | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | ret = clk_prepare_enable(data->clk); | ||
252 | if (ret) | ||
253 | return ret; | ||
254 | |||
255 | data->in_lpm = false; | ||
256 | |||
257 | ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false); | ||
258 | if (ret) { | ||
259 | dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); | ||
260 | goto clk_disable; | ||
261 | } | ||
262 | |||
263 | return 0; | ||
264 | |||
265 | clk_disable: | ||
266 | clk_disable_unprepare(data->clk); | ||
267 | return ret; | ||
219 | } | 268 | } |
220 | 269 | ||
270 | #ifdef CONFIG_PM_SLEEP | ||
221 | static int ci_hdrc_imx_suspend(struct device *dev) | 271 | static int ci_hdrc_imx_suspend(struct device *dev) |
222 | { | 272 | { |
273 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); | ||
274 | |||
275 | if (data->in_lpm) | ||
276 | /* The core's suspend doesn't run */ | ||
277 | return 0; | ||
278 | |||
223 | return imx_controller_suspend(dev); | 279 | return imx_controller_suspend(dev); |
224 | } | 280 | } |
225 | 281 | ||
226 | static int ci_hdrc_imx_resume(struct device *dev) | 282 | static int ci_hdrc_imx_resume(struct device *dev) |
227 | { | 283 | { |
228 | return imx_controller_resume(dev); | 284 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
285 | int ret; | ||
286 | |||
287 | ret = imx_controller_resume(dev); | ||
288 | if (!ret && data->supports_runtime_pm) { | ||
289 | pm_runtime_disable(dev); | ||
290 | pm_runtime_set_active(dev); | ||
291 | pm_runtime_enable(dev); | ||
292 | } | ||
293 | |||
294 | return ret; | ||
229 | } | 295 | } |
230 | #endif /* CONFIG_PM_SLEEP */ | 296 | #endif /* CONFIG_PM_SLEEP */ |
231 | 297 | ||
298 | static int ci_hdrc_imx_runtime_suspend(struct device *dev) | ||
299 | { | ||
300 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); | ||
301 | int ret; | ||
302 | |||
303 | if (data->in_lpm) { | ||
304 | WARN_ON(1); | ||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); | ||
309 | if (ret) { | ||
310 | dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); | ||
311 | return ret; | ||
312 | } | ||
313 | |||
314 | return imx_controller_suspend(dev); | ||
315 | } | ||
316 | |||
317 | static int ci_hdrc_imx_runtime_resume(struct device *dev) | ||
318 | { | ||
319 | return imx_controller_resume(dev); | ||
320 | } | ||
321 | |||
322 | #endif /* CONFIG_PM */ | ||
323 | |||
232 | static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { | 324 | static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { |
233 | SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) | 325 | SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) |
326 | SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, | ||
327 | ci_hdrc_imx_runtime_resume, NULL) | ||
234 | }; | 328 | }; |
235 | static struct platform_driver ci_hdrc_imx_driver = { | 329 | static struct platform_driver ci_hdrc_imx_driver = { |
236 | .probe = ci_hdrc_imx_probe, | 330 | .probe = ci_hdrc_imx_probe, |