diff options
Diffstat (limited to 'drivers/usb/chipidea/ci_hdrc_imx.c')
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.c | 153 |
1 files changed, 127 insertions, 26 deletions
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 0f05de7c6b6c..389f0e034259 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c | |||
@@ -23,22 +23,40 @@ | |||
23 | #include "ci.h" | 23 | #include "ci.h" |
24 | #include "ci_hdrc_imx.h" | 24 | #include "ci_hdrc_imx.h" |
25 | 25 | ||
26 | #define CI_HDRC_IMX_IMX28_WRITE_FIX BIT(0) | ||
27 | |||
28 | struct ci_hdrc_imx_platform_flag { | 26 | struct ci_hdrc_imx_platform_flag { |
29 | unsigned int flags; | 27 | unsigned int flags; |
28 | bool runtime_pm; | ||
30 | }; | 29 | }; |
31 | 30 | ||
32 | static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { | 31 | static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { |
33 | }; | 32 | }; |
34 | 33 | ||
35 | static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { | 34 | static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { |
36 | .flags = CI_HDRC_IMX_IMX28_WRITE_FIX, | 35 | .flags = CI_HDRC_IMX28_WRITE_FIX | |
36 | CI_HDRC_TURN_VBUS_EARLY_ON, | ||
37 | }; | ||
38 | |||
39 | static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = { | ||
40 | .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | | ||
41 | CI_HDRC_TURN_VBUS_EARLY_ON, | ||
42 | }; | ||
43 | |||
44 | static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = { | ||
45 | .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | | ||
46 | CI_HDRC_TURN_VBUS_EARLY_ON, | ||
47 | }; | ||
48 | |||
49 | static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { | ||
50 | .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | | ||
51 | CI_HDRC_TURN_VBUS_EARLY_ON, | ||
37 | }; | 52 | }; |
38 | 53 | ||
39 | static const struct of_device_id ci_hdrc_imx_dt_ids[] = { | 54 | static const struct of_device_id ci_hdrc_imx_dt_ids[] = { |
40 | { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, | 55 | { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, |
41 | { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, | 56 | { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, |
57 | { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, | ||
58 | { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, | ||
59 | { .compatible = "fsl,imx6sx-usb", .data = &imx6sl_usb_data}, | ||
42 | { /* sentinel */ } | 60 | { /* sentinel */ } |
43 | }; | 61 | }; |
44 | MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); | 62 | MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); |
@@ -48,6 +66,8 @@ struct ci_hdrc_imx_data { | |||
48 | struct platform_device *ci_pdev; | 66 | struct platform_device *ci_pdev; |
49 | struct clk *clk; | 67 | struct clk *clk; |
50 | struct imx_usbmisc_data *usbmisc_data; | 68 | struct imx_usbmisc_data *usbmisc_data; |
69 | bool supports_runtime_pm; | ||
70 | bool in_lpm; | ||
51 | }; | 71 | }; |
52 | 72 | ||
53 | /* Common functions shared by usbmisc drivers */ | 73 | /* Common functions shared by usbmisc drivers */ |
@@ -145,21 +165,18 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) | |||
145 | } | 165 | } |
146 | 166 | ||
147 | pdata.usb_phy = data->phy; | 167 | pdata.usb_phy = data->phy; |
148 | 168 | pdata.flags |= imx_platform_flag->flags; | |
149 | if (imx_platform_flag->flags & CI_HDRC_IMX_IMX28_WRITE_FIX) | 169 | if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) |
150 | pdata.flags |= CI_HDRC_IMX28_WRITE_FIX; | 170 | data->supports_runtime_pm = true; |
151 | 171 | ||
152 | ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); | 172 | ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); |
153 | if (ret) | 173 | if (ret) |
154 | goto err_clk; | 174 | goto err_clk; |
155 | 175 | ||
156 | if (data->usbmisc_data) { | 176 | ret = imx_usbmisc_init(data->usbmisc_data); |
157 | ret = imx_usbmisc_init(data->usbmisc_data); | 177 | if (ret) { |
158 | if (ret) { | 178 | dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret); |
159 | dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", | 179 | goto err_clk; |
160 | ret); | ||
161 | goto err_clk; | ||
162 | } | ||
163 | } | 180 | } |
164 | 181 | ||
165 | data->ci_pdev = ci_hdrc_add_device(&pdev->dev, | 182 | data->ci_pdev = ci_hdrc_add_device(&pdev->dev, |
@@ -173,19 +190,20 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) | |||
173 | goto err_clk; | 190 | goto err_clk; |
174 | } | 191 | } |
175 | 192 | ||
176 | if (data->usbmisc_data) { | 193 | ret = imx_usbmisc_init_post(data->usbmisc_data); |
177 | ret = imx_usbmisc_init_post(data->usbmisc_data); | 194 | if (ret) { |
178 | if (ret) { | 195 | dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret); |
179 | dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", | 196 | goto disable_device; |
180 | ret); | ||
181 | goto disable_device; | ||
182 | } | ||
183 | } | 197 | } |
184 | 198 | ||
185 | platform_set_drvdata(pdev, data); | 199 | platform_set_drvdata(pdev, data); |
186 | 200 | ||
187 | pm_runtime_no_callbacks(&pdev->dev); | 201 | if (data->supports_runtime_pm) { |
188 | pm_runtime_enable(&pdev->dev); | 202 | pm_runtime_set_active(&pdev->dev); |
203 | pm_runtime_enable(&pdev->dev); | ||
204 | } | ||
205 | |||
206 | device_set_wakeup_capable(&pdev->dev, true); | ||
189 | 207 | ||
190 | return 0; | 208 | return 0; |
191 | 209 | ||
@@ -200,14 +218,18 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) | |||
200 | { | 218 | { |
201 | struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); | 219 | struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); |
202 | 220 | ||
203 | pm_runtime_disable(&pdev->dev); | 221 | if (data->supports_runtime_pm) { |
222 | pm_runtime_get_sync(&pdev->dev); | ||
223 | pm_runtime_disable(&pdev->dev); | ||
224 | pm_runtime_put_noidle(&pdev->dev); | ||
225 | } | ||
204 | ci_hdrc_remove_device(data->ci_pdev); | 226 | ci_hdrc_remove_device(data->ci_pdev); |
205 | clk_disable_unprepare(data->clk); | 227 | clk_disable_unprepare(data->clk); |
206 | 228 | ||
207 | return 0; | 229 | return 0; |
208 | } | 230 | } |
209 | 231 | ||
210 | #ifdef CONFIG_PM_SLEEP | 232 | #ifdef CONFIG_PM |
211 | static int imx_controller_suspend(struct device *dev) | 233 | static int imx_controller_suspend(struct device *dev) |
212 | { | 234 | { |
213 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); | 235 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
@@ -215,6 +237,7 @@ static int imx_controller_suspend(struct device *dev) | |||
215 | dev_dbg(dev, "at %s\n", __func__); | 237 | dev_dbg(dev, "at %s\n", __func__); |
216 | 238 | ||
217 | clk_disable_unprepare(data->clk); | 239 | clk_disable_unprepare(data->clk); |
240 | data->in_lpm = true; | ||
218 | 241 | ||
219 | return 0; | 242 | return 0; |
220 | } | 243 | } |
@@ -222,25 +245,103 @@ static int imx_controller_suspend(struct device *dev) | |||
222 | static int imx_controller_resume(struct device *dev) | 245 | static int imx_controller_resume(struct device *dev) |
223 | { | 246 | { |
224 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); | 247 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
248 | int ret = 0; | ||
225 | 249 | ||
226 | dev_dbg(dev, "at %s\n", __func__); | 250 | dev_dbg(dev, "at %s\n", __func__); |
227 | 251 | ||
228 | return clk_prepare_enable(data->clk); | 252 | if (!data->in_lpm) { |
253 | WARN_ON(1); | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | ret = clk_prepare_enable(data->clk); | ||
258 | if (ret) | ||
259 | return ret; | ||
260 | |||
261 | data->in_lpm = false; | ||
262 | |||
263 | ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false); | ||
264 | if (ret) { | ||
265 | dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); | ||
266 | goto clk_disable; | ||
267 | } | ||
268 | |||
269 | return 0; | ||
270 | |||
271 | clk_disable: | ||
272 | clk_disable_unprepare(data->clk); | ||
273 | return ret; | ||
229 | } | 274 | } |
230 | 275 | ||
276 | #ifdef CONFIG_PM_SLEEP | ||
231 | static int ci_hdrc_imx_suspend(struct device *dev) | 277 | static int ci_hdrc_imx_suspend(struct device *dev) |
232 | { | 278 | { |
279 | int ret; | ||
280 | |||
281 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); | ||
282 | |||
283 | if (data->in_lpm) | ||
284 | /* The core's suspend doesn't run */ | ||
285 | return 0; | ||
286 | |||
287 | if (device_may_wakeup(dev)) { | ||
288 | ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); | ||
289 | if (ret) { | ||
290 | dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", | ||
291 | ret); | ||
292 | return ret; | ||
293 | } | ||
294 | } | ||
295 | |||
233 | return imx_controller_suspend(dev); | 296 | return imx_controller_suspend(dev); |
234 | } | 297 | } |
235 | 298 | ||
236 | static int ci_hdrc_imx_resume(struct device *dev) | 299 | static int ci_hdrc_imx_resume(struct device *dev) |
237 | { | 300 | { |
238 | return imx_controller_resume(dev); | 301 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
302 | int ret; | ||
303 | |||
304 | ret = imx_controller_resume(dev); | ||
305 | if (!ret && data->supports_runtime_pm) { | ||
306 | pm_runtime_disable(dev); | ||
307 | pm_runtime_set_active(dev); | ||
308 | pm_runtime_enable(dev); | ||
309 | } | ||
310 | |||
311 | return ret; | ||
239 | } | 312 | } |
240 | #endif /* CONFIG_PM_SLEEP */ | 313 | #endif /* CONFIG_PM_SLEEP */ |
241 | 314 | ||
315 | static int ci_hdrc_imx_runtime_suspend(struct device *dev) | ||
316 | { | ||
317 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); | ||
318 | int ret; | ||
319 | |||
320 | if (data->in_lpm) { | ||
321 | WARN_ON(1); | ||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); | ||
326 | if (ret) { | ||
327 | dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); | ||
328 | return ret; | ||
329 | } | ||
330 | |||
331 | return imx_controller_suspend(dev); | ||
332 | } | ||
333 | |||
334 | static int ci_hdrc_imx_runtime_resume(struct device *dev) | ||
335 | { | ||
336 | return imx_controller_resume(dev); | ||
337 | } | ||
338 | |||
339 | #endif /* CONFIG_PM */ | ||
340 | |||
242 | static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { | 341 | static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { |
243 | SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) | 342 | SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) |
343 | SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, | ||
344 | ci_hdrc_imx_runtime_resume, NULL) | ||
244 | }; | 345 | }; |
245 | static struct platform_driver ci_hdrc_imx_driver = { | 346 | static struct platform_driver ci_hdrc_imx_driver = { |
246 | .probe = ci_hdrc_imx_probe, | 347 | .probe = ci_hdrc_imx_probe, |