diff options
| author | Peter Chen <peter.chen@nxp.com> | 2018-10-15 21:17:02 -0400 |
|---|---|---|
| committer | Peter Chen <peter.chen@nxp.com> | 2018-12-10 20:12:30 -0500 |
| commit | 7c8e8909417eb6342ac487dc5ab3076d46718f71 (patch) | |
| tree | 2368c0024f335e03ce1e29b0e9b12815c98273b2 /drivers/usb/chipidea | |
| parent | 014abe34a9095daaa6cbb2693ee90bbb54674693 (diff) | |
usb: chipidea: imx: add HSIC support
To support imx HSIC, there are some special requirement:
- The HSIC pad is 1.2v, it may need to supply from external
- The data/strobe pin needs to be pulled down first, and after
host mode is initialized, the strobe pin needs to be pulled up
- During the USB suspend/resume, special setting is needed
Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de>
Tested-by: Frieder Schrempf <frieder.schrempf@kontron.de>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
Diffstat (limited to 'drivers/usb/chipidea')
| -rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.c | 140 | ||||
| -rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.h | 9 | ||||
| -rw-r--r-- | drivers/usb/chipidea/usbmisc_imx.c | 140 |
3 files changed, 270 insertions, 19 deletions
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 09b37c0d075d..56781c329db0 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/usb/chipidea.h> | 14 | #include <linux/usb/chipidea.h> |
| 15 | #include <linux/usb/of.h> | 15 | #include <linux/usb/of.h> |
| 16 | #include <linux/clk.h> | 16 | #include <linux/clk.h> |
| 17 | #include <linux/pinctrl/consumer.h> | ||
| 17 | 18 | ||
| 18 | #include "ci.h" | 19 | #include "ci.h" |
| 19 | #include "ci_hdrc_imx.h" | 20 | #include "ci_hdrc_imx.h" |
| @@ -85,6 +86,9 @@ struct ci_hdrc_imx_data { | |||
| 85 | bool supports_runtime_pm; | 86 | bool supports_runtime_pm; |
| 86 | bool override_phy_control; | 87 | bool override_phy_control; |
| 87 | bool in_lpm; | 88 | bool in_lpm; |
| 89 | struct pinctrl *pinctrl; | ||
| 90 | struct pinctrl_state *pinctrl_hsic_active; | ||
| 91 | struct regulator *hsic_pad_regulator; | ||
| 88 | /* SoC before i.mx6 (except imx23/imx28) needs three clks */ | 92 | /* SoC before i.mx6 (except imx23/imx28) needs three clks */ |
| 89 | bool need_three_clks; | 93 | bool need_three_clks; |
| 90 | struct clk *clk_ipg; | 94 | struct clk *clk_ipg; |
| @@ -245,19 +249,49 @@ static void imx_disable_unprepare_clks(struct device *dev) | |||
| 245 | } | 249 | } |
| 246 | } | 250 | } |
| 247 | 251 | ||
| 252 | static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event) | ||
| 253 | { | ||
| 254 | struct device *dev = ci->dev->parent; | ||
| 255 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); | ||
| 256 | int ret = 0; | ||
| 257 | |||
| 258 | switch (event) { | ||
| 259 | case CI_HDRC_IMX_HSIC_ACTIVE_EVENT: | ||
| 260 | ret = pinctrl_select_state(data->pinctrl, | ||
| 261 | data->pinctrl_hsic_active); | ||
| 262 | if (ret) | ||
| 263 | dev_err(dev, "hsic_active select failed, err=%d\n", | ||
| 264 | ret); | ||
| 265 | break; | ||
| 266 | case CI_HDRC_IMX_HSIC_SUSPEND_EVENT: | ||
| 267 | ret = imx_usbmisc_hsic_set_connect(data->usbmisc_data); | ||
| 268 | if (ret) | ||
| 269 | dev_err(dev, | ||
| 270 | "hsic_set_connect failed, err=%d\n", ret); | ||
| 271 | break; | ||
| 272 | default: | ||
| 273 | break; | ||
| 274 | } | ||
| 275 | |||
| 276 | return ret; | ||
| 277 | } | ||
| 278 | |||
| 248 | static int ci_hdrc_imx_probe(struct platform_device *pdev) | 279 | static int ci_hdrc_imx_probe(struct platform_device *pdev) |
| 249 | { | 280 | { |
| 250 | struct ci_hdrc_imx_data *data; | 281 | struct ci_hdrc_imx_data *data; |
| 251 | struct ci_hdrc_platform_data pdata = { | 282 | struct ci_hdrc_platform_data pdata = { |
| 252 | .name = dev_name(&pdev->dev), | 283 | .name = dev_name(&pdev->dev), |
| 253 | .capoffset = DEF_CAPOFFSET, | 284 | .capoffset = DEF_CAPOFFSET, |
| 285 | .notify_event = ci_hdrc_imx_notify_event, | ||
| 254 | }; | 286 | }; |
| 255 | int ret; | 287 | int ret; |
| 256 | const struct of_device_id *of_id; | 288 | const struct of_device_id *of_id; |
| 257 | const struct ci_hdrc_imx_platform_flag *imx_platform_flag; | 289 | const struct ci_hdrc_imx_platform_flag *imx_platform_flag; |
| 258 | struct device_node *np = pdev->dev.of_node; | 290 | struct device_node *np = pdev->dev.of_node; |
| 291 | struct device *dev = &pdev->dev; | ||
| 292 | struct pinctrl_state *pinctrl_hsic_idle; | ||
| 259 | 293 | ||
| 260 | of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev); | 294 | of_id = of_match_device(ci_hdrc_imx_dt_ids, dev); |
| 261 | if (!of_id) | 295 | if (!of_id) |
| 262 | return -ENODEV; | 296 | return -ENODEV; |
| 263 | 297 | ||
| @@ -268,19 +302,73 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) | |||
| 268 | return -ENOMEM; | 302 | return -ENOMEM; |
| 269 | 303 | ||
| 270 | platform_set_drvdata(pdev, data); | 304 | platform_set_drvdata(pdev, data); |
| 271 | data->usbmisc_data = usbmisc_get_init_data(&pdev->dev); | 305 | data->usbmisc_data = usbmisc_get_init_data(dev); |
| 272 | if (IS_ERR(data->usbmisc_data)) | 306 | if (IS_ERR(data->usbmisc_data)) |
| 273 | return PTR_ERR(data->usbmisc_data); | 307 | return PTR_ERR(data->usbmisc_data); |
| 274 | 308 | ||
| 275 | ret = imx_get_clks(&pdev->dev); | 309 | if (of_usb_get_phy_mode(dev->of_node) == USBPHY_INTERFACE_MODE_HSIC) { |
| 310 | pdata.flags |= CI_HDRC_IMX_IS_HSIC; | ||
| 311 | data->usbmisc_data->hsic = 1; | ||
| 312 | data->pinctrl = devm_pinctrl_get(dev); | ||
| 313 | if (IS_ERR(data->pinctrl)) { | ||
| 314 | dev_err(dev, "pinctrl get failed, err=%ld\n", | ||
| 315 | PTR_ERR(data->pinctrl)); | ||
| 316 | return PTR_ERR(data->pinctrl); | ||
| 317 | } | ||
| 318 | |||
| 319 | pinctrl_hsic_idle = pinctrl_lookup_state(data->pinctrl, "idle"); | ||
| 320 | if (IS_ERR(pinctrl_hsic_idle)) { | ||
| 321 | dev_err(dev, | ||
| 322 | "pinctrl_hsic_idle lookup failed, err=%ld\n", | ||
| 323 | PTR_ERR(pinctrl_hsic_idle)); | ||
| 324 | return PTR_ERR(pinctrl_hsic_idle); | ||
| 325 | } | ||
| 326 | |||
| 327 | ret = pinctrl_select_state(data->pinctrl, pinctrl_hsic_idle); | ||
| 328 | if (ret) { | ||
| 329 | dev_err(dev, "hsic_idle select failed, err=%d\n", ret); | ||
| 330 | return ret; | ||
| 331 | } | ||
| 332 | |||
| 333 | data->pinctrl_hsic_active = pinctrl_lookup_state(data->pinctrl, | ||
| 334 | "active"); | ||
| 335 | if (IS_ERR(data->pinctrl_hsic_active)) { | ||
| 336 | dev_err(dev, | ||
| 337 | "pinctrl_hsic_active lookup failed, err=%ld\n", | ||
| 338 | PTR_ERR(data->pinctrl_hsic_active)); | ||
| 339 | return PTR_ERR(data->pinctrl_hsic_active); | ||
| 340 | } | ||
| 341 | |||
| 342 | data->hsic_pad_regulator = devm_regulator_get(dev, "hsic"); | ||
| 343 | if (PTR_ERR(data->hsic_pad_regulator) == -EPROBE_DEFER) { | ||
| 344 | return -EPROBE_DEFER; | ||
| 345 | } else if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) { | ||
| 346 | /* no pad regualator is needed */ | ||
| 347 | data->hsic_pad_regulator = NULL; | ||
| 348 | } else if (IS_ERR(data->hsic_pad_regulator)) { | ||
| 349 | dev_err(dev, "Get HSIC pad regulator error: %ld\n", | ||
| 350 | PTR_ERR(data->hsic_pad_regulator)); | ||
| 351 | return PTR_ERR(data->hsic_pad_regulator); | ||
| 352 | } | ||
| 353 | |||
| 354 | if (data->hsic_pad_regulator) { | ||
| 355 | ret = regulator_enable(data->hsic_pad_regulator); | ||
| 356 | if (ret) { | ||
| 357 | dev_err(dev, | ||
| 358 | "Failed to enable HSIC pad regulator\n"); | ||
| 359 | return ret; | ||
| 360 | } | ||
| 361 | } | ||
| 362 | } | ||
| 363 | ret = imx_get_clks(dev); | ||
| 276 | if (ret) | 364 | if (ret) |
| 277 | return ret; | 365 | goto disable_hsic_regulator; |
| 278 | 366 | ||
| 279 | ret = imx_prepare_enable_clks(&pdev->dev); | 367 | ret = imx_prepare_enable_clks(dev); |
| 280 | if (ret) | 368 | if (ret) |
| 281 | return ret; | 369 | goto disable_hsic_regulator; |
| 282 | 370 | ||
| 283 | data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0); | 371 | data->phy = devm_usb_get_phy_by_phandle(dev, "fsl,usbphy", 0); |
| 284 | if (IS_ERR(data->phy)) { | 372 | if (IS_ERR(data->phy)) { |
| 285 | ret = PTR_ERR(data->phy); | 373 | ret = PTR_ERR(data->phy); |
| 286 | /* Return -EINVAL if no usbphy is available */ | 374 | /* Return -EINVAL if no usbphy is available */ |
| @@ -305,40 +393,43 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) | |||
| 305 | 393 | ||
| 306 | ret = imx_usbmisc_init(data->usbmisc_data); | 394 | ret = imx_usbmisc_init(data->usbmisc_data); |
| 307 | if (ret) { | 395 | if (ret) { |
| 308 | dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret); | 396 | dev_err(dev, "usbmisc init failed, ret=%d\n", ret); |
| 309 | goto err_clk; | 397 | goto err_clk; |
| 310 | } | 398 | } |
| 311 | 399 | ||
| 312 | data->ci_pdev = ci_hdrc_add_device(&pdev->dev, | 400 | data->ci_pdev = ci_hdrc_add_device(dev, |
| 313 | pdev->resource, pdev->num_resources, | 401 | pdev->resource, pdev->num_resources, |
| 314 | &pdata); | 402 | &pdata); |
| 315 | if (IS_ERR(data->ci_pdev)) { | 403 | if (IS_ERR(data->ci_pdev)) { |
| 316 | ret = PTR_ERR(data->ci_pdev); | 404 | ret = PTR_ERR(data->ci_pdev); |
| 317 | if (ret != -EPROBE_DEFER) | 405 | if (ret != -EPROBE_DEFER) |
| 318 | dev_err(&pdev->dev, | 406 | dev_err(dev, "ci_hdrc_add_device failed, err=%d\n", |
| 319 | "ci_hdrc_add_device failed, err=%d\n", ret); | 407 | ret); |
| 320 | goto err_clk; | 408 | goto err_clk; |
| 321 | } | 409 | } |
| 322 | 410 | ||
| 323 | ret = imx_usbmisc_init_post(data->usbmisc_data); | 411 | ret = imx_usbmisc_init_post(data->usbmisc_data); |
| 324 | if (ret) { | 412 | if (ret) { |
| 325 | dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret); | 413 | dev_err(dev, "usbmisc post failed, ret=%d\n", ret); |
| 326 | goto disable_device; | 414 | goto disable_device; |
| 327 | } | 415 | } |
| 328 | 416 | ||
| 329 | if (data->supports_runtime_pm) { | 417 | if (data->supports_runtime_pm) { |
| 330 | pm_runtime_set_active(&pdev->dev); | 418 | pm_runtime_set_active(dev); |
| 331 | pm_runtime_enable(&pdev->dev); | 419 | pm_runtime_enable(dev); |
| 332 | } | 420 | } |
| 333 | 421 | ||
| 334 | device_set_wakeup_capable(&pdev->dev, true); | 422 | device_set_wakeup_capable(dev, true); |
| 335 | 423 | ||
| 336 | return 0; | 424 | return 0; |
| 337 | 425 | ||
| 338 | disable_device: | 426 | disable_device: |
| 339 | ci_hdrc_remove_device(data->ci_pdev); | 427 | ci_hdrc_remove_device(data->ci_pdev); |
| 340 | err_clk: | 428 | err_clk: |
| 341 | imx_disable_unprepare_clks(&pdev->dev); | 429 | imx_disable_unprepare_clks(dev); |
| 430 | disable_hsic_regulator: | ||
| 431 | if (data->hsic_pad_regulator) | ||
| 432 | ret = regulator_disable(data->hsic_pad_regulator); | ||
| 342 | return ret; | 433 | return ret; |
| 343 | } | 434 | } |
| 344 | 435 | ||
| @@ -355,6 +446,8 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) | |||
| 355 | if (data->override_phy_control) | 446 | if (data->override_phy_control) |
| 356 | usb_phy_shutdown(data->phy); | 447 | usb_phy_shutdown(data->phy); |
| 357 | imx_disable_unprepare_clks(&pdev->dev); | 448 | imx_disable_unprepare_clks(&pdev->dev); |
| 449 | if (data->hsic_pad_regulator) | ||
| 450 | regulator_disable(data->hsic_pad_regulator); | ||
| 358 | 451 | ||
| 359 | return 0; | 452 | return 0; |
| 360 | } | 453 | } |
| @@ -367,9 +460,16 @@ static void ci_hdrc_imx_shutdown(struct platform_device *pdev) | |||
| 367 | static int __maybe_unused imx_controller_suspend(struct device *dev) | 460 | static int __maybe_unused imx_controller_suspend(struct device *dev) |
| 368 | { | 461 | { |
| 369 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); | 462 | struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); |
| 463 | int ret = 0; | ||
| 370 | 464 | ||
| 371 | dev_dbg(dev, "at %s\n", __func__); | 465 | dev_dbg(dev, "at %s\n", __func__); |
| 372 | 466 | ||
| 467 | ret = imx_usbmisc_hsic_set_clk(data->usbmisc_data, false); | ||
| 468 | if (ret) { | ||
| 469 | dev_err(dev, "usbmisc hsic_set_clk failed, ret=%d\n", ret); | ||
| 470 | return ret; | ||
| 471 | } | ||
| 472 | |||
| 373 | imx_disable_unprepare_clks(dev); | 473 | imx_disable_unprepare_clks(dev); |
| 374 | data->in_lpm = true; | 474 | data->in_lpm = true; |
| 375 | 475 | ||
| @@ -400,8 +500,16 @@ static int __maybe_unused imx_controller_resume(struct device *dev) | |||
| 400 | goto clk_disable; | 500 | goto clk_disable; |
| 401 | } | 501 | } |
| 402 | 502 | ||
| 503 | ret = imx_usbmisc_hsic_set_clk(data->usbmisc_data, true); | ||
| 504 | if (ret) { | ||
| 505 | dev_err(dev, "usbmisc hsic_set_clk failed, ret=%d\n", ret); | ||
| 506 | goto hsic_set_clk_fail; | ||
| 507 | } | ||
| 508 | |||
| 403 | return 0; | 509 | return 0; |
| 404 | 510 | ||
| 511 | hsic_set_clk_fail: | ||
| 512 | imx_usbmisc_set_wakeup(data->usbmisc_data, true); | ||
| 405 | clk_disable: | 513 | clk_disable: |
| 406 | imx_disable_unprepare_clks(dev); | 514 | imx_disable_unprepare_clks(dev); |
| 407 | return ret; | 515 | return ret; |
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index 204275f47573..fcecab478934 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h | |||
| @@ -14,10 +14,13 @@ struct imx_usbmisc_data { | |||
| 14 | unsigned int oc_polarity:1; /* over current polarity if oc enabled */ | 14 | unsigned int oc_polarity:1; /* over current polarity if oc enabled */ |
| 15 | unsigned int evdo:1; /* set external vbus divider option */ | 15 | unsigned int evdo:1; /* set external vbus divider option */ |
| 16 | unsigned int ulpi:1; /* connected to an ULPI phy */ | 16 | unsigned int ulpi:1; /* connected to an ULPI phy */ |
| 17 | unsigned int hsic:1; /* HSIC controlller */ | ||
| 17 | }; | 18 | }; |
| 18 | 19 | ||
| 19 | int imx_usbmisc_init(struct imx_usbmisc_data *); | 20 | int imx_usbmisc_init(struct imx_usbmisc_data *data); |
| 20 | int imx_usbmisc_init_post(struct imx_usbmisc_data *); | 21 | int imx_usbmisc_init_post(struct imx_usbmisc_data *data); |
| 21 | int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *, bool); | 22 | int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled); |
| 23 | int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data); | ||
| 24 | int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on); | ||
| 22 | 25 | ||
| 23 | #endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */ | 26 | #endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */ |
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index def80ff547e4..43a15a6e86f5 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c | |||
| @@ -64,10 +64,22 @@ | |||
| 64 | #define MX6_BM_OVER_CUR_DIS BIT(7) | 64 | #define MX6_BM_OVER_CUR_DIS BIT(7) |
| 65 | #define MX6_BM_OVER_CUR_POLARITY BIT(8) | 65 | #define MX6_BM_OVER_CUR_POLARITY BIT(8) |
| 66 | #define MX6_BM_WAKEUP_ENABLE BIT(10) | 66 | #define MX6_BM_WAKEUP_ENABLE BIT(10) |
| 67 | #define MX6_BM_UTMI_ON_CLOCK BIT(13) | ||
| 67 | #define MX6_BM_ID_WAKEUP BIT(16) | 68 | #define MX6_BM_ID_WAKEUP BIT(16) |
| 68 | #define MX6_BM_VBUS_WAKEUP BIT(17) | 69 | #define MX6_BM_VBUS_WAKEUP BIT(17) |
| 69 | #define MX6SX_BM_DPDM_WAKEUP_EN BIT(29) | 70 | #define MX6SX_BM_DPDM_WAKEUP_EN BIT(29) |
| 70 | #define MX6_BM_WAKEUP_INTR BIT(31) | 71 | #define MX6_BM_WAKEUP_INTR BIT(31) |
| 72 | |||
| 73 | #define MX6_USB_HSIC_CTRL_OFFSET 0x10 | ||
| 74 | /* Send resume signal without 480Mhz PHY clock */ | ||
| 75 | #define MX6SX_BM_HSIC_AUTO_RESUME BIT(23) | ||
| 76 | /* set before portsc.suspendM = 1 */ | ||
| 77 | #define MX6_BM_HSIC_DEV_CONN BIT(21) | ||
| 78 | /* HSIC enable */ | ||
| 79 | #define MX6_BM_HSIC_EN BIT(12) | ||
| 80 | /* Force HSIC module 480M clock on, even when in Host is in suspend mode */ | ||
| 81 | #define MX6_BM_HSIC_CLK_ON BIT(11) | ||
| 82 | |||
| 71 | #define MX6_USB_OTG1_PHY_CTRL 0x18 | 83 | #define MX6_USB_OTG1_PHY_CTRL 0x18 |
| 72 | /* For imx6dql, it is host-only controller, for later imx6, it is otg's */ | 84 | /* For imx6dql, it is host-only controller, for later imx6, it is otg's */ |
| 73 | #define MX6_USB_OTG2_PHY_CTRL 0x1c | 85 | #define MX6_USB_OTG2_PHY_CTRL 0x1c |
| @@ -94,6 +106,10 @@ struct usbmisc_ops { | |||
| 94 | int (*post)(struct imx_usbmisc_data *data); | 106 | int (*post)(struct imx_usbmisc_data *data); |
| 95 | /* It's called when we need to enable/disable usb wakeup */ | 107 | /* It's called when we need to enable/disable usb wakeup */ |
| 96 | int (*set_wakeup)(struct imx_usbmisc_data *data, bool enabled); | 108 | int (*set_wakeup)(struct imx_usbmisc_data *data, bool enabled); |
| 109 | /* It's called before setting portsc.suspendM */ | ||
| 110 | int (*hsic_set_connect)(struct imx_usbmisc_data *data); | ||
| 111 | /* It's called during suspend/resume */ | ||
| 112 | int (*hsic_set_clk)(struct imx_usbmisc_data *data, bool enabled); | ||
| 97 | }; | 113 | }; |
| 98 | 114 | ||
| 99 | struct imx_usbmisc { | 115 | struct imx_usbmisc { |
| @@ -353,6 +369,18 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) | |||
| 353 | writel(reg | MX6_BM_NON_BURST_SETTING, | 369 | writel(reg | MX6_BM_NON_BURST_SETTING, |
| 354 | usbmisc->base + data->index * 4); | 370 | usbmisc->base + data->index * 4); |
| 355 | 371 | ||
| 372 | /* For HSIC controller */ | ||
| 373 | if (data->hsic) { | ||
| 374 | reg = readl(usbmisc->base + data->index * 4); | ||
| 375 | writel(reg | MX6_BM_UTMI_ON_CLOCK, | ||
| 376 | usbmisc->base + data->index * 4); | ||
| 377 | reg = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET | ||
| 378 | + (data->index - 2) * 4); | ||
| 379 | reg |= MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON; | ||
| 380 | writel(reg, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET | ||
| 381 | + (data->index - 2) * 4); | ||
| 382 | } | ||
| 383 | |||
| 356 | spin_unlock_irqrestore(&usbmisc->lock, flags); | 384 | spin_unlock_irqrestore(&usbmisc->lock, flags); |
| 357 | 385 | ||
| 358 | usbmisc_imx6q_set_wakeup(data, false); | 386 | usbmisc_imx6q_set_wakeup(data, false); |
| @@ -360,6 +388,79 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) | |||
| 360 | return 0; | 388 | return 0; |
| 361 | } | 389 | } |
| 362 | 390 | ||
| 391 | static int usbmisc_imx6_hsic_get_reg_offset(struct imx_usbmisc_data *data) | ||
| 392 | { | ||
| 393 | int offset, ret = 0; | ||
| 394 | |||
| 395 | if (data->index == 2 || data->index == 3) { | ||
| 396 | offset = (data->index - 2) * 4; | ||
| 397 | } else if (data->index == 0) { | ||
| 398 | /* | ||
| 399 | * For SoCs like i.MX7D and later, each USB controller has | ||
| 400 | * its own non-core register region. For SoCs before i.MX7D, | ||
| 401 | * the first two USB controllers are non-HSIC controllers. | ||
| 402 | */ | ||
| 403 | offset = 0; | ||
| 404 | } else { | ||
| 405 | dev_err(data->dev, "index is error for usbmisc\n"); | ||
| 406 | ret = -EINVAL; | ||
| 407 | } | ||
| 408 | |||
| 409 | return ret ? ret : offset; | ||
| 410 | } | ||
| 411 | |||
| 412 | static int usbmisc_imx6_hsic_set_connect(struct imx_usbmisc_data *data) | ||
| 413 | { | ||
| 414 | unsigned long flags; | ||
| 415 | u32 val; | ||
| 416 | struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); | ||
| 417 | int offset; | ||
| 418 | |||
| 419 | spin_lock_irqsave(&usbmisc->lock, flags); | ||
| 420 | offset = usbmisc_imx6_hsic_get_reg_offset(data); | ||
| 421 | if (offset < 0) { | ||
| 422 | spin_unlock_irqrestore(&usbmisc->lock, flags); | ||
| 423 | return offset; | ||
| 424 | } | ||
| 425 | |||
| 426 | val = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET + offset); | ||
| 427 | if (!(val & MX6_BM_HSIC_DEV_CONN)) | ||
| 428 | writel(val | MX6_BM_HSIC_DEV_CONN, | ||
| 429 | usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET + offset); | ||
| 430 | |||
| 431 | spin_unlock_irqrestore(&usbmisc->lock, flags); | ||
| 432 | |||
| 433 | return 0; | ||
| 434 | } | ||
| 435 | |||
| 436 | static int usbmisc_imx6_hsic_set_clk(struct imx_usbmisc_data *data, bool on) | ||
| 437 | { | ||
| 438 | unsigned long flags; | ||
| 439 | u32 val; | ||
| 440 | struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); | ||
| 441 | int offset; | ||
| 442 | |||
| 443 | spin_lock_irqsave(&usbmisc->lock, flags); | ||
| 444 | offset = usbmisc_imx6_hsic_get_reg_offset(data); | ||
| 445 | if (offset < 0) { | ||
| 446 | spin_unlock_irqrestore(&usbmisc->lock, flags); | ||
| 447 | return offset; | ||
| 448 | } | ||
| 449 | |||
| 450 | val = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET + offset); | ||
| 451 | val |= MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON; | ||
| 452 | if (on) | ||
| 453 | val |= MX6_BM_HSIC_CLK_ON; | ||
| 454 | else | ||
| 455 | val &= ~MX6_BM_HSIC_CLK_ON; | ||
| 456 | |||
| 457 | writel(val, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET + offset); | ||
| 458 | spin_unlock_irqrestore(&usbmisc->lock, flags); | ||
| 459 | |||
| 460 | return 0; | ||
| 461 | } | ||
| 462 | |||
| 463 | |||
| 363 | static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data) | 464 | static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data) |
| 364 | { | 465 | { |
| 365 | void __iomem *reg = NULL; | 466 | void __iomem *reg = NULL; |
| @@ -385,6 +486,13 @@ static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data) | |||
| 385 | spin_unlock_irqrestore(&usbmisc->lock, flags); | 486 | spin_unlock_irqrestore(&usbmisc->lock, flags); |
| 386 | } | 487 | } |
| 387 | 488 | ||
| 489 | /* For HSIC controller */ | ||
| 490 | if (data->hsic) { | ||
| 491 | val = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET); | ||
| 492 | val |= MX6SX_BM_HSIC_AUTO_RESUME; | ||
| 493 | writel(val, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET); | ||
| 494 | } | ||
| 495 | |||
| 388 | return 0; | 496 | return 0; |
| 389 | } | 497 | } |
| 390 | 498 | ||
| @@ -454,6 +562,7 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) | |||
| 454 | reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK; | 562 | reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK; |
| 455 | writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID, | 563 | writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID, |
| 456 | usbmisc->base + MX7D_USBNC_USB_CTRL2); | 564 | usbmisc->base + MX7D_USBNC_USB_CTRL2); |
| 565 | |||
| 457 | spin_unlock_irqrestore(&usbmisc->lock, flags); | 566 | spin_unlock_irqrestore(&usbmisc->lock, flags); |
| 458 | 567 | ||
| 459 | usbmisc_imx7d_set_wakeup(data, false); | 568 | usbmisc_imx7d_set_wakeup(data, false); |
| @@ -481,6 +590,8 @@ static const struct usbmisc_ops imx53_usbmisc_ops = { | |||
| 481 | static const struct usbmisc_ops imx6q_usbmisc_ops = { | 590 | static const struct usbmisc_ops imx6q_usbmisc_ops = { |
| 482 | .set_wakeup = usbmisc_imx6q_set_wakeup, | 591 | .set_wakeup = usbmisc_imx6q_set_wakeup, |
| 483 | .init = usbmisc_imx6q_init, | 592 | .init = usbmisc_imx6q_init, |
| 593 | .hsic_set_connect = usbmisc_imx6_hsic_set_connect, | ||
| 594 | .hsic_set_clk = usbmisc_imx6_hsic_set_clk, | ||
| 484 | }; | 595 | }; |
| 485 | 596 | ||
| 486 | static const struct usbmisc_ops vf610_usbmisc_ops = { | 597 | static const struct usbmisc_ops vf610_usbmisc_ops = { |
| @@ -490,6 +601,8 @@ static const struct usbmisc_ops vf610_usbmisc_ops = { | |||
| 490 | static const struct usbmisc_ops imx6sx_usbmisc_ops = { | 601 | static const struct usbmisc_ops imx6sx_usbmisc_ops = { |
| 491 | .set_wakeup = usbmisc_imx6q_set_wakeup, | 602 | .set_wakeup = usbmisc_imx6q_set_wakeup, |
| 492 | .init = usbmisc_imx6sx_init, | 603 | .init = usbmisc_imx6sx_init, |
| 604 | .hsic_set_connect = usbmisc_imx6_hsic_set_connect, | ||
| 605 | .hsic_set_clk = usbmisc_imx6_hsic_set_clk, | ||
| 493 | }; | 606 | }; |
| 494 | 607 | ||
| 495 | static const struct usbmisc_ops imx7d_usbmisc_ops = { | 608 | static const struct usbmisc_ops imx7d_usbmisc_ops = { |
| @@ -546,6 +659,33 @@ int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled) | |||
| 546 | } | 659 | } |
| 547 | EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup); | 660 | EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup); |
| 548 | 661 | ||
| 662 | int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data) | ||
| 663 | { | ||
| 664 | struct imx_usbmisc *usbmisc; | ||
| 665 | |||
| 666 | if (!data) | ||
| 667 | return 0; | ||
| 668 | |||
| 669 | usbmisc = dev_get_drvdata(data->dev); | ||
| 670 | if (!usbmisc->ops->hsic_set_connect || !data->hsic) | ||
| 671 | return 0; | ||
| 672 | return usbmisc->ops->hsic_set_connect(data); | ||
| 673 | } | ||
| 674 | EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_connect); | ||
| 675 | |||
| 676 | int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on) | ||
| 677 | { | ||
| 678 | struct imx_usbmisc *usbmisc; | ||
| 679 | |||
| 680 | if (!data) | ||
| 681 | return 0; | ||
| 682 | |||
| 683 | usbmisc = dev_get_drvdata(data->dev); | ||
| 684 | if (!usbmisc->ops->hsic_set_clk || !data->hsic) | ||
| 685 | return 0; | ||
| 686 | return usbmisc->ops->hsic_set_clk(data, on); | ||
| 687 | } | ||
| 688 | EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk); | ||
| 549 | static const struct of_device_id usbmisc_imx_dt_ids[] = { | 689 | static const struct of_device_id usbmisc_imx_dt_ids[] = { |
| 550 | { | 690 | { |
| 551 | .compatible = "fsl,imx25-usbmisc", | 691 | .compatible = "fsl,imx25-usbmisc", |
