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 | 60c7721e15SFabio Estevam CI_HDRC_TURN_VBUS_EARLY_ON | 61c7721e15SFabio Estevam CI_HDRC_DISABLE_DEVICE_STREAMING, 6252fe568eSPeter Chen }; 6352fe568eSPeter Chen 645cb377c5SPeter Chen static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = { 655cb377c5SPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, 665cb377c5SPeter Chen }; 675cb377c5SPeter Chen 68d1609c31SPeter Chen static const struct ci_hdrc_imx_platform_flag imx7ulp_usb_data = { 69d1609c31SPeter Chen .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | 70d1609c31SPeter Chen CI_HDRC_PMQOS, 71d1609c31SPeter Chen }; 72d1609c31SPeter Chen 731071055eSPeter Chen static const struct of_device_id ci_hdrc_imx_dt_ids[] = { 7481345722SStefan Wahren { .compatible = "fsl,imx23-usb", .data = &imx23_usb_data}, 751071055eSPeter Chen { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, 761071055eSPeter Chen { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, 77e14db48dSPeter Chen { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, 78e14db48dSPeter Chen { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, 798315b77dSLi Jun { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data}, 8052fe568eSPeter Chen { .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data}, 815cb377c5SPeter Chen { .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data}, 82d1609c31SPeter Chen { .compatible = "fsl,imx7ulp-usb", .data = &imx7ulp_usb_data}, 831071055eSPeter Chen { /* sentinel */ } 841071055eSPeter Chen }; 851071055eSPeter Chen MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); 861071055eSPeter Chen 878e22978cSAlexander Shishkin struct ci_hdrc_imx_data { 888e22978cSAlexander Shishkin struct usb_phy *phy; 898e22978cSAlexander Shishkin struct platform_device *ci_pdev; 908e22978cSAlexander Shishkin struct clk *clk; 9105986ba9SSascha Hauer struct imx_usbmisc_data *usbmisc_data; 92e14db48dSPeter Chen bool supports_runtime_pm; 93be9cae24SSebastian Reichel bool override_phy_control; 94e14db48dSPeter Chen bool in_lpm; 957c8e8909SPeter Chen struct pinctrl *pinctrl; 967c8e8909SPeter Chen struct pinctrl_state *pinctrl_hsic_active; 977c8e8909SPeter Chen struct regulator *hsic_pad_regulator; 98ae3e57aeSPeter Chen /* SoC before i.mx6 (except imx23/imx28) needs three clks */ 99ae3e57aeSPeter Chen bool need_three_clks; 100ae3e57aeSPeter Chen struct clk *clk_ipg; 101ae3e57aeSPeter Chen struct clk *clk_ahb; 102ae3e57aeSPeter Chen struct clk *clk_per; 103ae3e57aeSPeter Chen /* --------------------------------- */ 104d1609c31SPeter Chen struct pm_qos_request pm_qos_req; 105d1609c31SPeter Chen const struct ci_hdrc_imx_platform_flag *plat_data; 1068e22978cSAlexander Shishkin }; 1078e22978cSAlexander Shishkin 1088e22978cSAlexander Shishkin /* Common functions shared by usbmisc drivers */ 1098e22978cSAlexander Shishkin 11005986ba9SSascha Hauer static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev) 1118e22978cSAlexander Shishkin { 112f40017e0SStefan Agner struct platform_device *misc_pdev; 1138e22978cSAlexander Shishkin struct device_node *np = dev->of_node; 1148e22978cSAlexander Shishkin struct of_phandle_args args; 11505986ba9SSascha Hauer struct imx_usbmisc_data *data; 1168e22978cSAlexander Shishkin int ret; 1178e22978cSAlexander Shishkin 11805986ba9SSascha Hauer /* 11905986ba9SSascha Hauer * In case the fsl,usbmisc property is not present this device doesn't 12005986ba9SSascha Hauer * need usbmisc. Return NULL (which is no error here) 12105986ba9SSascha Hauer */ 12205986ba9SSascha Hauer if (!of_get_property(np, "fsl,usbmisc", NULL)) 12305986ba9SSascha Hauer return NULL; 12405986ba9SSascha Hauer 12505986ba9SSascha Hauer data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 12605986ba9SSascha Hauer if (!data) 12705986ba9SSascha Hauer return ERR_PTR(-ENOMEM); 1288e22978cSAlexander Shishkin 1298e22978cSAlexander Shishkin ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells", 1308e22978cSAlexander Shishkin 0, &args); 1318e22978cSAlexander Shishkin if (ret) { 1328e22978cSAlexander Shishkin dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n", 1338e22978cSAlexander Shishkin ret); 13405986ba9SSascha Hauer return ERR_PTR(ret); 1358e22978cSAlexander Shishkin } 13605986ba9SSascha Hauer 13705986ba9SSascha Hauer data->index = args.args[0]; 138f40017e0SStefan Agner 139f40017e0SStefan Agner misc_pdev = of_find_device_by_node(args.np); 1408e22978cSAlexander Shishkin of_node_put(args.np); 1418e22978cSAlexander Shishkin 14283a43ff8SYu Kuai if (!misc_pdev) 143f40017e0SStefan Agner return ERR_PTR(-EPROBE_DEFER); 144f40017e0SStefan Agner 14583a43ff8SYu Kuai if (!platform_get_drvdata(misc_pdev)) { 14683a43ff8SYu Kuai put_device(&misc_pdev->dev); 14783a43ff8SYu Kuai return ERR_PTR(-EPROBE_DEFER); 14883a43ff8SYu Kuai } 149f40017e0SStefan Agner data->dev = &misc_pdev->dev; 150f40017e0SStefan Agner 151a82bf696SUwe Kleine-König /* 152a82bf696SUwe Kleine-König * Check the various over current related properties. If over current 153a82bf696SUwe Kleine-König * detection is disabled we're not interested in the polarity. 154a82bf696SUwe Kleine-König */ 155a82bf696SUwe Kleine-König if (of_find_property(np, "disable-over-current", NULL)) { 15605986ba9SSascha Hauer data->disable_oc = 1; 157a82bf696SUwe Kleine-König } else if (of_find_property(np, "over-current-active-high", NULL)) { 158a82bf696SUwe Kleine-König data->oc_pol_active_low = 0; 159a82bf696SUwe Kleine-König data->oc_pol_configured = 1; 160a82bf696SUwe Kleine-König } else if (of_find_property(np, "over-current-active-low", NULL)) { 161a82bf696SUwe Kleine-König data->oc_pol_active_low = 1; 162a82bf696SUwe Kleine-König data->oc_pol_configured = 1; 1631bf4743fSUwe Kleine-König } else { 1641bf4743fSUwe Kleine-König dev_warn(dev, "No over current polarity defined\n"); 165a82bf696SUwe Kleine-König } 1669dba516eSLi Jun 1675f0632c4SPhilipp Puschmann data->pwr_pol = of_property_read_bool(np, "power-active-high"); 1685f0632c4SPhilipp Puschmann data->evdo = of_property_read_bool(np, "external-vbus-divider"); 1698e22978cSAlexander Shishkin 170d13631bbSFabien Lahoudere if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) 171d13631bbSFabien Lahoudere data->ulpi = 1; 172d13631bbSFabien Lahoudere 17358a3cefbSPeter Chen of_property_read_u32(np, "samsung,picophy-pre-emp-curr-control", 17458a3cefbSPeter Chen &data->emp_curr_control); 17558a3cefbSPeter Chen of_property_read_u32(np, "samsung,picophy-dc-vol-level-adjust", 17658a3cefbSPeter Chen &data->dc_vol_level_adjust); 17758a3cefbSPeter Chen 17805986ba9SSascha Hauer return data; 1798e22978cSAlexander Shishkin } 1808e22978cSAlexander Shishkin 1818e22978cSAlexander Shishkin /* End of common functions shared by usbmisc drivers*/ 182ae3e57aeSPeter Chen static int imx_get_clks(struct device *dev) 183ae3e57aeSPeter Chen { 184ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 185ae3e57aeSPeter Chen int ret = 0; 186ae3e57aeSPeter Chen 187ae3e57aeSPeter Chen data->clk_ipg = devm_clk_get(dev, "ipg"); 188ae3e57aeSPeter Chen if (IS_ERR(data->clk_ipg)) { 189ae3e57aeSPeter Chen /* If the platform only needs one clocks */ 190ae3e57aeSPeter Chen data->clk = devm_clk_get(dev, NULL); 191ae3e57aeSPeter Chen if (IS_ERR(data->clk)) { 192ae3e57aeSPeter Chen ret = PTR_ERR(data->clk); 193ae3e57aeSPeter Chen dev_err(dev, 194ae3e57aeSPeter Chen "Failed to get clks, err=%ld,%ld\n", 195ae3e57aeSPeter Chen PTR_ERR(data->clk), PTR_ERR(data->clk_ipg)); 196ae3e57aeSPeter Chen return ret; 197ae3e57aeSPeter Chen } 198ae3e57aeSPeter Chen return ret; 199ae3e57aeSPeter Chen } 200ae3e57aeSPeter Chen 201ae3e57aeSPeter Chen data->clk_ahb = devm_clk_get(dev, "ahb"); 202ae3e57aeSPeter Chen if (IS_ERR(data->clk_ahb)) { 203ae3e57aeSPeter Chen ret = PTR_ERR(data->clk_ahb); 204ae3e57aeSPeter Chen dev_err(dev, 205ae3e57aeSPeter Chen "Failed to get ahb clock, err=%d\n", ret); 206ae3e57aeSPeter Chen return ret; 207ae3e57aeSPeter Chen } 208ae3e57aeSPeter Chen 209ae3e57aeSPeter Chen data->clk_per = devm_clk_get(dev, "per"); 210ae3e57aeSPeter Chen if (IS_ERR(data->clk_per)) { 211ae3e57aeSPeter Chen ret = PTR_ERR(data->clk_per); 212ae3e57aeSPeter Chen dev_err(dev, 213ae3e57aeSPeter Chen "Failed to get per clock, err=%d\n", ret); 214ae3e57aeSPeter Chen return ret; 215ae3e57aeSPeter Chen } 216ae3e57aeSPeter Chen 217ae3e57aeSPeter Chen data->need_three_clks = true; 218ae3e57aeSPeter Chen return ret; 219ae3e57aeSPeter Chen } 220ae3e57aeSPeter Chen 221ae3e57aeSPeter Chen static int imx_prepare_enable_clks(struct device *dev) 222ae3e57aeSPeter Chen { 223ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 224ae3e57aeSPeter Chen int ret = 0; 225ae3e57aeSPeter Chen 226ae3e57aeSPeter Chen if (data->need_three_clks) { 227ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_ipg); 228ae3e57aeSPeter Chen if (ret) { 229ae3e57aeSPeter Chen dev_err(dev, 230ae3e57aeSPeter Chen "Failed to prepare/enable ipg clk, err=%d\n", 231ae3e57aeSPeter Chen ret); 232ae3e57aeSPeter Chen return ret; 233ae3e57aeSPeter Chen } 234ae3e57aeSPeter Chen 235ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_ahb); 236ae3e57aeSPeter Chen if (ret) { 237ae3e57aeSPeter Chen dev_err(dev, 238ae3e57aeSPeter Chen "Failed to prepare/enable ahb clk, err=%d\n", 239ae3e57aeSPeter Chen ret); 240ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 241ae3e57aeSPeter Chen return ret; 242ae3e57aeSPeter Chen } 243ae3e57aeSPeter Chen 244ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk_per); 245ae3e57aeSPeter Chen if (ret) { 246ae3e57aeSPeter Chen dev_err(dev, 247ae3e57aeSPeter Chen "Failed to prepare/enable per clk, err=%d\n", 248ae3e57aeSPeter Chen ret); 249ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ahb); 250ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 251ae3e57aeSPeter Chen return ret; 252ae3e57aeSPeter Chen } 253ae3e57aeSPeter Chen } else { 254ae3e57aeSPeter Chen ret = clk_prepare_enable(data->clk); 255ae3e57aeSPeter Chen if (ret) { 256ae3e57aeSPeter Chen dev_err(dev, 257ae3e57aeSPeter Chen "Failed to prepare/enable clk, err=%d\n", 258ae3e57aeSPeter Chen ret); 259ae3e57aeSPeter Chen return ret; 260ae3e57aeSPeter Chen } 261ae3e57aeSPeter Chen } 262ae3e57aeSPeter Chen 263ae3e57aeSPeter Chen return ret; 264ae3e57aeSPeter Chen } 265ae3e57aeSPeter Chen 266ae3e57aeSPeter Chen static void imx_disable_unprepare_clks(struct device *dev) 267ae3e57aeSPeter Chen { 268ae3e57aeSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 269ae3e57aeSPeter Chen 270ae3e57aeSPeter Chen if (data->need_three_clks) { 271ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_per); 272ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ahb); 273ae3e57aeSPeter Chen clk_disable_unprepare(data->clk_ipg); 274ae3e57aeSPeter Chen } else { 275ae3e57aeSPeter Chen clk_disable_unprepare(data->clk); 276ae3e57aeSPeter Chen } 277ae3e57aeSPeter Chen } 2788e22978cSAlexander Shishkin 2797c8e8909SPeter Chen static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event) 2807c8e8909SPeter Chen { 2817c8e8909SPeter Chen struct device *dev = ci->dev->parent; 2827c8e8909SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 2837c8e8909SPeter Chen int ret = 0; 284746f316bSJun Li struct imx_usbmisc_data *mdata = data->usbmisc_data; 2857c8e8909SPeter Chen 2867c8e8909SPeter Chen switch (event) { 2877c8e8909SPeter Chen case CI_HDRC_IMX_HSIC_ACTIVE_EVENT: 2884d614128SPeter Chen if (data->pinctrl) { 2897c8e8909SPeter Chen ret = pinctrl_select_state(data->pinctrl, 2907c8e8909SPeter Chen data->pinctrl_hsic_active); 2917c8e8909SPeter Chen if (ret) 2924d614128SPeter Chen dev_err(dev, 2934d614128SPeter Chen "hsic_active select failed, err=%d\n", 2947c8e8909SPeter Chen ret); 2954d614128SPeter Chen } 2967c8e8909SPeter Chen break; 2977c8e8909SPeter Chen case CI_HDRC_IMX_HSIC_SUSPEND_EVENT: 298746f316bSJun Li ret = imx_usbmisc_hsic_set_connect(mdata); 2997c8e8909SPeter Chen if (ret) 3007c8e8909SPeter Chen dev_err(dev, 3017c8e8909SPeter Chen "hsic_set_connect failed, err=%d\n", ret); 3027c8e8909SPeter Chen break; 303746f316bSJun Li case CI_HDRC_CONTROLLER_VBUS_EVENT: 304746f316bSJun Li if (ci->vbus_active) 305746f316bSJun Li ret = imx_usbmisc_charger_detection(mdata, true); 306746f316bSJun Li else 307746f316bSJun Li ret = imx_usbmisc_charger_detection(mdata, false); 308746f316bSJun Li if (ci->usb_phy) 309746f316bSJun Li schedule_work(&ci->usb_phy->chg_work); 310746f316bSJun Li break; 3117c8e8909SPeter Chen default: 3127c8e8909SPeter Chen break; 3137c8e8909SPeter Chen } 3147c8e8909SPeter Chen 3157c8e8909SPeter Chen return ret; 3167c8e8909SPeter Chen } 3177c8e8909SPeter Chen 3188e22978cSAlexander Shishkin static int ci_hdrc_imx_probe(struct platform_device *pdev) 3198e22978cSAlexander Shishkin { 3208e22978cSAlexander Shishkin struct ci_hdrc_imx_data *data; 3218e22978cSAlexander Shishkin struct ci_hdrc_platform_data pdata = { 322c844d6c8SAlexander Shiyan .name = dev_name(&pdev->dev), 3238e22978cSAlexander Shishkin .capoffset = DEF_CAPOFFSET, 3247c8e8909SPeter Chen .notify_event = ci_hdrc_imx_notify_event, 3258e22978cSAlexander Shishkin }; 3268e22978cSAlexander Shishkin int ret; 3276f51bc34SLABBE Corentin const struct ci_hdrc_imx_platform_flag *imx_platform_flag; 328be9cae24SSebastian Reichel struct device_node *np = pdev->dev.of_node; 3297c8e8909SPeter Chen struct device *dev = &pdev->dev; 3306f51bc34SLABBE Corentin 33159b7c6a8SFabio Estevam imx_platform_flag = of_device_get_match_data(&pdev->dev); 3328e22978cSAlexander Shishkin 3338e22978cSAlexander Shishkin data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 33473529828SFabio Estevam if (!data) 3358e22978cSAlexander Shishkin return -ENOMEM; 3368e22978cSAlexander Shishkin 337d1609c31SPeter Chen data->plat_data = imx_platform_flag; 338d1609c31SPeter Chen pdata.flags |= imx_platform_flag->flags; 339ae3e57aeSPeter Chen platform_set_drvdata(pdev, data); 3407c8e8909SPeter Chen data->usbmisc_data = usbmisc_get_init_data(dev); 34105986ba9SSascha Hauer if (IS_ERR(data->usbmisc_data)) 34205986ba9SSascha Hauer return PTR_ERR(data->usbmisc_data); 34305986ba9SSascha Hauer 3448ff396feSPeter Chen if ((of_usb_get_phy_mode(dev->of_node) == USBPHY_INTERFACE_MODE_HSIC) 3458ff396feSPeter Chen && data->usbmisc_data) { 3467c8e8909SPeter Chen pdata.flags |= CI_HDRC_IMX_IS_HSIC; 3477c8e8909SPeter Chen data->usbmisc_data->hsic = 1; 3487c8e8909SPeter Chen data->pinctrl = devm_pinctrl_get(dev); 3493f4aad6eSPeter Chen if (PTR_ERR(data->pinctrl) == -ENODEV) 3503f4aad6eSPeter Chen data->pinctrl = NULL; 35118171cfcSAlexander Stein else if (IS_ERR(data->pinctrl)) 35218171cfcSAlexander Stein return dev_err_probe(dev, PTR_ERR(data->pinctrl), 35318171cfcSAlexander Stein "pinctrl get failed\n"); 354ae3e57aeSPeter Chen 3554d614128SPeter Chen data->hsic_pad_regulator = 3564d614128SPeter Chen devm_regulator_get_optional(dev, "hsic"); 3574d614128SPeter Chen if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) { 3584d614128SPeter Chen /* no pad regualator is needed */ 3594d614128SPeter Chen data->hsic_pad_regulator = NULL; 36018171cfcSAlexander Stein } else if (IS_ERR(data->hsic_pad_regulator)) 36118171cfcSAlexander Stein return dev_err_probe(dev, PTR_ERR(data->hsic_pad_regulator), 36218171cfcSAlexander Stein "Get HSIC pad regulator error\n"); 3634d614128SPeter Chen 3644d614128SPeter Chen if (data->hsic_pad_regulator) { 3654d614128SPeter Chen ret = regulator_enable(data->hsic_pad_regulator); 3664d614128SPeter Chen if (ret) { 3674d614128SPeter Chen dev_err(dev, 3684d614128SPeter Chen "Failed to enable HSIC pad regulator\n"); 3694d614128SPeter Chen return ret; 3704d614128SPeter Chen } 3714d614128SPeter Chen } 3724d614128SPeter Chen } 3734d614128SPeter Chen 3744d614128SPeter Chen /* HSIC pinctrl handling */ 3754d614128SPeter Chen if (data->pinctrl) { 3764d614128SPeter Chen struct pinctrl_state *pinctrl_hsic_idle; 3774d614128SPeter Chen 3787c8e8909SPeter Chen pinctrl_hsic_idle = pinctrl_lookup_state(data->pinctrl, "idle"); 3797c8e8909SPeter Chen if (IS_ERR(pinctrl_hsic_idle)) { 3807c8e8909SPeter Chen dev_err(dev, 3817c8e8909SPeter Chen "pinctrl_hsic_idle lookup failed, err=%ld\n", 3827c8e8909SPeter Chen PTR_ERR(pinctrl_hsic_idle)); 3837c8e8909SPeter Chen return PTR_ERR(pinctrl_hsic_idle); 3847c8e8909SPeter Chen } 3858e22978cSAlexander Shishkin 3867c8e8909SPeter Chen ret = pinctrl_select_state(data->pinctrl, pinctrl_hsic_idle); 3877c8e8909SPeter Chen if (ret) { 3887c8e8909SPeter Chen dev_err(dev, "hsic_idle select failed, err=%d\n", ret); 3897c8e8909SPeter Chen return ret; 3907c8e8909SPeter Chen } 3917c8e8909SPeter Chen 3927c8e8909SPeter Chen data->pinctrl_hsic_active = pinctrl_lookup_state(data->pinctrl, 3937c8e8909SPeter Chen "active"); 3947c8e8909SPeter Chen if (IS_ERR(data->pinctrl_hsic_active)) { 3957c8e8909SPeter Chen dev_err(dev, 3967c8e8909SPeter Chen "pinctrl_hsic_active lookup failed, err=%ld\n", 3977c8e8909SPeter Chen PTR_ERR(data->pinctrl_hsic_active)); 3987c8e8909SPeter Chen return PTR_ERR(data->pinctrl_hsic_active); 3997c8e8909SPeter Chen } 4007c8e8909SPeter Chen } 401d1609c31SPeter Chen 402d1609c31SPeter Chen if (pdata.flags & CI_HDRC_PMQOS) 40377b35245SRafael J. Wysocki cpu_latency_qos_add_request(&data->pm_qos_req, 0); 404d1609c31SPeter Chen 4057c8e8909SPeter Chen ret = imx_get_clks(dev); 4067c8e8909SPeter Chen if (ret) 4077c8e8909SPeter Chen goto disable_hsic_regulator; 4087c8e8909SPeter Chen 4097c8e8909SPeter Chen ret = imx_prepare_enable_clks(dev); 4107c8e8909SPeter Chen if (ret) 4117c8e8909SPeter Chen goto disable_hsic_regulator; 4127c8e8909SPeter Chen 4137c8e8909SPeter Chen data->phy = devm_usb_get_phy_by_phandle(dev, "fsl,usbphy", 0); 414af59a8b1SPeter Chen if (IS_ERR(data->phy)) { 415af59a8b1SPeter Chen ret = PTR_ERR(data->phy); 416d4d2e532SDan Carpenter if (ret != -ENODEV) 417d4d2e532SDan Carpenter goto err_clk; 4188253a34bSFabio Estevam data->phy = devm_usb_get_phy_by_phandle(dev, "phys", 0); 4198253a34bSFabio Estevam if (IS_ERR(data->phy)) { 4208253a34bSFabio Estevam ret = PTR_ERR(data->phy); 42116853d7bSMarkus Pargmann if (ret == -ENODEV) 422ed5a419bSPeter Chen data->phy = NULL; 423ed5a419bSPeter Chen else 4248e22978cSAlexander Shishkin goto err_clk; 4258e22978cSAlexander Shishkin } 4268253a34bSFabio Estevam } 4278e22978cSAlexander Shishkin 428ef44cb42SAntoine Tenart pdata.usb_phy = data->phy; 429746f316bSJun Li if (data->usbmisc_data) 430746f316bSJun Li data->usbmisc_data->usb_phy = data->phy; 431be9cae24SSebastian Reichel 43203e6275aSAndrey Smirnov if ((of_device_is_compatible(np, "fsl,imx53-usb") || 43303e6275aSAndrey Smirnov of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy && 434be9cae24SSebastian Reichel of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) { 435be9cae24SSebastian Reichel pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL; 436be9cae24SSebastian Reichel data->override_phy_control = true; 437be9cae24SSebastian Reichel usb_phy_init(pdata.usb_phy); 438be9cae24SSebastian Reichel } 439be9cae24SSebastian Reichel 440e14db48dSPeter Chen if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) 441e14db48dSPeter Chen data->supports_runtime_pm = true; 4421071055eSPeter Chen 44305986ba9SSascha Hauer ret = imx_usbmisc_init(data->usbmisc_data); 4448e22978cSAlexander Shishkin if (ret) { 4457c8e8909SPeter Chen dev_err(dev, "usbmisc init failed, ret=%d\n", ret); 446af59a8b1SPeter Chen goto err_clk; 4478e22978cSAlexander Shishkin } 4488e22978cSAlexander Shishkin 4497c8e8909SPeter Chen data->ci_pdev = ci_hdrc_add_device(dev, 4508e22978cSAlexander Shishkin pdev->resource, pdev->num_resources, 4518e22978cSAlexander Shishkin &pdata); 4528e22978cSAlexander Shishkin if (IS_ERR(data->ci_pdev)) { 4538e22978cSAlexander Shishkin ret = PTR_ERR(data->ci_pdev); 45418171cfcSAlexander Stein dev_err_probe(dev, ret, "ci_hdrc_add_device failed\n"); 455af59a8b1SPeter Chen goto err_clk; 4568e22978cSAlexander Shishkin } 4578e22978cSAlexander Shishkin 458df17aa9fSLi Jun if (data->usbmisc_data) { 45993c2c733SLi Jun if (!IS_ERR(pdata.id_extcon.edev) || 46093c2c733SLi Jun of_property_read_bool(np, "usb-role-switch")) 46193c2c733SLi Jun data->usbmisc_data->ext_id = 1; 46293c2c733SLi Jun 46393c2c733SLi Jun if (!IS_ERR(pdata.vbus_extcon.edev) || 46493c2c733SLi Jun of_property_read_bool(np, "usb-role-switch")) 46593c2c733SLi Jun data->usbmisc_data->ext_vbus = 1; 466d6f93d21SPeter Chen 467d6f93d21SPeter Chen /* usbmisc needs to know dr mode to choose wakeup setting */ 468d6f93d21SPeter Chen data->usbmisc_data->available_role = 469d6f93d21SPeter Chen ci_hdrc_query_available_role(data->ci_pdev); 470df17aa9fSLi Jun } 47193c2c733SLi Jun 47205986ba9SSascha Hauer ret = imx_usbmisc_init_post(data->usbmisc_data); 4738e22978cSAlexander Shishkin if (ret) { 4747c8e8909SPeter Chen dev_err(dev, "usbmisc post failed, ret=%d\n", ret); 4758e22978cSAlexander Shishkin goto disable_device; 4768e22978cSAlexander Shishkin } 4778e22978cSAlexander Shishkin 478e14db48dSPeter Chen if (data->supports_runtime_pm) { 4797c8e8909SPeter Chen pm_runtime_set_active(dev); 4807c8e8909SPeter Chen pm_runtime_enable(dev); 481e14db48dSPeter Chen } 4828e22978cSAlexander Shishkin 4837c8e8909SPeter Chen device_set_wakeup_capable(dev, true); 4846d653110SPeter Chen 4858e22978cSAlexander Shishkin return 0; 4868e22978cSAlexander Shishkin 4878e22978cSAlexander Shishkin disable_device: 4888e22978cSAlexander Shishkin ci_hdrc_remove_device(data->ci_pdev); 4898e22978cSAlexander Shishkin err_clk: 4907c8e8909SPeter Chen imx_disable_unprepare_clks(dev); 4917c8e8909SPeter Chen disable_hsic_regulator: 4927c8e8909SPeter Chen if (data->hsic_pad_regulator) 493141822aaSAndré Draszik /* don't overwrite original ret (cf. EPROBE_DEFER) */ 494141822aaSAndré Draszik regulator_disable(data->hsic_pad_regulator); 495d1609c31SPeter Chen if (pdata.flags & CI_HDRC_PMQOS) 49677b35245SRafael J. Wysocki cpu_latency_qos_remove_request(&data->pm_qos_req); 497141822aaSAndré Draszik data->ci_pdev = NULL; 4988e22978cSAlexander Shishkin return ret; 4998e22978cSAlexander Shishkin } 5008e22978cSAlexander Shishkin 5018e22978cSAlexander Shishkin static int ci_hdrc_imx_remove(struct platform_device *pdev) 5028e22978cSAlexander Shishkin { 5038e22978cSAlexander Shishkin struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); 5048e22978cSAlexander Shishkin 505e14db48dSPeter Chen if (data->supports_runtime_pm) { 506e14db48dSPeter Chen pm_runtime_get_sync(&pdev->dev); 5078e22978cSAlexander Shishkin pm_runtime_disable(&pdev->dev); 508e14db48dSPeter Chen pm_runtime_put_noidle(&pdev->dev); 509e14db48dSPeter Chen } 510141822aaSAndré Draszik if (data->ci_pdev) 5118e22978cSAlexander Shishkin ci_hdrc_remove_device(data->ci_pdev); 512be9cae24SSebastian Reichel if (data->override_phy_control) 513be9cae24SSebastian Reichel usb_phy_shutdown(data->phy); 514141822aaSAndré Draszik if (data->ci_pdev) { 515ae3e57aeSPeter Chen imx_disable_unprepare_clks(&pdev->dev); 516d1609c31SPeter Chen if (data->plat_data->flags & CI_HDRC_PMQOS) 51777b35245SRafael J. Wysocki cpu_latency_qos_remove_request(&data->pm_qos_req); 5187c8e8909SPeter Chen if (data->hsic_pad_regulator) 5197c8e8909SPeter Chen regulator_disable(data->hsic_pad_regulator); 520141822aaSAndré Draszik } 5218e22978cSAlexander Shishkin 5228e22978cSAlexander Shishkin return 0; 5238e22978cSAlexander Shishkin } 5248e22978cSAlexander Shishkin 525b09b5224SAndreas Fenkart static void ci_hdrc_imx_shutdown(struct platform_device *pdev) 526b09b5224SAndreas Fenkart { 527b09b5224SAndreas Fenkart ci_hdrc_imx_remove(pdev); 528b09b5224SAndreas Fenkart } 529b09b5224SAndreas Fenkart 530*b332d6d5SLi Jun static int __maybe_unused imx_controller_suspend(struct device *dev, 531*b332d6d5SLi Jun pm_message_t msg) 5322558c1f5SPeter Chen { 5332558c1f5SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 5347c8e8909SPeter Chen int ret = 0; 5352558c1f5SPeter Chen 5362558c1f5SPeter Chen dev_dbg(dev, "at %s\n", __func__); 5372558c1f5SPeter Chen 538*b332d6d5SLi Jun ret = imx_usbmisc_suspend(data->usbmisc_data, 539*b332d6d5SLi Jun PMSG_IS_AUTO(msg) || device_may_wakeup(dev)); 5407c8e8909SPeter Chen if (ret) { 541*b332d6d5SLi Jun dev_err(dev, 542*b332d6d5SLi Jun "usbmisc suspend failed, ret=%d\n", ret); 5437c8e8909SPeter Chen return ret; 5447c8e8909SPeter Chen } 5457c8e8909SPeter Chen 546ae3e57aeSPeter Chen imx_disable_unprepare_clks(dev); 547d1609c31SPeter Chen if (data->plat_data->flags & CI_HDRC_PMQOS) 54877b35245SRafael J. Wysocki cpu_latency_qos_remove_request(&data->pm_qos_req); 549d1609c31SPeter Chen 550e14db48dSPeter Chen data->in_lpm = true; 5512558c1f5SPeter Chen 5522558c1f5SPeter Chen return 0; 5532558c1f5SPeter Chen } 5542558c1f5SPeter Chen 555*b332d6d5SLi Jun static int __maybe_unused imx_controller_resume(struct device *dev, 556*b332d6d5SLi Jun pm_message_t msg) 5572558c1f5SPeter Chen { 5582558c1f5SPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 559e14db48dSPeter Chen int ret = 0; 5602558c1f5SPeter Chen 5612558c1f5SPeter Chen dev_dbg(dev, "at %s\n", __func__); 5622558c1f5SPeter Chen 563e14db48dSPeter Chen if (!data->in_lpm) { 564e14db48dSPeter Chen WARN_ON(1); 565e14db48dSPeter Chen return 0; 5662558c1f5SPeter Chen } 5672558c1f5SPeter Chen 568d1609c31SPeter Chen if (data->plat_data->flags & CI_HDRC_PMQOS) 56977b35245SRafael J. Wysocki cpu_latency_qos_add_request(&data->pm_qos_req, 0); 570d1609c31SPeter Chen 571ae3e57aeSPeter Chen ret = imx_prepare_enable_clks(dev); 572e14db48dSPeter Chen if (ret) 573e14db48dSPeter Chen return ret; 574e14db48dSPeter Chen 575e14db48dSPeter Chen data->in_lpm = false; 576e14db48dSPeter Chen 577*b332d6d5SLi Jun ret = imx_usbmisc_resume(data->usbmisc_data, 578*b332d6d5SLi Jun PMSG_IS_AUTO(msg) || device_may_wakeup(dev)); 579e14db48dSPeter Chen if (ret) { 580*b332d6d5SLi Jun dev_err(dev, "usbmisc resume failed, ret=%d\n", ret); 581e14db48dSPeter Chen goto clk_disable; 582e14db48dSPeter Chen } 583e14db48dSPeter Chen 584e14db48dSPeter Chen return 0; 585e14db48dSPeter Chen 586e14db48dSPeter Chen clk_disable: 587ae3e57aeSPeter Chen imx_disable_unprepare_clks(dev); 588e14db48dSPeter Chen return ret; 589e14db48dSPeter Chen } 590e14db48dSPeter Chen 5919f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev) 5922558c1f5SPeter Chen { 5936d653110SPeter Chen int ret; 5946d653110SPeter Chen 595e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 596e14db48dSPeter Chen 597e14db48dSPeter Chen if (data->in_lpm) 598e14db48dSPeter Chen /* The core's suspend doesn't run */ 599e14db48dSPeter Chen return 0; 600e14db48dSPeter Chen 601*b332d6d5SLi Jun ret = imx_controller_suspend(dev, PMSG_SUSPEND); 60271ac680eSPeter Chen if (ret) 60371ac680eSPeter Chen return ret; 60471ac680eSPeter Chen 60571ac680eSPeter Chen pinctrl_pm_select_sleep_state(dev); 60671ac680eSPeter Chen return ret; 6072558c1f5SPeter Chen } 6082558c1f5SPeter Chen 6099f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_resume(struct device *dev) 6102558c1f5SPeter Chen { 611e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 612e14db48dSPeter Chen int ret; 613e14db48dSPeter Chen 61471ac680eSPeter Chen pinctrl_pm_select_default_state(dev); 615*b332d6d5SLi Jun ret = imx_controller_resume(dev, PMSG_RESUME); 616e14db48dSPeter Chen if (!ret && data->supports_runtime_pm) { 617e14db48dSPeter Chen pm_runtime_disable(dev); 618e14db48dSPeter Chen pm_runtime_set_active(dev); 619e14db48dSPeter Chen pm_runtime_enable(dev); 620e14db48dSPeter Chen } 621e14db48dSPeter Chen 622e14db48dSPeter Chen return ret; 6232558c1f5SPeter Chen } 6242558c1f5SPeter Chen 6259f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev) 626e14db48dSPeter Chen { 627e14db48dSPeter Chen struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); 628e14db48dSPeter Chen 629e14db48dSPeter Chen if (data->in_lpm) { 630e14db48dSPeter Chen WARN_ON(1); 631e14db48dSPeter Chen return 0; 632e14db48dSPeter Chen } 633e14db48dSPeter Chen 634*b332d6d5SLi Jun return imx_controller_suspend(dev, PMSG_AUTO_SUSPEND); 635e14db48dSPeter Chen } 636e14db48dSPeter Chen 6379f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_runtime_resume(struct device *dev) 638e14db48dSPeter Chen { 639*b332d6d5SLi Jun return imx_controller_resume(dev, PMSG_AUTO_RESUME); 640e14db48dSPeter Chen } 641e14db48dSPeter Chen 6422558c1f5SPeter Chen static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { 6432558c1f5SPeter Chen SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume) 644e14db48dSPeter Chen SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, 645e14db48dSPeter Chen ci_hdrc_imx_runtime_resume, NULL) 6462558c1f5SPeter Chen }; 6478e22978cSAlexander Shishkin static struct platform_driver ci_hdrc_imx_driver = { 6488e22978cSAlexander Shishkin .probe = ci_hdrc_imx_probe, 6498e22978cSAlexander Shishkin .remove = ci_hdrc_imx_remove, 650b09b5224SAndreas Fenkart .shutdown = ci_hdrc_imx_shutdown, 6518e22978cSAlexander Shishkin .driver = { 6528e22978cSAlexander Shishkin .name = "imx_usb", 6538e22978cSAlexander Shishkin .of_match_table = ci_hdrc_imx_dt_ids, 6542558c1f5SPeter Chen .pm = &ci_hdrc_imx_pm_ops, 6558e22978cSAlexander Shishkin }, 6568e22978cSAlexander Shishkin }; 6578e22978cSAlexander Shishkin 6588e22978cSAlexander Shishkin module_platform_driver(ci_hdrc_imx_driver); 6598e22978cSAlexander Shishkin 6608e22978cSAlexander Shishkin MODULE_ALIAS("platform:imx-usb"); 6611f06072cSMarcus Folkesson MODULE_LICENSE("GPL"); 6628e22978cSAlexander Shishkin MODULE_DESCRIPTION("CI HDRC i.MX USB binding"); 6638e22978cSAlexander Shishkin MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 6648e22978cSAlexander Shishkin MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>"); 665