1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright 2012 Freescale Semiconductor, Inc. |
4 | * Copyright (C) 2012 Marek Vasut <marex@denx.de> |
5 | * on behalf of DENX Software Engineering GmbH |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/of.h> |
10 | #include <linux/of_platform.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/pm_runtime.h> |
13 | #include <linux/usb/chipidea.h> |
14 | #include <linux/usb/of.h> |
15 | #include <linux/clk.h> |
16 | #include <linux/pinctrl/consumer.h> |
17 | #include <linux/pm_qos.h> |
18 | |
19 | #include "ci.h" |
20 | #include "ci_hdrc_imx.h" |
21 | |
22 | struct ci_hdrc_imx_platform_flag { |
23 | unsigned int flags; |
24 | }; |
25 | |
26 | static const struct ci_hdrc_imx_platform_flag imx23_usb_data = { |
27 | .flags = CI_HDRC_TURN_VBUS_EARLY_ON | |
28 | CI_HDRC_DISABLE_STREAMING, |
29 | }; |
30 | |
31 | static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { |
32 | .flags = CI_HDRC_DISABLE_STREAMING, |
33 | }; |
34 | |
35 | static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { |
36 | .flags = CI_HDRC_IMX28_WRITE_FIX | |
37 | CI_HDRC_TURN_VBUS_EARLY_ON | |
38 | CI_HDRC_DISABLE_STREAMING, |
39 | }; |
40 | |
41 | static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = { |
42 | .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | |
43 | CI_HDRC_TURN_VBUS_EARLY_ON | |
44 | CI_HDRC_DISABLE_STREAMING, |
45 | }; |
46 | |
47 | static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = { |
48 | .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | |
49 | CI_HDRC_TURN_VBUS_EARLY_ON | |
50 | CI_HDRC_DISABLE_HOST_STREAMING, |
51 | }; |
52 | |
53 | static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { |
54 | .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | |
55 | CI_HDRC_TURN_VBUS_EARLY_ON | |
56 | CI_HDRC_DISABLE_HOST_STREAMING, |
57 | }; |
58 | |
59 | static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = { |
60 | .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | |
61 | CI_HDRC_TURN_VBUS_EARLY_ON | |
62 | CI_HDRC_DISABLE_DEVICE_STREAMING, |
63 | }; |
64 | |
65 | static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = { |
66 | .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, |
67 | }; |
68 | |
69 | static const struct ci_hdrc_imx_platform_flag imx7ulp_usb_data = { |
70 | .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | |
71 | CI_HDRC_HAS_PORTSC_PEC_MISSED | |
72 | CI_HDRC_PMQOS, |
73 | }; |
74 | |
75 | static const struct ci_hdrc_imx_platform_flag imx8ulp_usb_data = { |
76 | .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | |
77 | CI_HDRC_HAS_PORTSC_PEC_MISSED, |
78 | }; |
79 | |
80 | static const struct of_device_id ci_hdrc_imx_dt_ids[] = { |
81 | { .compatible = "fsl,imx23-usb" , .data = &imx23_usb_data}, |
82 | { .compatible = "fsl,imx28-usb" , .data = &imx28_usb_data}, |
83 | { .compatible = "fsl,imx27-usb" , .data = &imx27_usb_data}, |
84 | { .compatible = "fsl,imx6q-usb" , .data = &imx6q_usb_data}, |
85 | { .compatible = "fsl,imx6sl-usb" , .data = &imx6sl_usb_data}, |
86 | { .compatible = "fsl,imx6sx-usb" , .data = &imx6sx_usb_data}, |
87 | { .compatible = "fsl,imx6ul-usb" , .data = &imx6ul_usb_data}, |
88 | { .compatible = "fsl,imx7d-usb" , .data = &imx7d_usb_data}, |
89 | { .compatible = "fsl,imx7ulp-usb" , .data = &imx7ulp_usb_data}, |
90 | { .compatible = "fsl,imx8ulp-usb" , .data = &imx8ulp_usb_data}, |
91 | { /* sentinel */ } |
92 | }; |
93 | MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); |
94 | |
95 | struct ci_hdrc_imx_data { |
96 | struct usb_phy *phy; |
97 | struct platform_device *ci_pdev; |
98 | struct clk *clk; |
99 | struct imx_usbmisc_data *usbmisc_data; |
100 | bool supports_runtime_pm; |
101 | bool override_phy_control; |
102 | bool in_lpm; |
103 | struct pinctrl *pinctrl; |
104 | struct pinctrl_state *pinctrl_hsic_active; |
105 | struct regulator *hsic_pad_regulator; |
106 | /* SoC before i.mx6 (except imx23/imx28) needs three clks */ |
107 | bool need_three_clks; |
108 | struct clk *clk_ipg; |
109 | struct clk *clk_ahb; |
110 | struct clk *clk_per; |
111 | /* --------------------------------- */ |
112 | struct pm_qos_request pm_qos_req; |
113 | const struct ci_hdrc_imx_platform_flag *plat_data; |
114 | }; |
115 | |
116 | /* Common functions shared by usbmisc drivers */ |
117 | |
118 | static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) |
119 | { |
120 | struct platform_device *misc_pdev; |
121 | struct device_node *np = dev->of_node; |
122 | struct of_phandle_args args; |
123 | struct imx_usbmisc_data *data; |
124 | int ret; |
125 | |
126 | /* |
127 | * In case the fsl,usbmisc property is not present this device doesn't |
128 | * need usbmisc. Return NULL (which is no error here) |
129 | */ |
130 | if (!of_get_property(node: np, name: "fsl,usbmisc" , NULL)) |
131 | return NULL; |
132 | |
133 | data = devm_kzalloc(dev, size: sizeof(*data), GFP_KERNEL); |
134 | if (!data) |
135 | return ERR_PTR(error: -ENOMEM); |
136 | |
137 | ret = of_parse_phandle_with_args(np, list_name: "fsl,usbmisc" , cells_name: "#index-cells" , |
138 | index: 0, out_args: &args); |
139 | if (ret) { |
140 | dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n" , |
141 | ret); |
142 | return ERR_PTR(error: ret); |
143 | } |
144 | |
145 | data->index = args.args[0]; |
146 | |
147 | misc_pdev = of_find_device_by_node(np: args.np); |
148 | of_node_put(node: args.np); |
149 | |
150 | if (!misc_pdev) |
151 | return ERR_PTR(error: -EPROBE_DEFER); |
152 | |
153 | if (!platform_get_drvdata(pdev: misc_pdev)) { |
154 | put_device(dev: &misc_pdev->dev); |
155 | return ERR_PTR(error: -EPROBE_DEFER); |
156 | } |
157 | data->dev = &misc_pdev->dev; |
158 | |
159 | /* |
160 | * Check the various over current related properties. If over current |
161 | * detection is disabled we're not interested in the polarity. |
162 | */ |
163 | if (of_property_read_bool(np, propname: "disable-over-current" )) { |
164 | data->disable_oc = 1; |
165 | } else if (of_property_read_bool(np, propname: "over-current-active-high" )) { |
166 | data->oc_pol_active_low = 0; |
167 | data->oc_pol_configured = 1; |
168 | } else if (of_property_read_bool(np, propname: "over-current-active-low" )) { |
169 | data->oc_pol_active_low = 1; |
170 | data->oc_pol_configured = 1; |
171 | } else { |
172 | dev_warn(dev, "No over current polarity defined\n" ); |
173 | } |
174 | |
175 | data->pwr_pol = of_property_read_bool(np, propname: "power-active-high" ); |
176 | data->evdo = of_property_read_bool(np, propname: "external-vbus-divider" ); |
177 | |
178 | if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) |
179 | data->ulpi = 1; |
180 | |
181 | if (of_property_read_u32(np, propname: "samsung,picophy-pre-emp-curr-control" , |
182 | out_value: &data->emp_curr_control)) |
183 | data->emp_curr_control = -1; |
184 | if (of_property_read_u32(np, propname: "samsung,picophy-dc-vol-level-adjust" , |
185 | out_value: &data->dc_vol_level_adjust)) |
186 | data->dc_vol_level_adjust = -1; |
187 | if (of_property_read_u32(np, propname: "fsl,picophy-rise-fall-time-adjust" , |
188 | out_value: &data->rise_fall_time_adjust)) |
189 | data->rise_fall_time_adjust = -1; |
190 | |
191 | return data; |
192 | } |
193 | |
194 | /* End of common functions shared by usbmisc drivers*/ |
195 | static int imx_get_clks(struct device *dev) |
196 | { |
197 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
198 | int ret = 0; |
199 | |
200 | data->clk_ipg = devm_clk_get(dev, id: "ipg" ); |
201 | if (IS_ERR(ptr: data->clk_ipg)) { |
202 | /* If the platform only needs one clocks */ |
203 | data->clk = devm_clk_get(dev, NULL); |
204 | if (IS_ERR(ptr: data->clk)) { |
205 | ret = PTR_ERR(ptr: data->clk); |
206 | dev_err(dev, |
207 | "Failed to get clks, err=%ld,%ld\n" , |
208 | PTR_ERR(data->clk), PTR_ERR(data->clk_ipg)); |
209 | return ret; |
210 | } |
211 | return ret; |
212 | } |
213 | |
214 | data->clk_ahb = devm_clk_get(dev, id: "ahb" ); |
215 | if (IS_ERR(ptr: data->clk_ahb)) { |
216 | ret = PTR_ERR(ptr: data->clk_ahb); |
217 | dev_err(dev, |
218 | "Failed to get ahb clock, err=%d\n" , ret); |
219 | return ret; |
220 | } |
221 | |
222 | data->clk_per = devm_clk_get(dev, id: "per" ); |
223 | if (IS_ERR(ptr: data->clk_per)) { |
224 | ret = PTR_ERR(ptr: data->clk_per); |
225 | dev_err(dev, |
226 | "Failed to get per clock, err=%d\n" , ret); |
227 | return ret; |
228 | } |
229 | |
230 | data->need_three_clks = true; |
231 | return ret; |
232 | } |
233 | |
234 | static int imx_prepare_enable_clks(struct device *dev) |
235 | { |
236 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
237 | int ret = 0; |
238 | |
239 | if (data->need_three_clks) { |
240 | ret = clk_prepare_enable(clk: data->clk_ipg); |
241 | if (ret) { |
242 | dev_err(dev, |
243 | "Failed to prepare/enable ipg clk, err=%d\n" , |
244 | ret); |
245 | return ret; |
246 | } |
247 | |
248 | ret = clk_prepare_enable(clk: data->clk_ahb); |
249 | if (ret) { |
250 | dev_err(dev, |
251 | "Failed to prepare/enable ahb clk, err=%d\n" , |
252 | ret); |
253 | clk_disable_unprepare(clk: data->clk_ipg); |
254 | return ret; |
255 | } |
256 | |
257 | ret = clk_prepare_enable(clk: data->clk_per); |
258 | if (ret) { |
259 | dev_err(dev, |
260 | "Failed to prepare/enable per clk, err=%d\n" , |
261 | ret); |
262 | clk_disable_unprepare(clk: data->clk_ahb); |
263 | clk_disable_unprepare(clk: data->clk_ipg); |
264 | return ret; |
265 | } |
266 | } else { |
267 | ret = clk_prepare_enable(clk: data->clk); |
268 | if (ret) { |
269 | dev_err(dev, |
270 | "Failed to prepare/enable clk, err=%d\n" , |
271 | ret); |
272 | return ret; |
273 | } |
274 | } |
275 | |
276 | return ret; |
277 | } |
278 | |
279 | static void imx_disable_unprepare_clks(struct device *dev) |
280 | { |
281 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
282 | |
283 | if (data->need_three_clks) { |
284 | clk_disable_unprepare(clk: data->clk_per); |
285 | clk_disable_unprepare(clk: data->clk_ahb); |
286 | clk_disable_unprepare(clk: data->clk_ipg); |
287 | } else { |
288 | clk_disable_unprepare(clk: data->clk); |
289 | } |
290 | } |
291 | |
292 | static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event) |
293 | { |
294 | struct device *dev = ci->dev->parent; |
295 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
296 | int ret = 0; |
297 | struct imx_usbmisc_data *mdata = data->usbmisc_data; |
298 | |
299 | switch (event) { |
300 | case CI_HDRC_IMX_HSIC_ACTIVE_EVENT: |
301 | if (data->pinctrl) { |
302 | ret = pinctrl_select_state(p: data->pinctrl, |
303 | s: data->pinctrl_hsic_active); |
304 | if (ret) |
305 | dev_err(dev, |
306 | "hsic_active select failed, err=%d\n" , |
307 | ret); |
308 | } |
309 | break; |
310 | case CI_HDRC_IMX_HSIC_SUSPEND_EVENT: |
311 | ret = imx_usbmisc_hsic_set_connect(data: mdata); |
312 | if (ret) |
313 | dev_err(dev, |
314 | "hsic_set_connect failed, err=%d\n" , ret); |
315 | break; |
316 | case CI_HDRC_CONTROLLER_VBUS_EVENT: |
317 | if (ci->vbus_active) |
318 | ret = imx_usbmisc_charger_detection(data: mdata, connect: true); |
319 | else |
320 | ret = imx_usbmisc_charger_detection(data: mdata, connect: false); |
321 | if (ci->usb_phy) |
322 | schedule_work(work: &ci->usb_phy->chg_work); |
323 | break; |
324 | default: |
325 | break; |
326 | } |
327 | |
328 | return ret; |
329 | } |
330 | |
331 | static int ci_hdrc_imx_probe(struct platform_device *pdev) |
332 | { |
333 | struct ci_hdrc_imx_data *data; |
334 | struct ci_hdrc_platform_data pdata = { |
335 | .name = dev_name(dev: &pdev->dev), |
336 | .capoffset = DEF_CAPOFFSET, |
337 | .notify_event = ci_hdrc_imx_notify_event, |
338 | }; |
339 | int ret; |
340 | const struct ci_hdrc_imx_platform_flag *imx_platform_flag; |
341 | struct device_node *np = pdev->dev.of_node; |
342 | struct device *dev = &pdev->dev; |
343 | |
344 | imx_platform_flag = of_device_get_match_data(dev: &pdev->dev); |
345 | |
346 | data = devm_kzalloc(dev: &pdev->dev, size: sizeof(*data), GFP_KERNEL); |
347 | if (!data) |
348 | return -ENOMEM; |
349 | |
350 | data->plat_data = imx_platform_flag; |
351 | pdata.flags |= imx_platform_flag->flags; |
352 | platform_set_drvdata(pdev, data); |
353 | data->usbmisc_data = usbmisc_get_init_data(dev); |
354 | if (IS_ERR(ptr: data->usbmisc_data)) |
355 | return PTR_ERR(ptr: data->usbmisc_data); |
356 | |
357 | if ((of_usb_get_phy_mode(np: dev->of_node) == USBPHY_INTERFACE_MODE_HSIC) |
358 | && data->usbmisc_data) { |
359 | pdata.flags |= CI_HDRC_IMX_IS_HSIC; |
360 | data->usbmisc_data->hsic = 1; |
361 | data->pinctrl = devm_pinctrl_get(dev); |
362 | if (PTR_ERR(ptr: data->pinctrl) == -ENODEV) |
363 | data->pinctrl = NULL; |
364 | else if (IS_ERR(ptr: data->pinctrl)) |
365 | return dev_err_probe(dev, err: PTR_ERR(ptr: data->pinctrl), |
366 | fmt: "pinctrl get failed\n" ); |
367 | |
368 | data->hsic_pad_regulator = |
369 | devm_regulator_get_optional(dev, id: "hsic" ); |
370 | if (PTR_ERR(ptr: data->hsic_pad_regulator) == -ENODEV) { |
371 | /* no pad regulator is needed */ |
372 | data->hsic_pad_regulator = NULL; |
373 | } else if (IS_ERR(ptr: data->hsic_pad_regulator)) |
374 | return dev_err_probe(dev, err: PTR_ERR(ptr: data->hsic_pad_regulator), |
375 | fmt: "Get HSIC pad regulator error\n" ); |
376 | |
377 | if (data->hsic_pad_regulator) { |
378 | ret = regulator_enable(regulator: data->hsic_pad_regulator); |
379 | if (ret) { |
380 | dev_err(dev, |
381 | "Failed to enable HSIC pad regulator\n" ); |
382 | return ret; |
383 | } |
384 | } |
385 | } |
386 | |
387 | /* HSIC pinctrl handling */ |
388 | if (data->pinctrl) { |
389 | struct pinctrl_state *pinctrl_hsic_idle; |
390 | |
391 | pinctrl_hsic_idle = pinctrl_lookup_state(p: data->pinctrl, name: "idle" ); |
392 | if (IS_ERR(ptr: pinctrl_hsic_idle)) { |
393 | dev_err(dev, |
394 | "pinctrl_hsic_idle lookup failed, err=%ld\n" , |
395 | PTR_ERR(pinctrl_hsic_idle)); |
396 | return PTR_ERR(ptr: pinctrl_hsic_idle); |
397 | } |
398 | |
399 | ret = pinctrl_select_state(p: data->pinctrl, s: pinctrl_hsic_idle); |
400 | if (ret) { |
401 | dev_err(dev, "hsic_idle select failed, err=%d\n" , ret); |
402 | return ret; |
403 | } |
404 | |
405 | data->pinctrl_hsic_active = pinctrl_lookup_state(p: data->pinctrl, |
406 | name: "active" ); |
407 | if (IS_ERR(ptr: data->pinctrl_hsic_active)) { |
408 | dev_err(dev, |
409 | "pinctrl_hsic_active lookup failed, err=%ld\n" , |
410 | PTR_ERR(data->pinctrl_hsic_active)); |
411 | return PTR_ERR(ptr: data->pinctrl_hsic_active); |
412 | } |
413 | } |
414 | |
415 | if (pdata.flags & CI_HDRC_PMQOS) |
416 | cpu_latency_qos_add_request(req: &data->pm_qos_req, value: 0); |
417 | |
418 | ret = imx_get_clks(dev); |
419 | if (ret) |
420 | goto disable_hsic_regulator; |
421 | |
422 | ret = imx_prepare_enable_clks(dev); |
423 | if (ret) |
424 | goto disable_hsic_regulator; |
425 | |
426 | data->phy = devm_usb_get_phy_by_phandle(dev, phandle: "fsl,usbphy" , index: 0); |
427 | if (IS_ERR(ptr: data->phy)) { |
428 | ret = PTR_ERR(ptr: data->phy); |
429 | if (ret != -ENODEV) { |
430 | dev_err_probe(dev, err: ret, fmt: "Failed to parse fsl,usbphy\n" ); |
431 | goto err_clk; |
432 | } |
433 | data->phy = devm_usb_get_phy_by_phandle(dev, phandle: "phys" , index: 0); |
434 | if (IS_ERR(ptr: data->phy)) { |
435 | ret = PTR_ERR(ptr: data->phy); |
436 | if (ret == -ENODEV) { |
437 | data->phy = NULL; |
438 | } else { |
439 | dev_err_probe(dev, err: ret, fmt: "Failed to parse phys\n" ); |
440 | goto err_clk; |
441 | } |
442 | } |
443 | } |
444 | |
445 | pdata.usb_phy = data->phy; |
446 | if (data->usbmisc_data) |
447 | data->usbmisc_data->usb_phy = data->phy; |
448 | |
449 | if ((of_device_is_compatible(device: np, "fsl,imx53-usb" ) || |
450 | of_device_is_compatible(device: np, "fsl,imx51-usb" )) && pdata.usb_phy && |
451 | of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) { |
452 | pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL; |
453 | data->override_phy_control = true; |
454 | usb_phy_init(x: pdata.usb_phy); |
455 | } |
456 | |
457 | if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) |
458 | data->supports_runtime_pm = true; |
459 | |
460 | ret = imx_usbmisc_init(data: data->usbmisc_data); |
461 | if (ret) { |
462 | dev_err(dev, "usbmisc init failed, ret=%d\n" , ret); |
463 | goto err_clk; |
464 | } |
465 | |
466 | data->ci_pdev = ci_hdrc_add_device(dev, |
467 | res: pdev->resource, nres: pdev->num_resources, |
468 | platdata: &pdata); |
469 | if (IS_ERR(ptr: data->ci_pdev)) { |
470 | ret = PTR_ERR(ptr: data->ci_pdev); |
471 | dev_err_probe(dev, err: ret, fmt: "ci_hdrc_add_device failed\n" ); |
472 | goto err_clk; |
473 | } |
474 | |
475 | if (data->usbmisc_data) { |
476 | if (!IS_ERR(ptr: pdata.id_extcon.edev) || |
477 | of_property_read_bool(np, propname: "usb-role-switch" )) |
478 | data->usbmisc_data->ext_id = 1; |
479 | |
480 | if (!IS_ERR(ptr: pdata.vbus_extcon.edev) || |
481 | of_property_read_bool(np, propname: "usb-role-switch" )) |
482 | data->usbmisc_data->ext_vbus = 1; |
483 | |
484 | /* usbmisc needs to know dr mode to choose wakeup setting */ |
485 | data->usbmisc_data->available_role = |
486 | ci_hdrc_query_available_role(pdev: data->ci_pdev); |
487 | } |
488 | |
489 | ret = imx_usbmisc_init_post(data: data->usbmisc_data); |
490 | if (ret) { |
491 | dev_err(dev, "usbmisc post failed, ret=%d\n" , ret); |
492 | goto disable_device; |
493 | } |
494 | |
495 | if (data->supports_runtime_pm) { |
496 | pm_runtime_set_active(dev); |
497 | pm_runtime_enable(dev); |
498 | } |
499 | |
500 | device_set_wakeup_capable(dev, capable: true); |
501 | |
502 | return 0; |
503 | |
504 | disable_device: |
505 | ci_hdrc_remove_device(pdev: data->ci_pdev); |
506 | err_clk: |
507 | imx_disable_unprepare_clks(dev); |
508 | disable_hsic_regulator: |
509 | if (data->hsic_pad_regulator) |
510 | /* don't overwrite original ret (cf. EPROBE_DEFER) */ |
511 | regulator_disable(regulator: data->hsic_pad_regulator); |
512 | if (pdata.flags & CI_HDRC_PMQOS) |
513 | cpu_latency_qos_remove_request(req: &data->pm_qos_req); |
514 | data->ci_pdev = NULL; |
515 | return ret; |
516 | } |
517 | |
518 | static void ci_hdrc_imx_remove(struct platform_device *pdev) |
519 | { |
520 | struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); |
521 | |
522 | if (data->supports_runtime_pm) { |
523 | pm_runtime_get_sync(dev: &pdev->dev); |
524 | pm_runtime_disable(dev: &pdev->dev); |
525 | pm_runtime_put_noidle(dev: &pdev->dev); |
526 | } |
527 | if (data->ci_pdev) |
528 | ci_hdrc_remove_device(pdev: data->ci_pdev); |
529 | if (data->override_phy_control) |
530 | usb_phy_shutdown(x: data->phy); |
531 | if (data->ci_pdev) { |
532 | imx_disable_unprepare_clks(dev: &pdev->dev); |
533 | if (data->plat_data->flags & CI_HDRC_PMQOS) |
534 | cpu_latency_qos_remove_request(req: &data->pm_qos_req); |
535 | if (data->hsic_pad_regulator) |
536 | regulator_disable(regulator: data->hsic_pad_regulator); |
537 | } |
538 | } |
539 | |
540 | static void ci_hdrc_imx_shutdown(struct platform_device *pdev) |
541 | { |
542 | ci_hdrc_imx_remove(pdev); |
543 | } |
544 | |
545 | static int __maybe_unused imx_controller_suspend(struct device *dev, |
546 | pm_message_t msg) |
547 | { |
548 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
549 | int ret = 0; |
550 | |
551 | dev_dbg(dev, "at %s\n" , __func__); |
552 | |
553 | ret = imx_usbmisc_suspend(data: data->usbmisc_data, |
554 | PMSG_IS_AUTO(msg) || device_may_wakeup(dev)); |
555 | if (ret) { |
556 | dev_err(dev, |
557 | "usbmisc suspend failed, ret=%d\n" , ret); |
558 | return ret; |
559 | } |
560 | |
561 | imx_disable_unprepare_clks(dev); |
562 | if (data->plat_data->flags & CI_HDRC_PMQOS) |
563 | cpu_latency_qos_remove_request(req: &data->pm_qos_req); |
564 | |
565 | data->in_lpm = true; |
566 | |
567 | return 0; |
568 | } |
569 | |
570 | static int __maybe_unused imx_controller_resume(struct device *dev, |
571 | pm_message_t msg) |
572 | { |
573 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
574 | int ret = 0; |
575 | |
576 | dev_dbg(dev, "at %s\n" , __func__); |
577 | |
578 | if (!data->in_lpm) { |
579 | WARN_ON(1); |
580 | return 0; |
581 | } |
582 | |
583 | if (data->plat_data->flags & CI_HDRC_PMQOS) |
584 | cpu_latency_qos_add_request(req: &data->pm_qos_req, value: 0); |
585 | |
586 | ret = imx_prepare_enable_clks(dev); |
587 | if (ret) |
588 | return ret; |
589 | |
590 | data->in_lpm = false; |
591 | |
592 | ret = imx_usbmisc_resume(data: data->usbmisc_data, |
593 | PMSG_IS_AUTO(msg) || device_may_wakeup(dev)); |
594 | if (ret) { |
595 | dev_err(dev, "usbmisc resume failed, ret=%d\n" , ret); |
596 | goto clk_disable; |
597 | } |
598 | |
599 | return 0; |
600 | |
601 | clk_disable: |
602 | imx_disable_unprepare_clks(dev); |
603 | return ret; |
604 | } |
605 | |
606 | static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev) |
607 | { |
608 | int ret; |
609 | |
610 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
611 | |
612 | if (data->in_lpm) |
613 | /* The core's suspend doesn't run */ |
614 | return 0; |
615 | |
616 | ret = imx_controller_suspend(dev, PMSG_SUSPEND); |
617 | if (ret) |
618 | return ret; |
619 | |
620 | pinctrl_pm_select_sleep_state(dev); |
621 | return ret; |
622 | } |
623 | |
624 | static int __maybe_unused ci_hdrc_imx_resume(struct device *dev) |
625 | { |
626 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
627 | int ret; |
628 | |
629 | pinctrl_pm_select_default_state(dev); |
630 | ret = imx_controller_resume(dev, PMSG_RESUME); |
631 | if (!ret && data->supports_runtime_pm) { |
632 | pm_runtime_disable(dev); |
633 | pm_runtime_set_active(dev); |
634 | pm_runtime_enable(dev); |
635 | } |
636 | |
637 | return ret; |
638 | } |
639 | |
640 | static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev) |
641 | { |
642 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
643 | |
644 | if (data->in_lpm) { |
645 | WARN_ON(1); |
646 | return 0; |
647 | } |
648 | |
649 | return imx_controller_suspend(dev, PMSG_AUTO_SUSPEND); |
650 | } |
651 | |
652 | static int __maybe_unused ci_hdrc_imx_runtime_resume(struct device *dev) |
653 | { |
654 | return imx_controller_resume(dev, PMSG_AUTO_RESUME); |
655 | } |
656 | |
657 | static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { |
658 | SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) |
659 | SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, |
660 | ci_hdrc_imx_runtime_resume, NULL) |
661 | }; |
662 | static struct platform_driver ci_hdrc_imx_driver = { |
663 | .probe = ci_hdrc_imx_probe, |
664 | .remove_new = ci_hdrc_imx_remove, |
665 | .shutdown = ci_hdrc_imx_shutdown, |
666 | .driver = { |
667 | .name = "imx_usb" , |
668 | .of_match_table = ci_hdrc_imx_dt_ids, |
669 | .pm = &ci_hdrc_imx_pm_ops, |
670 | }, |
671 | }; |
672 | |
673 | module_platform_driver(ci_hdrc_imx_driver); |
674 | |
675 | MODULE_ALIAS("platform:imx-usb" ); |
676 | MODULE_LICENSE("GPL" ); |
677 | MODULE_DESCRIPTION("CI HDRC i.MX USB binding" ); |
678 | MODULE_AUTHOR("Marek Vasut <marex@denx.de>" ); |
679 | MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>" ); |
680 | |