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