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> 9484468fbSRob Herring #include <linux/of.h> 108e22978cSAlexander Shishkin #include <linux/of_platform.h> 118e22978cSAlexander Shishkin #include <linux/platform_device.h> 128e22978cSAlexander Shishkin #include <linux/pm_runtime.h> 138e22978cSAlexander Shishkin #include <linux/usb/chipidea.h> 14d13631bbSFabien Lahoudere #include <linux/usb/of.h> 158e22978cSAlexander Shishkin #include <linux/clk.h> 167c8e8909SPeter Chen #include <linux/pinctrl/consumer.h> 17d1609c31SPeter Chen #include <linux/pm_qos.h> 188e22978cSAlexander Shishkin 198e22978cSAlexander Shishkin #include "ci.h" 208e22978cSAlexander Shishkin #include "ci_hdrc_imx.h" 218e22978cSAlexander Shishkin 221071055eSPeter Chen struct ci_hdrc_imx_platform_flag { 231071055eSPeter Chen unsigned int flags; 241071055eSPeter Chen }; 251071055eSPeter Chen 2681345722SStefan Wahren static const struct ci_hdrc_imx_platform_flag imx23_usb_data = { 2781345722SStefan Wahren .flags = CI_HDRC_TURN_VBUS_EARLY_ON | 2881345722SStefan Wahren CI_HDRC_DISABLE_STREAMING, 2981345722SStefan Wahren }; 3081345722SStefan Wahren 311071055eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { 3204e16379SSebastian Reichel .flags = CI_HDRC_DISABLE_STREAMING, 331071055eSPeter Chen }; 341071055eSPeter Chen 351071055eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { 366adb9b7bSLi Jun .flags = CI_HDRC_IMX28_WRITE_FIX | 370ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 380ef877a4SPeter Chen CI_HDRC_DISABLE_STREAMING, 391071055eSPeter Chen }; 401071055eSPeter Chen 41e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = { 426adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 430ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 440ef877a4SPeter Chen CI_HDRC_DISABLE_STREAMING, 45e14db48dSPeter Chen }; 46e14db48dSPeter Chen 47e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = { 486adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 490ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 500ef877a4SPeter Chen CI_HDRC_DISABLE_HOST_STREAMING, 51e14db48dSPeter Chen }; 52e14db48dSPeter Chen 53e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { 546adb9b7bSLi Jun .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 550ef877a4SPeter Chen CI_HDRC_TURN_VBUS_EARLY_ON | 560ef877a4SPeter Chen CI_HDRC_DISABLE_HOST_STREAMING, 57e14db48dSPeter Chen }; 58e14db48dSPeter Chen 5952fe568eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = { 6052fe568eSPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 61c7721e15SFabio Estevam CI_HDRC_TURN_VBUS_EARLY_ON | 62c7721e15SFabio Estevam CI_HDRC_DISABLE_DEVICE_STREAMING, 6352fe568eSPeter Chen }; 6452fe568eSPeter Chen 655cb377c5SPeter Chen static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = { 665cb377c5SPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, 675cb377c5SPeter Chen }; 685cb377c5SPeter Chen 69d1609c31SPeter Chen static const struct ci_hdrc_imx_platform_flag imx7ulp_usb_data = { 70d1609c31SPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 7112e6ac69SXu Yang CI_HDRC_HAS_PORTSC_PEC_MISSED | 72d1609c31SPeter Chen CI_HDRC_PMQOS, 73d1609c31SPeter Chen }; 74d1609c31SPeter Chen 759a070e8eSXu Yang static const struct ci_hdrc_imx_platform_flag imx8ulp_usb_data = { 7612e6ac69SXu Yang .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 7712e6ac69SXu Yang CI_HDRC_HAS_PORTSC_PEC_MISSED, 789a070e8eSXu Yang }; 799a070e8eSXu Yang 801071055eSPeter Chen static const struct of_device_id ci_hdrc_imx_dt_ids[] = { 8181345722SStefan Wahren { .compatible = "fsl,imx23-usb", .data = &imx23_usb_data}, 821071055eSPeter Chen { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, 831071055eSPeter Chen { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, 84e14db48dSPeter Chen { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, 85e14db48dSPeter Chen { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, 868315b77dSLi Jun { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data}, 8752fe568eSPeter Chen { .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data}, 885cb377c5SPeter Chen { .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data}, 89d1609c31SPeter Chen { .compatible = "fsl,imx7ulp-usb", .data = &imx7ulp_usb_data}, 909a070e8eSXu Yang { .compatible = "fsl,imx8ulp-usb", .data = &imx8ulp_usb_data}, 911071055eSPeter Chen { /* sentinel */ } 921071055eSPeter Chen }; 931071055eSPeter Chen MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); 941071055eSPeter Chen 958e22978cSAlexander Shishkin struct ci_hdrc_imx_data { 968e22978cSAlexander Shishkin struct usb_phy *phy; 978e22978cSAlexander Shishkin struct platform_device *ci_pdev; 988e22978cSAlexander Shishkin struct clk *clk; 995dbe9ac2SXu Yang struct clk *clk_wakeup; 10005986ba9SSascha Hauer struct imx_usbmisc_data *usbmisc_data; 101e14db48dSPeter Chen bool supports_runtime_pm; 102be9cae24SSebastian Reichel bool override_phy_control; 103e14db48dSPeter Chen bool in_lpm; 1047c8e8909SPeter Chen struct pinctrl *pinctrl; 1057c8e8909SPeter Chen struct pinctrl_state *pinctrl_hsic_active; 1067c8e8909SPeter Chen struct regulator *hsic_pad_regulator; 107ae3e57aeSPeter Chen /* SoC before i.mx6 (except imx23/imx28) needs three clks */ 108ae3e57aeSPeter Chen bool need_three_clks; 109ae3e57aeSPeter Chen struct clk *clk_ipg; 110ae3e57aeSPeter Chen struct clk *clk_ahb; 111ae3e57aeSPeter Chen struct clk *clk_per; 112ae3e57aeSPeter Chen /* --------------------------------- */ 113d1609c31SPeter Chen struct pm_qos_request pm_qos_req; 114d1609c31SPeter Chen const struct ci_hdrc_imx_platform_flag *plat_data; 1158e22978cSAlexander Shishkin }; 1168e22978cSAlexander Shishkin 1178e22978cSAlexander Shishkin /* Common functions shared by usbmisc drivers */ 1188e22978cSAlexander Shishkin 11905986ba9SSascha Hauer static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) 1208e22978cSAlexander Shishkin { 121f40017e0SStefan Agner struct platform_device *misc_pdev; 1228e22978cSAlexander Shishkin struct device_node *np = dev->of_node; 1238e22978cSAlexander Shishkin struct of_phandle_args args; 12405986ba9SSascha Hauer struct imx_usbmisc_data *data; 1258e22978cSAlexander Shishkin int ret; 1268e22978cSAlexander Shishkin 12705986ba9SSascha Hauer /* 12805986ba9SSascha Hauer * In case the fsl,usbmisc property is not present this device doesn't 12905986ba9SSascha Hauer * need usbmisc. Return NULL (which is no error here) 13005986ba9SSascha Hauer */ 13105986ba9SSascha Hauer if (!of_get_property(np, "fsl,usbmisc", NULL)) 13205986ba9SSascha Hauer return NULL; 13305986ba9SSascha Hauer 13405986ba9SSascha Hauer data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 13505986ba9SSascha Hauer if (!data) 13605986ba9SSascha Hauer return ERR_PTR(-ENOMEM); 1378e22978cSAlexander Shishkin 1388e22978cSAlexander Shishkin ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", 1398e22978cSAlexander Shishkin 0, &args); 1408e22978cSAlexander Shishkin if (ret) { 1418e22978cSAlexander Shishkin dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", 1428e22978cSAlexander Shishkin ret); 14305986ba9SSascha Hauer return ERR_PTR(ret); 1448e22978cSAlexander Shishkin } 14505986ba9SSascha Hauer 14605986ba9SSascha Hauer data->index = args.args[0]; 147f40017e0SStefan Agner 148f40017e0SStefan Agner misc_pdev = of_find_device_by_node(args.np); 1498e22978cSAlexander Shishkin of_node_put(args.np); 1508e22978cSAlexander Shishkin 15183a43ff8SYu Kuai if (!misc_pdev) 152f40017e0SStefan Agner return ERR_PTR(-EPROBE_DEFER); 153f40017e0SStefan Agner 15483a43ff8SYu Kuai if (!platform_get_drvdata(misc_pdev)) { 15583a43ff8SYu Kuai put_device(&misc_pdev->dev); 15683a43ff8SYu Kuai return ERR_PTR(-EPROBE_DEFER); 15783a43ff8SYu Kuai } 158f40017e0SStefan Agner data->dev = &misc_pdev->dev; 159f40017e0SStefan Agner 160a82bf696SUwe Kleine-König /* 161a82bf696SUwe Kleine-König * Check the various over current related properties. If over current 162a82bf696SUwe Kleine-König * detection is disabled we're not interested in the polarity. 163a82bf696SUwe Kleine-König */ 164f977caeaSRob Herring if (of_property_read_bool(np, "disable-over-current")) { 16505986ba9SSascha Hauer data->disable_oc = 1; 166f977caeaSRob Herring } else if (of_property_read_bool(np, "over-current-active-high")) { 167a82bf696SUwe Kleine-König data->oc_pol_active_low = 0; 168a82bf696SUwe Kleine-König data->oc_pol_configured = 1; 169f977caeaSRob Herring } else if (of_property_read_bool(np, "over-current-active-low")) { 170a82bf696SUwe Kleine-König data->oc_pol_active_low = 1; 171a82bf696SUwe Kleine-König data->oc_pol_configured = 1; 1721bf4743fSUwe Kleine-König } else { 1731bf4743fSUwe Kleine-König dev_warn(dev, "No over current polarity defined\n"); 174a82bf696SUwe Kleine-König } 1759dba516eSLi Jun 1765f0632c4SPhilipp Puschmann data->pwr_pol = of_property_read_bool(np, "power-active-high"); 1775f0632c4SPhilipp Puschmann data->evdo = of_property_read_bool(np, "external-vbus-divider"); 1788e22978cSAlexander Shishkin 179d13631bbSFabien Lahoudere if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) 180d13631bbSFabien Lahoudere data->ulpi = 1; 181d13631bbSFabien Lahoudere 18236668515SXu Yang if (of_property_read_u32(np, "samsung,picophy-pre-emp-curr-control", 18336668515SXu Yang &data->emp_curr_control)) 18436668515SXu Yang data->emp_curr_control = -1; 18536668515SXu Yang if (of_property_read_u32(np, "samsung,picophy-dc-vol-level-adjust", 18636668515SXu Yang &data->dc_vol_level_adjust)) 18736668515SXu Yang data->dc_vol_level_adjust = -1; 1883bd442e4SXu Yang if (of_property_read_u32(np, "fsl,picophy-rise-fall-time-adjust", 1893bd442e4SXu Yang &data->rise_fall_time_adjust)) 1903bd442e4SXu Yang data->rise_fall_time_adjust = -1; 19158a3cefbSPeter Chen 19205986ba9SSascha Hauer return data; 1938e22978cSAlexander Shishkin } 1948e22978cSAlexander Shishkin 1958e22978cSAlexander Shishkin /* End of common functions shared by usbmisc drivers*/ 196ae3e57aeSPeter Chen static int imx_get_clks(struct device *dev) 197ae3e57aeSPeter Chen { 198ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 199ae3e57aeSPeter Chen int ret = 0; 200ae3e57aeSPeter Chen 201ae3e57aeSPeter Chen data->clk_ipg = devm_clk_get(dev, "ipg"); 202ae3e57aeSPeter Chen if (IS_ERR(data->clk_ipg)) { 2035dbe9ac2SXu Yang /* If the platform only needs one primary clock */ 204ae3e57aeSPeter Chen data->clk = devm_clk_get(dev, NULL); 205ae3e57aeSPeter Chen if (IS_ERR(data->clk)) { 206ae3e57aeSPeter Chen ret = PTR_ERR(data->clk); 207ae3e57aeSPeter Chen dev_err(dev, 208ae3e57aeSPeter Chen "Failed to get clks, err=%ld,%ld\n", 209ae3e57aeSPeter Chen PTR_ERR(data->clk), PTR_ERR(data->clk_ipg)); 210ae3e57aeSPeter Chen return ret; 211ae3e57aeSPeter Chen } 2125dbe9ac2SXu Yang /* Get wakeup clock. Not all of the platforms need to 2135dbe9ac2SXu Yang * handle this clock. So make it optional. 2145dbe9ac2SXu Yang */ 21543590333SXu Yang data->clk_wakeup = devm_clk_get_optional(dev, "usb_wakeup"); 2165dbe9ac2SXu Yang if (IS_ERR(data->clk_wakeup)) 2175dbe9ac2SXu Yang ret = dev_err_probe(dev, PTR_ERR(data->clk_wakeup), 2185dbe9ac2SXu Yang "Failed to get wakeup clk\n"); 219ae3e57aeSPeter Chen return ret; 220ae3e57aeSPeter Chen } 221ae3e57aeSPeter Chen 222ae3e57aeSPeter Chen data->clk_ahb = devm_clk_get(dev, "ahb"); 223ae3e57aeSPeter Chen if (IS_ERR(data->clk_ahb)) { 224ae3e57aeSPeter Chen ret = PTR_ERR(data->clk_ahb); 225ae3e57aeSPeter Chen dev_err(dev, 226ae3e57aeSPeter Chen "Failed to get ahb clock, err=%d\n", ret); 227ae3e57aeSPeter Chen return ret; 228ae3e57aeSPeter Chen } 229ae3e57aeSPeter Chen 230ae3e57aeSPeter Chen data->clk_per = devm_clk_get(dev, "per"); 231ae3e57aeSPeter Chen if (IS_ERR(data->clk_per)) { 232ae3e57aeSPeter Chen ret = PTR_ERR(data->clk_per); 233ae3e57aeSPeter Chen dev_err(dev, 234ae3e57aeSPeter Chen "Failed to get per clock, err=%d\n", ret); 235ae3e57aeSPeter Chen return ret; 236ae3e57aeSPeter Chen } 237ae3e57aeSPeter Chen 238ae3e57aeSPeter Chen data->need_three_clks = true; 239ae3e57aeSPeter Chen return ret; 240ae3e57aeSPeter Chen } 241ae3e57aeSPeter Chen 242ae3e57aeSPeter Chen static int imx_prepare_enable_clks(struct device *dev) 243ae3e57aeSPeter Chen { 244ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 245ae3e57aeSPeter Chen int ret = 0; 246ae3e57aeSPeter Chen 247ae3e57aeSPeter Chen if (data->need_three_clks) { 248ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_ipg); 249ae3e57aeSPeter Chen if (ret) { 250ae3e57aeSPeter Chen dev_err(dev, 251ae3e57aeSPeter Chen "Failed to prepare/enable ipg clk, err=%d\n", 252ae3e57aeSPeter Chen ret); 253ae3e57aeSPeter Chen return ret; 254ae3e57aeSPeter Chen } 255ae3e57aeSPeter Chen 256ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_ahb); 257ae3e57aeSPeter Chen if (ret) { 258ae3e57aeSPeter Chen dev_err(dev, 259ae3e57aeSPeter Chen "Failed to prepare/enable ahb clk, err=%d\n", 260ae3e57aeSPeter Chen ret); 261ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 262ae3e57aeSPeter Chen return ret; 263ae3e57aeSPeter Chen } 264ae3e57aeSPeter Chen 265ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_per); 266ae3e57aeSPeter Chen if (ret) { 267ae3e57aeSPeter Chen dev_err(dev, 268ae3e57aeSPeter Chen "Failed to prepare/enable per clk, err=%d\n", 269ae3e57aeSPeter Chen ret); 270ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ahb); 271ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 272ae3e57aeSPeter Chen return ret; 273ae3e57aeSPeter Chen } 274ae3e57aeSPeter Chen } else { 275ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk); 276ae3e57aeSPeter Chen if (ret) { 277ae3e57aeSPeter Chen dev_err(dev, 278ae3e57aeSPeter Chen "Failed to prepare/enable clk, err=%d\n", 279ae3e57aeSPeter Chen ret); 280ae3e57aeSPeter Chen return ret; 281ae3e57aeSPeter Chen } 282ae3e57aeSPeter Chen } 283ae3e57aeSPeter Chen 284ae3e57aeSPeter Chen return ret; 285ae3e57aeSPeter Chen } 286ae3e57aeSPeter Chen 287ae3e57aeSPeter Chen static void imx_disable_unprepare_clks(struct device *dev) 288ae3e57aeSPeter Chen { 289ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 290ae3e57aeSPeter Chen 291ae3e57aeSPeter Chen if (data->need_three_clks) { 292ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_per); 293ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ahb); 294ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 295ae3e57aeSPeter Chen } else { 296ae3e57aeSPeter Chen clk_disable_unprepare(data->clk); 297ae3e57aeSPeter Chen } 298ae3e57aeSPeter Chen } 2998e22978cSAlexander Shishkin 3007c8e8909SPeter Chen static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event) 3017c8e8909SPeter Chen { 3027c8e8909SPeter Chen struct device *dev = ci->dev->parent; 3037c8e8909SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 3047c8e8909SPeter Chen int ret = 0; 305746f316bSJun Li struct imx_usbmisc_data *mdata = data->usbmisc_data; 3067c8e8909SPeter Chen 3077c8e8909SPeter Chen switch (event) { 3087c8e8909SPeter Chen case CI_HDRC_IMX_HSIC_ACTIVE_EVENT: 3094d614128SPeter Chen if (data->pinctrl) { 3107c8e8909SPeter Chen ret = pinctrl_select_state(data->pinctrl, 3117c8e8909SPeter Chen data->pinctrl_hsic_active); 3127c8e8909SPeter Chen if (ret) 3134d614128SPeter Chen dev_err(dev, 3144d614128SPeter Chen "hsic_active select failed, err=%d\n", 3157c8e8909SPeter Chen ret); 3164d614128SPeter Chen } 3177c8e8909SPeter Chen break; 3187c8e8909SPeter Chen case CI_HDRC_IMX_HSIC_SUSPEND_EVENT: 319746f316bSJun Li ret = imx_usbmisc_hsic_set_connect(mdata); 3207c8e8909SPeter Chen if (ret) 3217c8e8909SPeter Chen dev_err(dev, 3227c8e8909SPeter Chen "hsic_set_connect failed, err=%d\n", ret); 3237c8e8909SPeter Chen break; 324746f316bSJun Li case CI_HDRC_CONTROLLER_VBUS_EVENT: 325746f316bSJun Li if (ci->vbus_active) 326746f316bSJun Li ret = imx_usbmisc_charger_detection(mdata, true); 327746f316bSJun Li else 328746f316bSJun Li ret = imx_usbmisc_charger_detection(mdata, false); 329746f316bSJun Li if (ci->usb_phy) 330746f316bSJun Li schedule_work(&ci->usb_phy->chg_work); 331746f316bSJun Li break; 3327c8e8909SPeter Chen default: 3337c8e8909SPeter Chen break; 3347c8e8909SPeter Chen } 3357c8e8909SPeter Chen 3367c8e8909SPeter Chen return ret; 3377c8e8909SPeter Chen } 3387c8e8909SPeter Chen 3398e22978cSAlexander Shishkin static int ci_hdrc_imx_probe(struct platform_device *pdev) 3408e22978cSAlexander Shishkin { 3418e22978cSAlexander Shishkin struct ci_hdrc_imx_data *data; 3428e22978cSAlexander Shishkin struct ci_hdrc_platform_data pdata = { 343c844d6c8SAlexander Shiyan .name = dev_name(&pdev->dev), 3448e22978cSAlexander Shishkin .capoffset = DEF_CAPOFFSET, 3457c8e8909SPeter Chen .notify_event = ci_hdrc_imx_notify_event, 3468e22978cSAlexander Shishkin }; 3478e22978cSAlexander Shishkin int ret; 3486f51bc34SLABBE Corentin const struct ci_hdrc_imx_platform_flag *imx_platform_flag; 349be9cae24SSebastian Reichel struct device_node *np = pdev->dev.of_node; 3507c8e8909SPeter Chen struct device *dev = &pdev->dev; 3516f51bc34SLABBE Corentin 35259b7c6a8SFabio Estevam imx_platform_flag = of_device_get_match_data(&pdev->dev); 3538e22978cSAlexander Shishkin 3548e22978cSAlexander Shishkin data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 35573529828SFabio Estevam if (!data) 3568e22978cSAlexander Shishkin return -ENOMEM; 3578e22978cSAlexander Shishkin 358d1609c31SPeter Chen data->plat_data = imx_platform_flag; 359d1609c31SPeter Chen pdata.flags |= imx_platform_flag->flags; 360ae3e57aeSPeter Chen platform_set_drvdata(pdev, data); 3617c8e8909SPeter Chen data->usbmisc_data = usbmisc_get_init_data(dev); 36205986ba9SSascha Hauer if (IS_ERR(data->usbmisc_data)) 36305986ba9SSascha Hauer return PTR_ERR(data->usbmisc_data); 36405986ba9SSascha Hauer 3658ff396feSPeter Chen if ((of_usb_get_phy_mode(dev->of_node) == USBPHY_INTERFACE_MODE_HSIC) 3668ff396feSPeter Chen && data->usbmisc_data) { 3677c8e8909SPeter Chen pdata.flags |= CI_HDRC_IMX_IS_HSIC; 3687c8e8909SPeter Chen data->usbmisc_data->hsic = 1; 3697c8e8909SPeter Chen data->pinctrl = devm_pinctrl_get(dev); 3703f4aad6eSPeter Chen if (PTR_ERR(data->pinctrl) == -ENODEV) 3713f4aad6eSPeter Chen data->pinctrl = NULL; 37218171cfcSAlexander Stein else if (IS_ERR(data->pinctrl)) 37318171cfcSAlexander Stein return dev_err_probe(dev, PTR_ERR(data->pinctrl), 37418171cfcSAlexander Stein "pinctrl get failed\n"); 375ae3e57aeSPeter Chen 3764d614128SPeter Chen data->hsic_pad_regulator = 3774d614128SPeter Chen devm_regulator_get_optional(dev, "hsic"); 3784d614128SPeter Chen if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) { 3799c3959bbSJonathan Neuschäfer /* no pad regulator is needed */ 3804d614128SPeter Chen data->hsic_pad_regulator = NULL; 38118171cfcSAlexander Stein } else if (IS_ERR(data->hsic_pad_regulator)) 38218171cfcSAlexander Stein return dev_err_probe(dev, PTR_ERR(data->hsic_pad_regulator), 38318171cfcSAlexander Stein "Get HSIC pad regulator error\n"); 3844d614128SPeter Chen 3854d614128SPeter Chen if (data->hsic_pad_regulator) { 3864d614128SPeter Chen ret = regulator_enable(data->hsic_pad_regulator); 3874d614128SPeter Chen if (ret) { 3884d614128SPeter Chen dev_err(dev, 3894d614128SPeter Chen "Failed to enable HSIC pad regulator\n"); 3904d614128SPeter Chen return ret; 3914d614128SPeter Chen } 3924d614128SPeter Chen } 3934d614128SPeter Chen } 3944d614128SPeter Chen 3954d614128SPeter Chen /* HSIC pinctrl handling */ 3964d614128SPeter Chen if (data->pinctrl) { 3974d614128SPeter Chen struct pinctrl_state *pinctrl_hsic_idle; 3984d614128SPeter Chen 3997c8e8909SPeter Chen pinctrl_hsic_idle = pinctrl_lookup_state(data->pinctrl, "idle"); 4007c8e8909SPeter Chen if (IS_ERR(pinctrl_hsic_idle)) { 4017c8e8909SPeter Chen dev_err(dev, 4027c8e8909SPeter Chen "pinctrl_hsic_idle lookup failed, err=%ld\n", 4037c8e8909SPeter Chen PTR_ERR(pinctrl_hsic_idle)); 4047c8e8909SPeter Chen return PTR_ERR(pinctrl_hsic_idle); 4057c8e8909SPeter Chen } 4068e22978cSAlexander Shishkin 4077c8e8909SPeter Chen ret = pinctrl_select_state(data->pinctrl, pinctrl_hsic_idle); 4087c8e8909SPeter Chen if (ret) { 4097c8e8909SPeter Chen dev_err(dev, "hsic_idle select failed, err=%d\n", ret); 4107c8e8909SPeter Chen return ret; 4117c8e8909SPeter Chen } 4127c8e8909SPeter Chen 4137c8e8909SPeter Chen data->pinctrl_hsic_active = pinctrl_lookup_state(data->pinctrl, 4147c8e8909SPeter Chen "active"); 4157c8e8909SPeter Chen if (IS_ERR(data->pinctrl_hsic_active)) { 4167c8e8909SPeter Chen dev_err(dev, 4177c8e8909SPeter Chen "pinctrl_hsic_active lookup failed, err=%ld\n", 4187c8e8909SPeter Chen PTR_ERR(data->pinctrl_hsic_active)); 4197c8e8909SPeter Chen return PTR_ERR(data->pinctrl_hsic_active); 4207c8e8909SPeter Chen } 4217c8e8909SPeter Chen } 422d1609c31SPeter Chen 423d1609c31SPeter Chen if (pdata.flags & CI_HDRC_PMQOS) 42477b35245SRafael J. Wysocki cpu_latency_qos_add_request(&data->pm_qos_req, 0); 425d1609c31SPeter Chen 4267c8e8909SPeter Chen ret = imx_get_clks(dev); 4277c8e8909SPeter Chen if (ret) 4287c8e8909SPeter Chen goto disable_hsic_regulator; 4297c8e8909SPeter Chen 4307c8e8909SPeter Chen ret = imx_prepare_enable_clks(dev); 4317c8e8909SPeter Chen if (ret) 4327c8e8909SPeter Chen goto disable_hsic_regulator; 4337c8e8909SPeter Chen 4345dbe9ac2SXu Yang ret = clk_prepare_enable(data->clk_wakeup); 4355dbe9ac2SXu Yang if (ret) 4365dbe9ac2SXu Yang goto err_wakeup_clk; 4375dbe9ac2SXu Yang 4387c8e8909SPeter Chen data->phy = devm_usb_get_phy_by_phandle(dev, "fsl,usbphy", 0); 439af59a8b1SPeter Chen if (IS_ERR(data->phy)) { 440af59a8b1SPeter Chen ret = PTR_ERR(data->phy); 4413a1bd049SAlexander Stein if (ret != -ENODEV) { 4423a1bd049SAlexander Stein dev_err_probe(dev, ret, "Failed to parse fsl,usbphy\n"); 443d4d2e532SDan Carpenter goto err_clk; 4443a1bd049SAlexander Stein } 4458253a34bSFabio Estevam data->phy = devm_usb_get_phy_by_phandle(dev, "phys", 0); 4468253a34bSFabio Estevam if (IS_ERR(data->phy)) { 4478253a34bSFabio Estevam ret = PTR_ERR(data->phy); 4483a1bd049SAlexander Stein if (ret == -ENODEV) { 449ed5a419bSPeter Chen data->phy = NULL; 4503a1bd049SAlexander Stein } else { 4513a1bd049SAlexander Stein dev_err_probe(dev, ret, "Failed to parse phys\n"); 4528e22978cSAlexander Shishkin goto err_clk; 4538e22978cSAlexander Shishkin } 4548253a34bSFabio Estevam } 4553a1bd049SAlexander Stein } 4568e22978cSAlexander Shishkin 457ef44cb42SAntoine Tenart pdata.usb_phy = data->phy; 458746f316bSJun Li if (data->usbmisc_data) 459746f316bSJun Li data->usbmisc_data->usb_phy = data->phy; 460be9cae24SSebastian Reichel 46103e6275aSAndrey Smirnov if ((of_device_is_compatible(np, "fsl,imx53-usb") || 46203e6275aSAndrey Smirnov of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy && 463be9cae24SSebastian Reichel of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) { 464be9cae24SSebastian Reichel pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL; 465be9cae24SSebastian Reichel data->override_phy_control = true; 466be9cae24SSebastian Reichel usb_phy_init(pdata.usb_phy); 467be9cae24SSebastian Reichel } 468be9cae24SSebastian Reichel 469e14db48dSPeter Chen if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) 470e14db48dSPeter Chen data->supports_runtime_pm = true; 4711071055eSPeter Chen 47205986ba9SSascha Hauer ret = imx_usbmisc_init(data->usbmisc_data); 4738e22978cSAlexander Shishkin if (ret) { 4747c8e8909SPeter Chen dev_err(dev, "usbmisc init failed, ret=%d\n", ret); 475af59a8b1SPeter Chen goto err_clk; 4768e22978cSAlexander Shishkin } 4778e22978cSAlexander Shishkin 4787c8e8909SPeter Chen data->ci_pdev = ci_hdrc_add_device(dev, 4798e22978cSAlexander Shishkin pdev->resource, pdev->num_resources, 4808e22978cSAlexander Shishkin &pdata); 4818e22978cSAlexander Shishkin if (IS_ERR(data->ci_pdev)) { 4828e22978cSAlexander Shishkin ret = PTR_ERR(data->ci_pdev); 48318171cfcSAlexander Stein dev_err_probe(dev, ret, "ci_hdrc_add_device failed\n"); 484af59a8b1SPeter Chen goto err_clk; 4858e22978cSAlexander Shishkin } 4868e22978cSAlexander Shishkin 487df17aa9fSLi Jun if (data->usbmisc_data) { 48893c2c733SLi Jun if (!IS_ERR(pdata.id_extcon.edev) || 48993c2c733SLi Jun of_property_read_bool(np, "usb-role-switch")) 49093c2c733SLi Jun data->usbmisc_data->ext_id = 1; 49193c2c733SLi Jun 49293c2c733SLi Jun if (!IS_ERR(pdata.vbus_extcon.edev) || 49393c2c733SLi Jun of_property_read_bool(np, "usb-role-switch")) 49493c2c733SLi Jun data->usbmisc_data->ext_vbus = 1; 495d6f93d21SPeter Chen 496d6f93d21SPeter Chen /* usbmisc needs to know dr mode to choose wakeup setting */ 497d6f93d21SPeter Chen data->usbmisc_data->available_role = 498d6f93d21SPeter Chen ci_hdrc_query_available_role(data->ci_pdev); 499df17aa9fSLi Jun } 50093c2c733SLi Jun 50105986ba9SSascha Hauer ret = imx_usbmisc_init_post(data->usbmisc_data); 5028e22978cSAlexander Shishkin if (ret) { 5037c8e8909SPeter Chen dev_err(dev, "usbmisc post failed, ret=%d\n", ret); 5048e22978cSAlexander Shishkin goto disable_device; 5058e22978cSAlexander Shishkin } 5068e22978cSAlexander Shishkin 507e14db48dSPeter Chen if (data->supports_runtime_pm) { 5087c8e8909SPeter Chen pm_runtime_set_active(dev); 5097c8e8909SPeter Chen pm_runtime_enable(dev); 510e14db48dSPeter Chen } 5118e22978cSAlexander Shishkin 5127c8e8909SPeter Chen device_set_wakeup_capable(dev, true); 5136d653110SPeter Chen 5148e22978cSAlexander Shishkin return 0; 5158e22978cSAlexander Shishkin 5168e22978cSAlexander Shishkin disable_device: 5178e22978cSAlexander Shishkin ci_hdrc_remove_device(data->ci_pdev); 5188e22978cSAlexander Shishkin err_clk: 5195dbe9ac2SXu Yang clk_disable_unprepare(data->clk_wakeup); 5205dbe9ac2SXu Yang err_wakeup_clk: 5217c8e8909SPeter Chen imx_disable_unprepare_clks(dev); 5227c8e8909SPeter Chen disable_hsic_regulator: 5237c8e8909SPeter Chen if (data->hsic_pad_regulator) 524141822aaSAndré Draszik /* don't overwrite original ret (cf. EPROBE_DEFER) */ 525141822aaSAndré Draszik regulator_disable(data->hsic_pad_regulator); 526d1609c31SPeter Chen if (pdata.flags & CI_HDRC_PMQOS) 52777b35245SRafael J. Wysocki cpu_latency_qos_remove_request(&data->pm_qos_req); 528141822aaSAndré Draszik data->ci_pdev = NULL; 5298e22978cSAlexander Shishkin return ret; 5308e22978cSAlexander Shishkin } 5318e22978cSAlexander Shishkin 532ad593ed6SUwe Kleine-König static void ci_hdrc_imx_remove(struct platform_device *pdev) 5338e22978cSAlexander Shishkin { 5348e22978cSAlexander Shishkin struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); 5358e22978cSAlexander Shishkin 536e14db48dSPeter Chen if (data->supports_runtime_pm) { 537e14db48dSPeter Chen pm_runtime_get_sync(&pdev->dev); 5388e22978cSAlexander Shishkin pm_runtime_disable(&pdev->dev); 539e14db48dSPeter Chen pm_runtime_put_noidle(&pdev->dev); 540e14db48dSPeter Chen } 541141822aaSAndré Draszik if (data->ci_pdev) 5428e22978cSAlexander Shishkin ci_hdrc_remove_device(data->ci_pdev); 543be9cae24SSebastian Reichel if (data->override_phy_control) 544be9cae24SSebastian Reichel usb_phy_shutdown(data->phy); 545141822aaSAndré Draszik if (data->ci_pdev) { 546ae3e57aeSPeter Chen imx_disable_unprepare_clks(&pdev->dev); 5475dbe9ac2SXu Yang clk_disable_unprepare(data->clk_wakeup); 548d1609c31SPeter Chen if (data->plat_data->flags & CI_HDRC_PMQOS) 54977b35245SRafael J. Wysocki cpu_latency_qos_remove_request(&data->pm_qos_req); 5507c8e8909SPeter Chen if (data->hsic_pad_regulator) 5517c8e8909SPeter Chen regulator_disable(data->hsic_pad_regulator); 552141822aaSAndré Draszik } 5538e22978cSAlexander Shishkin } 5548e22978cSAlexander Shishkin 555b09b5224SAndreas Fenkart static void ci_hdrc_imx_shutdown(struct platform_device *pdev) 556b09b5224SAndreas Fenkart { 557b09b5224SAndreas Fenkart ci_hdrc_imx_remove(pdev); 558b09b5224SAndreas Fenkart } 559b09b5224SAndreas Fenkart 560*26faae34SFabio Estevam static int imx_controller_suspend(struct device *dev, 561b332d6d5SLi Jun pm_message_t msg) 5622558c1f5SPeter Chen { 5632558c1f5SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 5647c8e8909SPeter Chen int ret = 0; 5652558c1f5SPeter Chen 5662558c1f5SPeter Chen dev_dbg(dev, "at %s\n", __func__); 5672558c1f5SPeter Chen 568b332d6d5SLi Jun ret = imx_usbmisc_suspend(data->usbmisc_data, 569b332d6d5SLi Jun PMSG_IS_AUTO(msg) || device_may_wakeup(dev)); 5707c8e8909SPeter Chen if (ret) { 571b332d6d5SLi Jun dev_err(dev, 572b332d6d5SLi Jun "usbmisc suspend failed, ret=%d\n", ret); 5737c8e8909SPeter Chen return ret; 5747c8e8909SPeter Chen } 5757c8e8909SPeter Chen 576ae3e57aeSPeter Chen imx_disable_unprepare_clks(dev); 577d1609c31SPeter Chen if (data->plat_data->flags & CI_HDRC_PMQOS) 57877b35245SRafael J. Wysocki cpu_latency_qos_remove_request(&data->pm_qos_req); 579d1609c31SPeter Chen 580e14db48dSPeter Chen data->in_lpm = true; 5812558c1f5SPeter Chen 5822558c1f5SPeter Chen return 0; 5832558c1f5SPeter Chen } 5842558c1f5SPeter Chen 585*26faae34SFabio Estevam static int imx_controller_resume(struct device *dev, 586b332d6d5SLi Jun pm_message_t msg) 5872558c1f5SPeter Chen { 5882558c1f5SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 589e14db48dSPeter Chen int ret = 0; 5902558c1f5SPeter Chen 5912558c1f5SPeter Chen dev_dbg(dev, "at %s\n", __func__); 5922558c1f5SPeter Chen 593e14db48dSPeter Chen if (!data->in_lpm) { 594e14db48dSPeter Chen WARN_ON(1); 595e14db48dSPeter Chen return 0; 5962558c1f5SPeter Chen } 5972558c1f5SPeter Chen 598d1609c31SPeter Chen if (data->plat_data->flags & CI_HDRC_PMQOS) 59977b35245SRafael J. Wysocki cpu_latency_qos_add_request(&data->pm_qos_req, 0); 600d1609c31SPeter Chen 601ae3e57aeSPeter Chen ret = imx_prepare_enable_clks(dev); 602e14db48dSPeter Chen if (ret) 603e14db48dSPeter Chen return ret; 604e14db48dSPeter Chen 605e14db48dSPeter Chen data->in_lpm = false; 606e14db48dSPeter Chen 607b332d6d5SLi Jun ret = imx_usbmisc_resume(data->usbmisc_data, 608b332d6d5SLi Jun PMSG_IS_AUTO(msg) || device_may_wakeup(dev)); 609e14db48dSPeter Chen if (ret) { 610b332d6d5SLi Jun dev_err(dev, "usbmisc resume failed, ret=%d\n", ret); 611e14db48dSPeter Chen goto clk_disable; 612e14db48dSPeter Chen } 613e14db48dSPeter Chen 614e14db48dSPeter Chen return 0; 615e14db48dSPeter Chen 616e14db48dSPeter Chen clk_disable: 617ae3e57aeSPeter Chen imx_disable_unprepare_clks(dev); 618e14db48dSPeter Chen return ret; 619e14db48dSPeter Chen } 620e14db48dSPeter Chen 621*26faae34SFabio Estevam static int ci_hdrc_imx_suspend(struct device *dev) 6222558c1f5SPeter Chen { 6236d653110SPeter Chen int ret; 6246d653110SPeter Chen 625e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 626e14db48dSPeter Chen 627e14db48dSPeter Chen if (data->in_lpm) 628e14db48dSPeter Chen /* The core's suspend doesn't run */ 629e14db48dSPeter Chen return 0; 630e14db48dSPeter Chen 631b332d6d5SLi Jun ret = imx_controller_suspend(dev, PMSG_SUSPEND); 63271ac680eSPeter Chen if (ret) 63371ac680eSPeter Chen return ret; 63471ac680eSPeter Chen 63571ac680eSPeter Chen pinctrl_pm_select_sleep_state(dev); 63671ac680eSPeter Chen return ret; 6372558c1f5SPeter Chen } 6382558c1f5SPeter Chen 639*26faae34SFabio Estevam static int ci_hdrc_imx_resume(struct device *dev) 6402558c1f5SPeter Chen { 641e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 642e14db48dSPeter Chen int ret; 643e14db48dSPeter Chen 64471ac680eSPeter Chen pinctrl_pm_select_default_state(dev); 645b332d6d5SLi Jun ret = imx_controller_resume(dev, PMSG_RESUME); 646e14db48dSPeter Chen if (!ret && data->supports_runtime_pm) { 647e14db48dSPeter Chen pm_runtime_disable(dev); 648e14db48dSPeter Chen pm_runtime_set_active(dev); 649e14db48dSPeter Chen pm_runtime_enable(dev); 650e14db48dSPeter Chen } 651e14db48dSPeter Chen 652e14db48dSPeter Chen return ret; 6532558c1f5SPeter Chen } 6542558c1f5SPeter Chen 655*26faae34SFabio Estevam static int ci_hdrc_imx_runtime_suspend(struct device *dev) 656e14db48dSPeter Chen { 657e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 658e14db48dSPeter Chen 659e14db48dSPeter Chen if (data->in_lpm) { 660e14db48dSPeter Chen WARN_ON(1); 661e14db48dSPeter Chen return 0; 662e14db48dSPeter Chen } 663e14db48dSPeter Chen 664b332d6d5SLi Jun return imx_controller_suspend(dev, PMSG_AUTO_SUSPEND); 665e14db48dSPeter Chen } 666e14db48dSPeter Chen 667*26faae34SFabio Estevam static int ci_hdrc_imx_runtime_resume(struct device *dev) 668e14db48dSPeter Chen { 669b332d6d5SLi Jun return imx_controller_resume(dev, PMSG_AUTO_RESUME); 670e14db48dSPeter Chen } 671e14db48dSPeter Chen 6722558c1f5SPeter Chen static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { 673*26faae34SFabio Estevam SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) 674*26faae34SFabio Estevam RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, ci_hdrc_imx_runtime_resume, NULL) 6752558c1f5SPeter Chen }; 6768e22978cSAlexander Shishkin static struct platform_driver ci_hdrc_imx_driver = { 6778e22978cSAlexander Shishkin .probe = ci_hdrc_imx_probe, 678ad593ed6SUwe Kleine-König .remove_new = ci_hdrc_imx_remove, 679b09b5224SAndreas Fenkart .shutdown = ci_hdrc_imx_shutdown, 6808e22978cSAlexander Shishkin .driver = { 6818e22978cSAlexander Shishkin .name = "imx_usb", 6828e22978cSAlexander Shishkin .of_match_table = ci_hdrc_imx_dt_ids, 683*26faae34SFabio Estevam .pm = pm_ptr(&ci_hdrc_imx_pm_ops), 6848e22978cSAlexander Shishkin }, 6858e22978cSAlexander Shishkin }; 6868e22978cSAlexander Shishkin 6878e22978cSAlexander Shishkin module_platform_driver(ci_hdrc_imx_driver); 6888e22978cSAlexander Shishkin 6898e22978cSAlexander Shishkin MODULE_ALIAS("platform:imx-usb"); 6901f06072cSMarcus Folkesson MODULE_LICENSE("GPL"); 6918e22978cSAlexander Shishkin MODULE_DESCRIPTION("CI HDRC i.MX USB binding"); 6928e22978cSAlexander Shishkin MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 6938e22978cSAlexander Shishkin MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>"); 694