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