1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright 2012 Freescale Semiconductor, Inc.
4 * Copyright (C) 2012 Marek Vasut <marex@denx.de>
5 * on behalf of DENX Software Engineering GmbH
8 #include <linux/module.h>
9 #include <linux/of_platform.h>
10 #include <linux/platform_device.h>
11 #include <linux/pm_runtime.h>
12 #include <linux/usb/chipidea.h>
13 #include <linux/usb/of.h>
14 #include <linux/clk.h>
15 #include <linux/pinctrl/consumer.h>
18 #include "ci_hdrc_imx.h"
20 struct ci_hdrc_imx_platform_flag
{
24 static const struct ci_hdrc_imx_platform_flag imx23_usb_data
= {
25 .flags
= CI_HDRC_TURN_VBUS_EARLY_ON
|
26 CI_HDRC_DISABLE_STREAMING
,
29 static const struct ci_hdrc_imx_platform_flag imx27_usb_data
= {
30 .flags
= CI_HDRC_DISABLE_STREAMING
,
33 static const struct ci_hdrc_imx_platform_flag imx28_usb_data
= {
34 .flags
= CI_HDRC_IMX28_WRITE_FIX
|
35 CI_HDRC_TURN_VBUS_EARLY_ON
|
36 CI_HDRC_DISABLE_STREAMING
,
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 CI_HDRC_DISABLE_STREAMING
,
45 static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data
= {
46 .flags
= CI_HDRC_SUPPORTS_RUNTIME_PM
|
47 CI_HDRC_TURN_VBUS_EARLY_ON
|
48 CI_HDRC_DISABLE_HOST_STREAMING
,
51 static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data
= {
52 .flags
= CI_HDRC_SUPPORTS_RUNTIME_PM
|
53 CI_HDRC_TURN_VBUS_EARLY_ON
|
54 CI_HDRC_DISABLE_HOST_STREAMING
,
57 static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data
= {
58 .flags
= CI_HDRC_SUPPORTS_RUNTIME_PM
|
59 CI_HDRC_TURN_VBUS_EARLY_ON
,
62 static const struct ci_hdrc_imx_platform_flag imx7d_usb_data
= {
63 .flags
= CI_HDRC_SUPPORTS_RUNTIME_PM
,
66 static const struct of_device_id ci_hdrc_imx_dt_ids
[] = {
67 { .compatible
= "fsl,imx23-usb", .data
= &imx23_usb_data
},
68 { .compatible
= "fsl,imx28-usb", .data
= &imx28_usb_data
},
69 { .compatible
= "fsl,imx27-usb", .data
= &imx27_usb_data
},
70 { .compatible
= "fsl,imx6q-usb", .data
= &imx6q_usb_data
},
71 { .compatible
= "fsl,imx6sl-usb", .data
= &imx6sl_usb_data
},
72 { .compatible
= "fsl,imx6sx-usb", .data
= &imx6sx_usb_data
},
73 { .compatible
= "fsl,imx6ul-usb", .data
= &imx6ul_usb_data
},
74 { .compatible
= "fsl,imx7d-usb", .data
= &imx7d_usb_data
},
77 MODULE_DEVICE_TABLE(of
, ci_hdrc_imx_dt_ids
);
79 struct ci_hdrc_imx_data
{
81 struct platform_device
*ci_pdev
;
83 struct imx_usbmisc_data
*usbmisc_data
;
84 bool supports_runtime_pm
;
85 bool override_phy_control
;
87 struct pinctrl
*pinctrl
;
88 struct pinctrl_state
*pinctrl_hsic_active
;
89 struct regulator
*hsic_pad_regulator
;
90 /* SoC before i.mx6 (except imx23/imx28) needs three clks */
95 /* --------------------------------- */
98 /* Common functions shared by usbmisc drivers */
100 static struct imx_usbmisc_data
*usbmisc_get_init_data(struct device
*dev
)
102 struct platform_device
*misc_pdev
;
103 struct device_node
*np
= dev
->of_node
;
104 struct of_phandle_args args
;
105 struct imx_usbmisc_data
*data
;
109 * In case the fsl,usbmisc property is not present this device doesn't
110 * need usbmisc. Return NULL (which is no error here)
112 if (!of_get_property(np
, "fsl,usbmisc", NULL
))
115 data
= devm_kzalloc(dev
, sizeof(*data
), GFP_KERNEL
);
117 return ERR_PTR(-ENOMEM
);
119 ret
= of_parse_phandle_with_args(np
, "fsl,usbmisc", "#index-cells",
122 dev_err(dev
, "Failed to parse property fsl,usbmisc, errno %d\n",
127 data
->index
= args
.args
[0];
129 misc_pdev
= of_find_device_by_node(args
.np
);
130 of_node_put(args
.np
);
132 if (!misc_pdev
|| !platform_get_drvdata(misc_pdev
))
133 return ERR_PTR(-EPROBE_DEFER
);
135 data
->dev
= &misc_pdev
->dev
;
138 * Check the various over current related properties. If over current
139 * detection is disabled we're not interested in the polarity.
141 if (of_find_property(np
, "disable-over-current", NULL
)) {
142 data
->disable_oc
= 1;
143 } else if (of_find_property(np
, "over-current-active-high", NULL
)) {
144 data
->oc_pol_active_low
= 0;
145 data
->oc_pol_configured
= 1;
146 } else if (of_find_property(np
, "over-current-active-low", NULL
)) {
147 data
->oc_pol_active_low
= 1;
148 data
->oc_pol_configured
= 1;
150 dev_warn(dev
, "No over current polarity defined\n");
153 data
->pwr_pol
= of_property_read_bool(np
, "power-active-high");
154 data
->evdo
= of_property_read_bool(np
, "external-vbus-divider");
156 if (of_usb_get_phy_mode(np
) == USBPHY_INTERFACE_MODE_ULPI
)
162 /* End of common functions shared by usbmisc drivers*/
163 static int imx_get_clks(struct device
*dev
)
165 struct ci_hdrc_imx_data
*data
= dev_get_drvdata(dev
);
168 data
->clk_ipg
= devm_clk_get(dev
, "ipg");
169 if (IS_ERR(data
->clk_ipg
)) {
170 /* If the platform only needs one clocks */
171 data
->clk
= devm_clk_get(dev
, NULL
);
172 if (IS_ERR(data
->clk
)) {
173 ret
= PTR_ERR(data
->clk
);
175 "Failed to get clks, err=%ld,%ld\n",
176 PTR_ERR(data
->clk
), PTR_ERR(data
->clk_ipg
));
182 data
->clk_ahb
= devm_clk_get(dev
, "ahb");
183 if (IS_ERR(data
->clk_ahb
)) {
184 ret
= PTR_ERR(data
->clk_ahb
);
186 "Failed to get ahb clock, err=%d\n", ret
);
190 data
->clk_per
= devm_clk_get(dev
, "per");
191 if (IS_ERR(data
->clk_per
)) {
192 ret
= PTR_ERR(data
->clk_per
);
194 "Failed to get per clock, err=%d\n", ret
);
198 data
->need_three_clks
= true;
202 static int imx_prepare_enable_clks(struct device
*dev
)
204 struct ci_hdrc_imx_data
*data
= dev_get_drvdata(dev
);
207 if (data
->need_three_clks
) {
208 ret
= clk_prepare_enable(data
->clk_ipg
);
211 "Failed to prepare/enable ipg clk, err=%d\n",
216 ret
= clk_prepare_enable(data
->clk_ahb
);
219 "Failed to prepare/enable ahb clk, err=%d\n",
221 clk_disable_unprepare(data
->clk_ipg
);
225 ret
= clk_prepare_enable(data
->clk_per
);
228 "Failed to prepare/enable per clk, err=%d\n",
230 clk_disable_unprepare(data
->clk_ahb
);
231 clk_disable_unprepare(data
->clk_ipg
);
235 ret
= clk_prepare_enable(data
->clk
);
238 "Failed to prepare/enable clk, err=%d\n",
247 static void imx_disable_unprepare_clks(struct device
*dev
)
249 struct ci_hdrc_imx_data
*data
= dev_get_drvdata(dev
);
251 if (data
->need_three_clks
) {
252 clk_disable_unprepare(data
->clk_per
);
253 clk_disable_unprepare(data
->clk_ahb
);
254 clk_disable_unprepare(data
->clk_ipg
);
256 clk_disable_unprepare(data
->clk
);
260 static int ci_hdrc_imx_notify_event(struct ci_hdrc
*ci
, unsigned int event
)
262 struct device
*dev
= ci
->dev
->parent
;
263 struct ci_hdrc_imx_data
*data
= dev_get_drvdata(dev
);
267 case CI_HDRC_IMX_HSIC_ACTIVE_EVENT
:
268 ret
= pinctrl_select_state(data
->pinctrl
,
269 data
->pinctrl_hsic_active
);
271 dev_err(dev
, "hsic_active select failed, err=%d\n",
274 case CI_HDRC_IMX_HSIC_SUSPEND_EVENT
:
275 ret
= imx_usbmisc_hsic_set_connect(data
->usbmisc_data
);
278 "hsic_set_connect failed, err=%d\n", ret
);
287 static int ci_hdrc_imx_probe(struct platform_device
*pdev
)
289 struct ci_hdrc_imx_data
*data
;
290 struct ci_hdrc_platform_data pdata
= {
291 .name
= dev_name(&pdev
->dev
),
292 .capoffset
= DEF_CAPOFFSET
,
293 .notify_event
= ci_hdrc_imx_notify_event
,
296 const struct of_device_id
*of_id
;
297 const struct ci_hdrc_imx_platform_flag
*imx_platform_flag
;
298 struct device_node
*np
= pdev
->dev
.of_node
;
299 struct device
*dev
= &pdev
->dev
;
300 struct pinctrl_state
*pinctrl_hsic_idle
;
302 of_id
= of_match_device(ci_hdrc_imx_dt_ids
, dev
);
306 imx_platform_flag
= of_id
->data
;
308 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
312 platform_set_drvdata(pdev
, data
);
313 data
->usbmisc_data
= usbmisc_get_init_data(dev
);
314 if (IS_ERR(data
->usbmisc_data
))
315 return PTR_ERR(data
->usbmisc_data
);
317 if ((of_usb_get_phy_mode(dev
->of_node
) == USBPHY_INTERFACE_MODE_HSIC
)
318 && data
->usbmisc_data
) {
319 pdata
.flags
|= CI_HDRC_IMX_IS_HSIC
;
320 data
->usbmisc_data
->hsic
= 1;
321 data
->pinctrl
= devm_pinctrl_get(dev
);
322 if (IS_ERR(data
->pinctrl
)) {
323 dev_err(dev
, "pinctrl get failed, err=%ld\n",
324 PTR_ERR(data
->pinctrl
));
325 return PTR_ERR(data
->pinctrl
);
328 pinctrl_hsic_idle
= pinctrl_lookup_state(data
->pinctrl
, "idle");
329 if (IS_ERR(pinctrl_hsic_idle
)) {
331 "pinctrl_hsic_idle lookup failed, err=%ld\n",
332 PTR_ERR(pinctrl_hsic_idle
));
333 return PTR_ERR(pinctrl_hsic_idle
);
336 ret
= pinctrl_select_state(data
->pinctrl
, pinctrl_hsic_idle
);
338 dev_err(dev
, "hsic_idle select failed, err=%d\n", ret
);
342 data
->pinctrl_hsic_active
= pinctrl_lookup_state(data
->pinctrl
,
344 if (IS_ERR(data
->pinctrl_hsic_active
)) {
346 "pinctrl_hsic_active lookup failed, err=%ld\n",
347 PTR_ERR(data
->pinctrl_hsic_active
));
348 return PTR_ERR(data
->pinctrl_hsic_active
);
351 data
->hsic_pad_regulator
= devm_regulator_get(dev
, "hsic");
352 if (PTR_ERR(data
->hsic_pad_regulator
) == -EPROBE_DEFER
) {
353 return -EPROBE_DEFER
;
354 } else if (PTR_ERR(data
->hsic_pad_regulator
) == -ENODEV
) {
355 /* no pad regualator is needed */
356 data
->hsic_pad_regulator
= NULL
;
357 } else if (IS_ERR(data
->hsic_pad_regulator
)) {
358 dev_err(dev
, "Get HSIC pad regulator error: %ld\n",
359 PTR_ERR(data
->hsic_pad_regulator
));
360 return PTR_ERR(data
->hsic_pad_regulator
);
363 if (data
->hsic_pad_regulator
) {
364 ret
= regulator_enable(data
->hsic_pad_regulator
);
367 "Failed to enable HSIC pad regulator\n");
372 ret
= imx_get_clks(dev
);
374 goto disable_hsic_regulator
;
376 ret
= imx_prepare_enable_clks(dev
);
378 goto disable_hsic_regulator
;
380 data
->phy
= devm_usb_get_phy_by_phandle(dev
, "fsl,usbphy", 0);
381 if (IS_ERR(data
->phy
)) {
382 ret
= PTR_ERR(data
->phy
);
383 /* Return -EINVAL if no usbphy is available */
389 pdata
.usb_phy
= data
->phy
;
391 if ((of_device_is_compatible(np
, "fsl,imx53-usb") ||
392 of_device_is_compatible(np
, "fsl,imx51-usb")) && pdata
.usb_phy
&&
393 of_usb_get_phy_mode(np
) == USBPHY_INTERFACE_MODE_ULPI
) {
394 pdata
.flags
|= CI_HDRC_OVERRIDE_PHY_CONTROL
;
395 data
->override_phy_control
= true;
396 usb_phy_init(pdata
.usb_phy
);
399 pdata
.flags
|= imx_platform_flag
->flags
;
400 if (pdata
.flags
& CI_HDRC_SUPPORTS_RUNTIME_PM
)
401 data
->supports_runtime_pm
= true;
403 ret
= imx_usbmisc_init(data
->usbmisc_data
);
405 dev_err(dev
, "usbmisc init failed, ret=%d\n", ret
);
409 data
->ci_pdev
= ci_hdrc_add_device(dev
,
410 pdev
->resource
, pdev
->num_resources
,
412 if (IS_ERR(data
->ci_pdev
)) {
413 ret
= PTR_ERR(data
->ci_pdev
);
414 if (ret
!= -EPROBE_DEFER
)
415 dev_err(dev
, "ci_hdrc_add_device failed, err=%d\n",
420 ret
= imx_usbmisc_init_post(data
->usbmisc_data
);
422 dev_err(dev
, "usbmisc post failed, ret=%d\n", ret
);
426 if (data
->supports_runtime_pm
) {
427 pm_runtime_set_active(dev
);
428 pm_runtime_enable(dev
);
431 device_set_wakeup_capable(dev
, true);
436 ci_hdrc_remove_device(data
->ci_pdev
);
438 imx_disable_unprepare_clks(dev
);
439 disable_hsic_regulator
:
440 if (data
->hsic_pad_regulator
)
441 ret
= regulator_disable(data
->hsic_pad_regulator
);
445 static int ci_hdrc_imx_remove(struct platform_device
*pdev
)
447 struct ci_hdrc_imx_data
*data
= platform_get_drvdata(pdev
);
449 if (data
->supports_runtime_pm
) {
450 pm_runtime_get_sync(&pdev
->dev
);
451 pm_runtime_disable(&pdev
->dev
);
452 pm_runtime_put_noidle(&pdev
->dev
);
454 ci_hdrc_remove_device(data
->ci_pdev
);
455 if (data
->override_phy_control
)
456 usb_phy_shutdown(data
->phy
);
457 imx_disable_unprepare_clks(&pdev
->dev
);
458 if (data
->hsic_pad_regulator
)
459 regulator_disable(data
->hsic_pad_regulator
);
464 static void ci_hdrc_imx_shutdown(struct platform_device
*pdev
)
466 ci_hdrc_imx_remove(pdev
);
469 static int __maybe_unused
imx_controller_suspend(struct device
*dev
)
471 struct ci_hdrc_imx_data
*data
= dev_get_drvdata(dev
);
474 dev_dbg(dev
, "at %s\n", __func__
);
476 ret
= imx_usbmisc_hsic_set_clk(data
->usbmisc_data
, false);
478 dev_err(dev
, "usbmisc hsic_set_clk failed, ret=%d\n", ret
);
482 imx_disable_unprepare_clks(dev
);
488 static int __maybe_unused
imx_controller_resume(struct device
*dev
)
490 struct ci_hdrc_imx_data
*data
= dev_get_drvdata(dev
);
493 dev_dbg(dev
, "at %s\n", __func__
);
500 ret
= imx_prepare_enable_clks(dev
);
504 data
->in_lpm
= false;
506 ret
= imx_usbmisc_set_wakeup(data
->usbmisc_data
, false);
508 dev_err(dev
, "usbmisc set_wakeup failed, ret=%d\n", ret
);
512 ret
= imx_usbmisc_hsic_set_clk(data
->usbmisc_data
, true);
514 dev_err(dev
, "usbmisc hsic_set_clk failed, ret=%d\n", ret
);
515 goto hsic_set_clk_fail
;
521 imx_usbmisc_set_wakeup(data
->usbmisc_data
, true);
523 imx_disable_unprepare_clks(dev
);
527 static int __maybe_unused
ci_hdrc_imx_suspend(struct device
*dev
)
531 struct ci_hdrc_imx_data
*data
= dev_get_drvdata(dev
);
534 /* The core's suspend doesn't run */
537 if (device_may_wakeup(dev
)) {
538 ret
= imx_usbmisc_set_wakeup(data
->usbmisc_data
, true);
540 dev_err(dev
, "usbmisc set_wakeup failed, ret=%d\n",
546 return imx_controller_suspend(dev
);
549 static int __maybe_unused
ci_hdrc_imx_resume(struct device
*dev
)
551 struct ci_hdrc_imx_data
*data
= dev_get_drvdata(dev
);
554 ret
= imx_controller_resume(dev
);
555 if (!ret
&& data
->supports_runtime_pm
) {
556 pm_runtime_disable(dev
);
557 pm_runtime_set_active(dev
);
558 pm_runtime_enable(dev
);
564 static int __maybe_unused
ci_hdrc_imx_runtime_suspend(struct device
*dev
)
566 struct ci_hdrc_imx_data
*data
= dev_get_drvdata(dev
);
574 ret
= imx_usbmisc_set_wakeup(data
->usbmisc_data
, true);
576 dev_err(dev
, "usbmisc set_wakeup failed, ret=%d\n", ret
);
580 return imx_controller_suspend(dev
);
583 static int __maybe_unused
ci_hdrc_imx_runtime_resume(struct device
*dev
)
585 return imx_controller_resume(dev
);
588 static const struct dev_pm_ops ci_hdrc_imx_pm_ops
= {
589 SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend
, ci_hdrc_imx_resume
)
590 SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend
,
591 ci_hdrc_imx_runtime_resume
, NULL
)
593 static struct platform_driver ci_hdrc_imx_driver
= {
594 .probe
= ci_hdrc_imx_probe
,
595 .remove
= ci_hdrc_imx_remove
,
596 .shutdown
= ci_hdrc_imx_shutdown
,
599 .of_match_table
= ci_hdrc_imx_dt_ids
,
600 .pm
= &ci_hdrc_imx_pm_ops
,
604 module_platform_driver(ci_hdrc_imx_driver
);
606 MODULE_ALIAS("platform:imx-usb");
607 MODULE_LICENSE("GPL");
608 MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
609 MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
610 MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");