15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 28e22978cSAlexander Shishkin /* 38e22978cSAlexander Shishkin * Copyright 2012 Freescale Semiconductor, Inc. 48e22978cSAlexander Shishkin * Copyright (C) 2012 Marek Vasut <marex@denx.de> 58e22978cSAlexander Shishkin * on behalf of DENX Software Engineering GmbH 68e22978cSAlexander Shishkin */ 78e22978cSAlexander Shishkin 88e22978cSAlexander Shishkin #include <linux/module.h> 98e22978cSAlexander Shishkin #include <linux/of_platform.h> 108e22978cSAlexander Shishkin #include <linux/platform_device.h> 118e22978cSAlexander Shishkin #include <linux/pm_runtime.h> 128e22978cSAlexander Shishkin #include <linux/usb/chipidea.h> 13d13631bbSFabien Lahoudere #include <linux/usb/of.h> 148e22978cSAlexander Shishkin #include <linux/clk.h> 157c8e8909SPeter Chen #include <linux/pinctrl/consumer.h> 16d1609c31SPeter Chen #include <linux/pm_qos.h> 178e22978cSAlexander Shishkin 188e22978cSAlexander Shishkin #include "ci.h" 198e22978cSAlexander Shishkin #include "ci_hdrc_imx.h" 208e22978cSAlexander Shishkin 211071055eSPeter Chen struct ci_hdrc_imx_platform_flag { 221071055eSPeter Chen unsigned int flags; 231071055eSPeter Chen }; 241071055eSPeter Chen 2581345722SStefan Wahren static const struct ci_hdrc_imx_platform_flag imx23_usb_data = { 2681345722SStefan Wahren .flags = CI_HDRC_TURN_VBUS_EARLY_ON | 2781345722SStefan Wahren CI_HDRC_DISABLE_STREAMING, 2881345722SStefan Wahren }; 2981345722SStefan Wahren 301071055eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { 3104e16379SSebastian Reichel .flags = CI_HDRC_DISABLE_STREAMING, 321071055eSPeter Chen }; 331071055eSPeter Chen 341071055eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { 356adb9b7bSLi Jun .flags = CI_HDRC_IMX28_WRITE_FIX | 360ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 370ef877a4SPeter Chen CI_HDRC_DISABLE_STREAMING, 381071055eSPeter Chen }; 391071055eSPeter Chen 40e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = { 416adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 420ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 430ef877a4SPeter Chen CI_HDRC_DISABLE_STREAMING, 44e14db48dSPeter Chen }; 45e14db48dSPeter Chen 46e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = { 476adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 480ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 490ef877a4SPeter Chen CI_HDRC_DISABLE_HOST_STREAMING, 50e14db48dSPeter Chen }; 51e14db48dSPeter Chen 52e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { 536adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 540ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 550ef877a4SPeter Chen CI_HDRC_DISABLE_HOST_STREAMING, 56e14db48dSPeter Chen }; 57e14db48dSPeter Chen 5852fe568eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = { 5952fe568eSPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 6052fe568eSPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON, 6152fe568eSPeter Chen }; 6252fe568eSPeter Chen 635cb377c5SPeter Chen static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = { 645cb377c5SPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, 655cb377c5SPeter Chen }; 665cb377c5SPeter Chen 67d1609c31SPeter Chen static const struct ci_hdrc_imx_platform_flag imx7ulp_usb_data = { 68d1609c31SPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 69d1609c31SPeter Chen CI_HDRC_PMQOS, 70d1609c31SPeter Chen }; 71d1609c31SPeter Chen 721071055eSPeter Chen static const struct of_device_id ci_hdrc_imx_dt_ids[] = { 7381345722SStefan Wahren { .compatible = "fsl,imx23-usb", .data = &imx23_usb_data}, 741071055eSPeter Chen { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, 751071055eSPeter Chen { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, 76e14db48dSPeter Chen { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, 77e14db48dSPeter Chen { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, 788315b77dSLi Jun { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data}, 7952fe568eSPeter Chen { .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data}, 805cb377c5SPeter Chen { .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data}, 81d1609c31SPeter Chen { .compatible = "fsl,imx7ulp-usb", .data = &imx7ulp_usb_data}, 821071055eSPeter Chen { /* sentinel */ } 831071055eSPeter Chen }; 841071055eSPeter Chen MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); 851071055eSPeter Chen 868e22978cSAlexander Shishkin struct ci_hdrc_imx_data { 878e22978cSAlexander Shishkin struct usb_phy *phy; 888e22978cSAlexander Shishkin struct platform_device *ci_pdev; 898e22978cSAlexander Shishkin struct clk *clk; 9005986ba9SSascha Hauer struct imx_usbmisc_data *usbmisc_data; 91e14db48dSPeter Chen bool supports_runtime_pm; 92be9cae24SSebastian Reichel bool override_phy_control; 93e14db48dSPeter Chen bool in_lpm; 947c8e8909SPeter Chen struct pinctrl *pinctrl; 957c8e8909SPeter Chen struct pinctrl_state *pinctrl_hsic_active; 967c8e8909SPeter Chen struct regulator *hsic_pad_regulator; 97ae3e57aeSPeter Chen /* SoC before i.mx6 (except imx23/imx28) needs three clks */ 98ae3e57aeSPeter Chen bool need_three_clks; 99ae3e57aeSPeter Chen struct clk *clk_ipg; 100ae3e57aeSPeter Chen struct clk *clk_ahb; 101ae3e57aeSPeter Chen struct clk *clk_per; 102ae3e57aeSPeter Chen /* --------------------------------- */ 103d1609c31SPeter Chen struct pm_qos_request pm_qos_req; 104d1609c31SPeter Chen const struct ci_hdrc_imx_platform_flag *plat_data; 1058e22978cSAlexander Shishkin }; 1068e22978cSAlexander Shishkin 1078e22978cSAlexander Shishkin /* Common functions shared by usbmisc drivers */ 1088e22978cSAlexander Shishkin 10905986ba9SSascha Hauer static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) 1108e22978cSAlexander Shishkin { 111f40017e0SStefan Agner struct platform_device *misc_pdev; 1128e22978cSAlexander Shishkin struct device_node *np = dev->of_node; 1138e22978cSAlexander Shishkin struct of_phandle_args args; 11405986ba9SSascha Hauer struct imx_usbmisc_data *data; 1158e22978cSAlexander Shishkin int ret; 1168e22978cSAlexander Shishkin 11705986ba9SSascha Hauer /* 11805986ba9SSascha Hauer * In case the fsl,usbmisc property is not present this device doesn't 11905986ba9SSascha Hauer * need usbmisc. Return NULL (which is no error here) 12005986ba9SSascha Hauer */ 12105986ba9SSascha Hauer if (!of_get_property(np, "fsl,usbmisc", NULL)) 12205986ba9SSascha Hauer return NULL; 12305986ba9SSascha Hauer 12405986ba9SSascha Hauer data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 12505986ba9SSascha Hauer if (!data) 12605986ba9SSascha Hauer return ERR_PTR(-ENOMEM); 1278e22978cSAlexander Shishkin 1288e22978cSAlexander Shishkin ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", 1298e22978cSAlexander Shishkin 0, &args); 1308e22978cSAlexander Shishkin if (ret) { 1318e22978cSAlexander Shishkin dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", 1328e22978cSAlexander Shishkin ret); 13305986ba9SSascha Hauer return ERR_PTR(ret); 1348e22978cSAlexander Shishkin } 13505986ba9SSascha Hauer 13605986ba9SSascha Hauer data->index = args.args[0]; 137f40017e0SStefan Agner 138f40017e0SStefan Agner misc_pdev = of_find_device_by_node(args.np); 1398e22978cSAlexander Shishkin of_node_put(args.np); 1408e22978cSAlexander Shishkin 141ef12da91STomeu Vizoso if (!misc_pdev || !platform_get_drvdata(misc_pdev)) 142f40017e0SStefan Agner return ERR_PTR(-EPROBE_DEFER); 143f40017e0SStefan Agner 144f40017e0SStefan Agner data->dev = &misc_pdev->dev; 145f40017e0SStefan Agner 146a82bf696SUwe Kleine-König /* 147a82bf696SUwe Kleine-König * Check the various over current related properties. If over current 148a82bf696SUwe Kleine-König * detection is disabled we're not interested in the polarity. 149a82bf696SUwe Kleine-König */ 150a82bf696SUwe Kleine-König if (of_find_property(np, "disable-over-current", NULL)) { 15105986ba9SSascha Hauer data->disable_oc = 1; 152a82bf696SUwe Kleine-König } else if (of_find_property(np, "over-current-active-high", NULL)) { 153a82bf696SUwe Kleine-König data->oc_pol_active_low = 0; 154a82bf696SUwe Kleine-König data->oc_pol_configured = 1; 155a82bf696SUwe Kleine-König } else if (of_find_property(np, "over-current-active-low", NULL)) { 156a82bf696SUwe Kleine-König data->oc_pol_active_low = 1; 157a82bf696SUwe Kleine-König data->oc_pol_configured = 1; 1581bf4743fSUwe Kleine-König } else { 1591bf4743fSUwe Kleine-König dev_warn(dev, "No over current polarity defined\n"); 160a82bf696SUwe Kleine-König } 1619dba516eSLi Jun 1625f0632c4SPhilipp Puschmann data->pwr_pol = of_property_read_bool(np, "power-active-high"); 1635f0632c4SPhilipp Puschmann data->evdo = of_property_read_bool(np, "external-vbus-divider"); 1648e22978cSAlexander Shishkin 165d13631bbSFabien Lahoudere if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) 166d13631bbSFabien Lahoudere data->ulpi = 1; 167d13631bbSFabien Lahoudere 16805986ba9SSascha Hauer return data; 1698e22978cSAlexander Shishkin } 1708e22978cSAlexander Shishkin 1718e22978cSAlexander Shishkin /* End of common functions shared by usbmisc drivers*/ 172ae3e57aeSPeter Chen static int imx_get_clks(struct device *dev) 173ae3e57aeSPeter Chen { 174ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 175ae3e57aeSPeter Chen int ret = 0; 176ae3e57aeSPeter Chen 177ae3e57aeSPeter Chen data->clk_ipg = devm_clk_get(dev, "ipg"); 178ae3e57aeSPeter Chen if (IS_ERR(data->clk_ipg)) { 179ae3e57aeSPeter Chen /* If the platform only needs one clocks */ 180ae3e57aeSPeter Chen data->clk = devm_clk_get(dev, NULL); 181ae3e57aeSPeter Chen if (IS_ERR(data->clk)) { 182ae3e57aeSPeter Chen ret = PTR_ERR(data->clk); 183ae3e57aeSPeter Chen dev_err(dev, 184ae3e57aeSPeter Chen "Failed to get clks, err=%ld,%ld\n", 185ae3e57aeSPeter Chen PTR_ERR(data->clk), PTR_ERR(data->clk_ipg)); 186ae3e57aeSPeter Chen return ret; 187ae3e57aeSPeter Chen } 188ae3e57aeSPeter Chen return ret; 189ae3e57aeSPeter Chen } 190ae3e57aeSPeter Chen 191ae3e57aeSPeter Chen data->clk_ahb = devm_clk_get(dev, "ahb"); 192ae3e57aeSPeter Chen if (IS_ERR(data->clk_ahb)) { 193ae3e57aeSPeter Chen ret = PTR_ERR(data->clk_ahb); 194ae3e57aeSPeter Chen dev_err(dev, 195ae3e57aeSPeter Chen "Failed to get ahb clock, err=%d\n", ret); 196ae3e57aeSPeter Chen return ret; 197ae3e57aeSPeter Chen } 198ae3e57aeSPeter Chen 199ae3e57aeSPeter Chen data->clk_per = devm_clk_get(dev, "per"); 200ae3e57aeSPeter Chen if (IS_ERR(data->clk_per)) { 201ae3e57aeSPeter Chen ret = PTR_ERR(data->clk_per); 202ae3e57aeSPeter Chen dev_err(dev, 203ae3e57aeSPeter Chen "Failed to get per clock, err=%d\n", ret); 204ae3e57aeSPeter Chen return ret; 205ae3e57aeSPeter Chen } 206ae3e57aeSPeter Chen 207ae3e57aeSPeter Chen data->need_three_clks = true; 208ae3e57aeSPeter Chen return ret; 209ae3e57aeSPeter Chen } 210ae3e57aeSPeter Chen 211ae3e57aeSPeter Chen static int imx_prepare_enable_clks(struct device *dev) 212ae3e57aeSPeter Chen { 213ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 214ae3e57aeSPeter Chen int ret = 0; 215ae3e57aeSPeter Chen 216ae3e57aeSPeter Chen if (data->need_three_clks) { 217ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_ipg); 218ae3e57aeSPeter Chen if (ret) { 219ae3e57aeSPeter Chen dev_err(dev, 220ae3e57aeSPeter Chen "Failed to prepare/enable ipg clk, err=%d\n", 221ae3e57aeSPeter Chen ret); 222ae3e57aeSPeter Chen return ret; 223ae3e57aeSPeter Chen } 224ae3e57aeSPeter Chen 225ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_ahb); 226ae3e57aeSPeter Chen if (ret) { 227ae3e57aeSPeter Chen dev_err(dev, 228ae3e57aeSPeter Chen "Failed to prepare/enable ahb clk, err=%d\n", 229ae3e57aeSPeter Chen ret); 230ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 231ae3e57aeSPeter Chen return ret; 232ae3e57aeSPeter Chen } 233ae3e57aeSPeter Chen 234ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_per); 235ae3e57aeSPeter Chen if (ret) { 236ae3e57aeSPeter Chen dev_err(dev, 237ae3e57aeSPeter Chen "Failed to prepare/enable per clk, err=%d\n", 238ae3e57aeSPeter Chen ret); 239ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ahb); 240ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 241ae3e57aeSPeter Chen return ret; 242ae3e57aeSPeter Chen } 243ae3e57aeSPeter Chen } else { 244ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk); 245ae3e57aeSPeter Chen if (ret) { 246ae3e57aeSPeter Chen dev_err(dev, 247ae3e57aeSPeter Chen "Failed to prepare/enable clk, err=%d\n", 248ae3e57aeSPeter Chen ret); 249ae3e57aeSPeter Chen return ret; 250ae3e57aeSPeter Chen } 251ae3e57aeSPeter Chen } 252ae3e57aeSPeter Chen 253ae3e57aeSPeter Chen return ret; 254ae3e57aeSPeter Chen } 255ae3e57aeSPeter Chen 256ae3e57aeSPeter Chen static void imx_disable_unprepare_clks(struct device *dev) 257ae3e57aeSPeter Chen { 258ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 259ae3e57aeSPeter Chen 260ae3e57aeSPeter Chen if (data->need_three_clks) { 261ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_per); 262ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ahb); 263ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 264ae3e57aeSPeter Chen } else { 265ae3e57aeSPeter Chen clk_disable_unprepare(data->clk); 266ae3e57aeSPeter Chen } 267ae3e57aeSPeter Chen } 2688e22978cSAlexander Shishkin 2697c8e8909SPeter Chen static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event) 2707c8e8909SPeter Chen { 2717c8e8909SPeter Chen struct device *dev = ci->dev->parent; 2727c8e8909SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 2737c8e8909SPeter Chen int ret = 0; 2747c8e8909SPeter Chen 2757c8e8909SPeter Chen switch (event) { 2767c8e8909SPeter Chen case CI_HDRC_IMX_HSIC_ACTIVE_EVENT: 2777c8e8909SPeter Chen ret = pinctrl_select_state(data->pinctrl, 2787c8e8909SPeter Chen data->pinctrl_hsic_active); 2797c8e8909SPeter Chen if (ret) 2807c8e8909SPeter Chen dev_err(dev, "hsic_active select failed, err=%d\n", 2817c8e8909SPeter Chen ret); 2827c8e8909SPeter Chen break; 2837c8e8909SPeter Chen case CI_HDRC_IMX_HSIC_SUSPEND_EVENT: 2847c8e8909SPeter Chen ret = imx_usbmisc_hsic_set_connect(data->usbmisc_data); 2857c8e8909SPeter Chen if (ret) 2867c8e8909SPeter Chen dev_err(dev, 2877c8e8909SPeter Chen "hsic_set_connect failed, err=%d\n", ret); 2887c8e8909SPeter Chen break; 2897c8e8909SPeter Chen default: 2907c8e8909SPeter Chen break; 2917c8e8909SPeter Chen } 2927c8e8909SPeter Chen 2937c8e8909SPeter Chen return ret; 2947c8e8909SPeter Chen } 2957c8e8909SPeter Chen 2968e22978cSAlexander Shishkin static int ci_hdrc_imx_probe(struct platform_device *pdev) 2978e22978cSAlexander Shishkin { 2988e22978cSAlexander Shishkin struct ci_hdrc_imx_data *data; 2998e22978cSAlexander Shishkin struct ci_hdrc_platform_data pdata = { 300c844d6c8SAlexander Shiyan .name = dev_name(&pdev->dev), 3018e22978cSAlexander Shishkin .capoffset = DEF_CAPOFFSET, 3027c8e8909SPeter Chen .notify_event = ci_hdrc_imx_notify_event, 3038e22978cSAlexander Shishkin }; 3048e22978cSAlexander Shishkin int ret; 3056f51bc34SLABBE Corentin const struct of_device_id *of_id; 3066f51bc34SLABBE Corentin const struct ci_hdrc_imx_platform_flag *imx_platform_flag; 307be9cae24SSebastian Reichel struct device_node *np = pdev->dev.of_node; 3087c8e8909SPeter Chen struct device *dev = &pdev->dev; 3097c8e8909SPeter Chen struct pinctrl_state *pinctrl_hsic_idle; 3106f51bc34SLABBE Corentin 3117c8e8909SPeter Chen of_id = of_match_device(ci_hdrc_imx_dt_ids, dev); 3126f51bc34SLABBE Corentin if (!of_id) 3136f51bc34SLABBE Corentin return -ENODEV; 3146f51bc34SLABBE Corentin 3156f51bc34SLABBE Corentin imx_platform_flag = of_id->data; 3168e22978cSAlexander Shishkin 3178e22978cSAlexander Shishkin data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 31873529828SFabio Estevam if (!data) 3198e22978cSAlexander Shishkin return -ENOMEM; 3208e22978cSAlexander Shishkin 321d1609c31SPeter Chen data->plat_data = imx_platform_flag; 322d1609c31SPeter Chen pdata.flags |= imx_platform_flag->flags; 323ae3e57aeSPeter Chen platform_set_drvdata(pdev, data); 3247c8e8909SPeter Chen data->usbmisc_data = usbmisc_get_init_data(dev); 32505986ba9SSascha Hauer if (IS_ERR(data->usbmisc_data)) 32605986ba9SSascha Hauer return PTR_ERR(data->usbmisc_data); 32705986ba9SSascha Hauer 3288ff396feSPeter Chen if ((of_usb_get_phy_mode(dev->of_node) == USBPHY_INTERFACE_MODE_HSIC) 3298ff396feSPeter Chen && data->usbmisc_data) { 3307c8e8909SPeter Chen pdata.flags |= CI_HDRC_IMX_IS_HSIC; 3317c8e8909SPeter Chen data->usbmisc_data->hsic = 1; 3327c8e8909SPeter Chen data->pinctrl = devm_pinctrl_get(dev); 3337c8e8909SPeter Chen if (IS_ERR(data->pinctrl)) { 3347c8e8909SPeter Chen dev_err(dev, "pinctrl get failed, err=%ld\n", 3357c8e8909SPeter Chen PTR_ERR(data->pinctrl)); 3367c8e8909SPeter Chen return PTR_ERR(data->pinctrl); 3377c8e8909SPeter Chen } 338ae3e57aeSPeter Chen 3397c8e8909SPeter Chen pinctrl_hsic_idle = pinctrl_lookup_state(data->pinctrl, "idle"); 3407c8e8909SPeter Chen if (IS_ERR(pinctrl_hsic_idle)) { 3417c8e8909SPeter Chen dev_err(dev, 3427c8e8909SPeter Chen "pinctrl_hsic_idle lookup failed, err=%ld\n", 3437c8e8909SPeter Chen PTR_ERR(pinctrl_hsic_idle)); 3447c8e8909SPeter Chen return PTR_ERR(pinctrl_hsic_idle); 3457c8e8909SPeter Chen } 3468e22978cSAlexander Shishkin 3477c8e8909SPeter Chen ret = pinctrl_select_state(data->pinctrl, pinctrl_hsic_idle); 3487c8e8909SPeter Chen if (ret) { 3497c8e8909SPeter Chen dev_err(dev, "hsic_idle select failed, err=%d\n", ret); 3507c8e8909SPeter Chen return ret; 3517c8e8909SPeter Chen } 3527c8e8909SPeter Chen 3537c8e8909SPeter Chen data->pinctrl_hsic_active = pinctrl_lookup_state(data->pinctrl, 3547c8e8909SPeter Chen "active"); 3557c8e8909SPeter Chen if (IS_ERR(data->pinctrl_hsic_active)) { 3567c8e8909SPeter Chen dev_err(dev, 3577c8e8909SPeter Chen "pinctrl_hsic_active lookup failed, err=%ld\n", 3587c8e8909SPeter Chen PTR_ERR(data->pinctrl_hsic_active)); 3597c8e8909SPeter Chen return PTR_ERR(data->pinctrl_hsic_active); 3607c8e8909SPeter Chen } 3617c8e8909SPeter Chen 3627c8e8909SPeter Chen data->hsic_pad_regulator = devm_regulator_get(dev, "hsic"); 3637c8e8909SPeter Chen if (PTR_ERR(data->hsic_pad_regulator) == -EPROBE_DEFER) { 3647c8e8909SPeter Chen return -EPROBE_DEFER; 3657c8e8909SPeter Chen } else if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) { 3667c8e8909SPeter Chen /* no pad regualator is needed */ 3677c8e8909SPeter Chen data->hsic_pad_regulator = NULL; 3687c8e8909SPeter Chen } else if (IS_ERR(data->hsic_pad_regulator)) { 3697c8e8909SPeter Chen dev_err(dev, "Get HSIC pad regulator error: %ld\n", 3707c8e8909SPeter Chen PTR_ERR(data->hsic_pad_regulator)); 3717c8e8909SPeter Chen return PTR_ERR(data->hsic_pad_regulator); 3727c8e8909SPeter Chen } 3737c8e8909SPeter Chen 3747c8e8909SPeter Chen if (data->hsic_pad_regulator) { 3757c8e8909SPeter Chen ret = regulator_enable(data->hsic_pad_regulator); 3767c8e8909SPeter Chen if (ret) { 3777c8e8909SPeter Chen dev_err(dev, 3787c8e8909SPeter Chen "Failed to enable HSIC pad regulator\n"); 3797c8e8909SPeter Chen return ret; 3807c8e8909SPeter Chen } 3817c8e8909SPeter Chen } 3827c8e8909SPeter Chen } 383d1609c31SPeter Chen 384d1609c31SPeter Chen if (pdata.flags & CI_HDRC_PMQOS) 385d1609c31SPeter Chen pm_qos_add_request(&data->pm_qos_req, 386d1609c31SPeter Chen PM_QOS_CPU_DMA_LATENCY, 0); 387d1609c31SPeter Chen 3887c8e8909SPeter Chen ret = imx_get_clks(dev); 3897c8e8909SPeter Chen if (ret) 3907c8e8909SPeter Chen goto disable_hsic_regulator; 3917c8e8909SPeter Chen 3927c8e8909SPeter Chen ret = imx_prepare_enable_clks(dev); 3937c8e8909SPeter Chen if (ret) 3947c8e8909SPeter Chen goto disable_hsic_regulator; 3957c8e8909SPeter Chen 3967c8e8909SPeter Chen data->phy = devm_usb_get_phy_by_phandle(dev, "fsl,usbphy", 0); 397af59a8b1SPeter Chen if (IS_ERR(data->phy)) { 398af59a8b1SPeter Chen ret = PTR_ERR(data->phy); 39916853d7bSMarkus Pargmann /* Return -EINVAL if no usbphy is available */ 40016853d7bSMarkus Pargmann if (ret == -ENODEV) 401ed5a419bSPeter Chen data->phy = NULL; 402ed5a419bSPeter Chen else 4038e22978cSAlexander Shishkin goto err_clk; 4048e22978cSAlexander Shishkin } 4058e22978cSAlexander Shishkin 406ef44cb42SAntoine Tenart pdata.usb_phy = data->phy; 407be9cae24SSebastian Reichel 40803e6275aSAndrey Smirnov if ((of_device_is_compatible(np, "fsl,imx53-usb") || 40903e6275aSAndrey Smirnov of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy && 410be9cae24SSebastian Reichel of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) { 411be9cae24SSebastian Reichel pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL; 412be9cae24SSebastian Reichel data->override_phy_control = true; 413be9cae24SSebastian Reichel usb_phy_init(pdata.usb_phy); 414be9cae24SSebastian Reichel } 415be9cae24SSebastian Reichel 416e14db48dSPeter Chen if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) 417e14db48dSPeter Chen data->supports_runtime_pm = true; 4181071055eSPeter Chen 41905986ba9SSascha Hauer ret = imx_usbmisc_init(data->usbmisc_data); 4208e22978cSAlexander Shishkin if (ret) { 4217c8e8909SPeter Chen dev_err(dev, "usbmisc init failed, ret=%d\n", ret); 422af59a8b1SPeter Chen goto err_clk; 4238e22978cSAlexander Shishkin } 4248e22978cSAlexander Shishkin 4257c8e8909SPeter Chen data->ci_pdev = ci_hdrc_add_device(dev, 4268e22978cSAlexander Shishkin pdev->resource, pdev->num_resources, 4278e22978cSAlexander Shishkin &pdata); 4288e22978cSAlexander Shishkin if (IS_ERR(data->ci_pdev)) { 4298e22978cSAlexander Shishkin ret = PTR_ERR(data->ci_pdev); 430d3d8425aSStefan Agner if (ret != -EPROBE_DEFER) 4317c8e8909SPeter Chen dev_err(dev, "ci_hdrc_add_device failed, err=%d\n", 4327c8e8909SPeter Chen ret); 433af59a8b1SPeter Chen goto err_clk; 4348e22978cSAlexander Shishkin } 4358e22978cSAlexander Shishkin 436*93c2c733SLi Jun if (!IS_ERR(pdata.id_extcon.edev) || 437*93c2c733SLi Jun of_property_read_bool(np, "usb-role-switch")) 438*93c2c733SLi Jun data->usbmisc_data->ext_id = 1; 439*93c2c733SLi Jun 440*93c2c733SLi Jun if (!IS_ERR(pdata.vbus_extcon.edev) || 441*93c2c733SLi Jun of_property_read_bool(np, "usb-role-switch")) 442*93c2c733SLi Jun data->usbmisc_data->ext_vbus = 1; 443*93c2c733SLi Jun 44405986ba9SSascha Hauer ret = imx_usbmisc_init_post(data->usbmisc_data); 4458e22978cSAlexander Shishkin if (ret) { 4467c8e8909SPeter Chen dev_err(dev, "usbmisc post failed, ret=%d\n", ret); 4478e22978cSAlexander Shishkin goto disable_device; 4488e22978cSAlexander Shishkin } 4498e22978cSAlexander Shishkin 450e14db48dSPeter Chen if (data->supports_runtime_pm) { 4517c8e8909SPeter Chen pm_runtime_set_active(dev); 4527c8e8909SPeter Chen pm_runtime_enable(dev); 453e14db48dSPeter Chen } 4548e22978cSAlexander Shishkin 4557c8e8909SPeter Chen device_set_wakeup_capable(dev, true); 4566d653110SPeter Chen 4578e22978cSAlexander Shishkin return 0; 4588e22978cSAlexander Shishkin 4598e22978cSAlexander Shishkin disable_device: 4608e22978cSAlexander Shishkin ci_hdrc_remove_device(data->ci_pdev); 4618e22978cSAlexander Shishkin err_clk: 4627c8e8909SPeter Chen imx_disable_unprepare_clks(dev); 4637c8e8909SPeter Chen disable_hsic_regulator: 4647c8e8909SPeter Chen if (data->hsic_pad_regulator) 465141822aaSAndré Draszik /* don't overwrite original ret (cf. EPROBE_DEFER) */ 466141822aaSAndré Draszik regulator_disable(data->hsic_pad_regulator); 467d1609c31SPeter Chen if (pdata.flags & CI_HDRC_PMQOS) 468d1609c31SPeter Chen pm_qos_remove_request(&data->pm_qos_req); 469141822aaSAndré Draszik data->ci_pdev = NULL; 4708e22978cSAlexander Shishkin return ret; 4718e22978cSAlexander Shishkin } 4728e22978cSAlexander Shishkin 4738e22978cSAlexander Shishkin static int ci_hdrc_imx_remove(struct platform_device *pdev) 4748e22978cSAlexander Shishkin { 4758e22978cSAlexander Shishkin struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); 4768e22978cSAlexander Shishkin 477e14db48dSPeter Chen if (data->supports_runtime_pm) { 478e14db48dSPeter Chen pm_runtime_get_sync(&pdev->dev); 4798e22978cSAlexander Shishkin pm_runtime_disable(&pdev->dev); 480e14db48dSPeter Chen pm_runtime_put_noidle(&pdev->dev); 481e14db48dSPeter Chen } 482141822aaSAndré Draszik if (data->ci_pdev) 4838e22978cSAlexander Shishkin ci_hdrc_remove_device(data->ci_pdev); 484be9cae24SSebastian Reichel if (data->override_phy_control) 485be9cae24SSebastian Reichel usb_phy_shutdown(data->phy); 486141822aaSAndré Draszik if (data->ci_pdev) { 487ae3e57aeSPeter Chen imx_disable_unprepare_clks(&pdev->dev); 488d1609c31SPeter Chen if (data->plat_data->flags & CI_HDRC_PMQOS) 489d1609c31SPeter Chen pm_qos_remove_request(&data->pm_qos_req); 4907c8e8909SPeter Chen if (data->hsic_pad_regulator) 4917c8e8909SPeter Chen regulator_disable(data->hsic_pad_regulator); 492141822aaSAndré Draszik } 4938e22978cSAlexander Shishkin 4948e22978cSAlexander Shishkin return 0; 4958e22978cSAlexander Shishkin } 4968e22978cSAlexander Shishkin 497b09b5224SAndreas Fenkart static void ci_hdrc_imx_shutdown(struct platform_device *pdev) 498b09b5224SAndreas Fenkart { 499b09b5224SAndreas Fenkart ci_hdrc_imx_remove(pdev); 500b09b5224SAndreas Fenkart } 501b09b5224SAndreas Fenkart 5029f644a64SMarcus Folkesson static int __maybe_unused imx_controller_suspend(struct device *dev) 5032558c1f5SPeter Chen { 5042558c1f5SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 5057c8e8909SPeter Chen int ret = 0; 5062558c1f5SPeter Chen 5072558c1f5SPeter Chen dev_dbg(dev, "at %s\n", __func__); 5082558c1f5SPeter Chen 5097c8e8909SPeter Chen ret = imx_usbmisc_hsic_set_clk(data->usbmisc_data, false); 5107c8e8909SPeter Chen if (ret) { 5117c8e8909SPeter Chen dev_err(dev, "usbmisc hsic_set_clk failed, ret=%d\n", ret); 5127c8e8909SPeter Chen return ret; 5137c8e8909SPeter Chen } 5147c8e8909SPeter Chen 515ae3e57aeSPeter Chen imx_disable_unprepare_clks(dev); 516d1609c31SPeter Chen if (data->plat_data->flags & CI_HDRC_PMQOS) 517d1609c31SPeter Chen pm_qos_remove_request(&data->pm_qos_req); 518d1609c31SPeter Chen 519e14db48dSPeter Chen data->in_lpm = true; 5202558c1f5SPeter Chen 5212558c1f5SPeter Chen return 0; 5222558c1f5SPeter Chen } 5232558c1f5SPeter Chen 5249f644a64SMarcus Folkesson static int __maybe_unused imx_controller_resume(struct device *dev) 5252558c1f5SPeter Chen { 5262558c1f5SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 527e14db48dSPeter Chen int ret = 0; 5282558c1f5SPeter Chen 5292558c1f5SPeter Chen dev_dbg(dev, "at %s\n", __func__); 5302558c1f5SPeter Chen 531e14db48dSPeter Chen if (!data->in_lpm) { 532e14db48dSPeter Chen WARN_ON(1); 533e14db48dSPeter Chen return 0; 5342558c1f5SPeter Chen } 5352558c1f5SPeter Chen 536d1609c31SPeter Chen if (data->plat_data->flags & CI_HDRC_PMQOS) 537d1609c31SPeter Chen pm_qos_add_request(&data->pm_qos_req, 538d1609c31SPeter Chen PM_QOS_CPU_DMA_LATENCY, 0); 539d1609c31SPeter Chen 540ae3e57aeSPeter Chen ret = imx_prepare_enable_clks(dev); 541e14db48dSPeter Chen if (ret) 542e14db48dSPeter Chen return ret; 543e14db48dSPeter Chen 544e14db48dSPeter Chen data->in_lpm = false; 545e14db48dSPeter Chen 546e14db48dSPeter Chen ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false); 547e14db48dSPeter Chen if (ret) { 548e14db48dSPeter Chen dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); 549e14db48dSPeter Chen goto clk_disable; 550e14db48dSPeter Chen } 551e14db48dSPeter Chen 5527c8e8909SPeter Chen ret = imx_usbmisc_hsic_set_clk(data->usbmisc_data, true); 5537c8e8909SPeter Chen if (ret) { 5547c8e8909SPeter Chen dev_err(dev, "usbmisc hsic_set_clk failed, ret=%d\n", ret); 5557c8e8909SPeter Chen goto hsic_set_clk_fail; 5567c8e8909SPeter Chen } 5577c8e8909SPeter Chen 558e14db48dSPeter Chen return 0; 559e14db48dSPeter Chen 5607c8e8909SPeter Chen hsic_set_clk_fail: 5617c8e8909SPeter Chen imx_usbmisc_set_wakeup(data->usbmisc_data, true); 562e14db48dSPeter Chen clk_disable: 563ae3e57aeSPeter Chen imx_disable_unprepare_clks(dev); 564e14db48dSPeter Chen return ret; 565e14db48dSPeter Chen } 566e14db48dSPeter Chen 5679f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev) 5682558c1f5SPeter Chen { 5696d653110SPeter Chen int ret; 5706d653110SPeter Chen 571e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 572e14db48dSPeter Chen 573e14db48dSPeter Chen if (data->in_lpm) 574e14db48dSPeter Chen /* The core's suspend doesn't run */ 575e14db48dSPeter Chen return 0; 576e14db48dSPeter Chen 5776d653110SPeter Chen if (device_may_wakeup(dev)) { 5786d653110SPeter Chen ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); 5796d653110SPeter Chen if (ret) { 5806d653110SPeter Chen dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", 5816d653110SPeter Chen ret); 5826d653110SPeter Chen return ret; 5836d653110SPeter Chen } 5846d653110SPeter Chen } 5856d653110SPeter Chen 5862558c1f5SPeter Chen return imx_controller_suspend(dev); 5872558c1f5SPeter Chen } 5882558c1f5SPeter Chen 5899f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_resume(struct device *dev) 5902558c1f5SPeter Chen { 591e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 592e14db48dSPeter Chen int ret; 593e14db48dSPeter Chen 594e14db48dSPeter Chen ret = imx_controller_resume(dev); 595e14db48dSPeter Chen if (!ret && data->supports_runtime_pm) { 596e14db48dSPeter Chen pm_runtime_disable(dev); 597e14db48dSPeter Chen pm_runtime_set_active(dev); 598e14db48dSPeter Chen pm_runtime_enable(dev); 599e14db48dSPeter Chen } 600e14db48dSPeter Chen 601e14db48dSPeter Chen return ret; 6022558c1f5SPeter Chen } 6032558c1f5SPeter Chen 6049f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev) 605e14db48dSPeter Chen { 606e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 607e14db48dSPeter Chen int ret; 608e14db48dSPeter Chen 609e14db48dSPeter Chen if (data->in_lpm) { 610e14db48dSPeter Chen WARN_ON(1); 611e14db48dSPeter Chen return 0; 612e14db48dSPeter Chen } 613e14db48dSPeter Chen 614e14db48dSPeter Chen ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true); 615e14db48dSPeter Chen if (ret) { 616e14db48dSPeter Chen dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret); 617e14db48dSPeter Chen return ret; 618e14db48dSPeter Chen } 619e14db48dSPeter Chen 620e14db48dSPeter Chen return imx_controller_suspend(dev); 621e14db48dSPeter Chen } 622e14db48dSPeter Chen 6239f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_runtime_resume(struct device *dev) 624e14db48dSPeter Chen { 625e14db48dSPeter Chen return imx_controller_resume(dev); 626e14db48dSPeter Chen } 627e14db48dSPeter Chen 6282558c1f5SPeter Chen static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { 6292558c1f5SPeter Chen SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) 630e14db48dSPeter Chen SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, 631e14db48dSPeter Chen ci_hdrc_imx_runtime_resume, NULL) 6322558c1f5SPeter Chen }; 6338e22978cSAlexander Shishkin static struct platform_driver ci_hdrc_imx_driver = { 6348e22978cSAlexander Shishkin .probe = ci_hdrc_imx_probe, 6358e22978cSAlexander Shishkin .remove = ci_hdrc_imx_remove, 636b09b5224SAndreas Fenkart .shutdown = ci_hdrc_imx_shutdown, 6378e22978cSAlexander Shishkin .driver = { 6388e22978cSAlexander Shishkin .name = "imx_usb", 6398e22978cSAlexander Shishkin .of_match_table = ci_hdrc_imx_dt_ids, 6402558c1f5SPeter Chen .pm = &ci_hdrc_imx_pm_ops, 6418e22978cSAlexander Shishkin }, 6428e22978cSAlexander Shishkin }; 6438e22978cSAlexander Shishkin 6448e22978cSAlexander Shishkin module_platform_driver(ci_hdrc_imx_driver); 6458e22978cSAlexander Shishkin 6468e22978cSAlexander Shishkin MODULE_ALIAS("platform:imx-usb"); 6471f06072cSMarcus Folkesson MODULE_LICENSE("GPL"); 6488e22978cSAlexander Shishkin MODULE_DESCRIPTION("CI HDRC i.MX USB binding"); 6498e22978cSAlexander Shishkin MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 6508e22978cSAlexander Shishkin MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>"); 651