xref: /linux/drivers/usb/chipidea/ci_hdrc_imx.c (revision b332d6d5c804085ac26d2e7e1a953b59b49644f3)
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