xref: /linux/drivers/usb/chipidea/ci_hdrc_imx.c (revision 9f644a64884f97f0d92f0689afc7fcf177b6ee92)
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/of_gpio.h>
118e22978cSAlexander Shishkin #include <linux/platform_device.h>
128e22978cSAlexander Shishkin #include <linux/pm_runtime.h>
138e22978cSAlexander Shishkin #include <linux/dma-mapping.h>
148e22978cSAlexander Shishkin #include <linux/usb/chipidea.h>
15d13631bbSFabien Lahoudere #include <linux/usb/of.h>
168e22978cSAlexander Shishkin #include <linux/clk.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 
671071055eSPeter Chen static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
6881345722SStefan Wahren 	{ .compatible = "fsl,imx23-usb", .data = &imx23_usb_data},
691071055eSPeter Chen 	{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
701071055eSPeter Chen 	{ .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
71e14db48dSPeter Chen 	{ .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
72e14db48dSPeter Chen 	{ .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
738315b77dSLi Jun 	{ .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data},
7452fe568eSPeter Chen 	{ .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data},
755cb377c5SPeter Chen 	{ .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
761071055eSPeter Chen 	{ /* sentinel */ }
771071055eSPeter Chen };
781071055eSPeter Chen MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
791071055eSPeter Chen 
808e22978cSAlexander Shishkin struct ci_hdrc_imx_data {
818e22978cSAlexander Shishkin 	struct usb_phy *phy;
828e22978cSAlexander Shishkin 	struct platform_device *ci_pdev;
838e22978cSAlexander Shishkin 	struct clk *clk;
8405986ba9SSascha Hauer 	struct imx_usbmisc_data *usbmisc_data;
85e14db48dSPeter Chen 	bool supports_runtime_pm;
86be9cae24SSebastian Reichel 	bool override_phy_control;
87e14db48dSPeter Chen 	bool in_lpm;
88ae3e57aeSPeter Chen 	/* SoC before i.mx6 (except imx23/imx28) needs three clks */
89ae3e57aeSPeter Chen 	bool need_three_clks;
90ae3e57aeSPeter Chen 	struct clk *clk_ipg;
91ae3e57aeSPeter Chen 	struct clk *clk_ahb;
92ae3e57aeSPeter Chen 	struct clk *clk_per;
93ae3e57aeSPeter Chen 	/* --------------------------------- */
948e22978cSAlexander Shishkin };
958e22978cSAlexander Shishkin 
968e22978cSAlexander Shishkin /* Common functions shared by usbmisc drivers */
978e22978cSAlexander Shishkin 
9805986ba9SSascha Hauer static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
998e22978cSAlexander Shishkin {
100f40017e0SStefan Agner 	struct platform_device *misc_pdev;
1018e22978cSAlexander Shishkin 	struct device_node *np = dev->of_node;
1028e22978cSAlexander Shishkin 	struct of_phandle_args args;
10305986ba9SSascha Hauer 	struct imx_usbmisc_data *data;
1048e22978cSAlexander Shishkin 	int ret;
1058e22978cSAlexander Shishkin 
10605986ba9SSascha Hauer 	/*
10705986ba9SSascha Hauer 	 * In case the fsl,usbmisc property is not present this device doesn't
10805986ba9SSascha Hauer 	 * need usbmisc. Return NULL (which is no error here)
10905986ba9SSascha Hauer 	 */
11005986ba9SSascha Hauer 	if (!of_get_property(np, "fsl,usbmisc", NULL))
11105986ba9SSascha Hauer 		return NULL;
11205986ba9SSascha Hauer 
11305986ba9SSascha Hauer 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
11405986ba9SSascha Hauer 	if (!data)
11505986ba9SSascha Hauer 		return ERR_PTR(-ENOMEM);
1168e22978cSAlexander Shishkin 
1178e22978cSAlexander Shishkin 	ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
1188e22978cSAlexander Shishkin 					0, &args);
1198e22978cSAlexander Shishkin 	if (ret) {
1208e22978cSAlexander Shishkin 		dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
1218e22978cSAlexander Shishkin 			ret);
12205986ba9SSascha Hauer 		return ERR_PTR(ret);
1238e22978cSAlexander Shishkin 	}
12405986ba9SSascha Hauer 
12505986ba9SSascha Hauer 	data->index = args.args[0];
126f40017e0SStefan Agner 
127f40017e0SStefan Agner 	misc_pdev = of_find_device_by_node(args.np);
1288e22978cSAlexander Shishkin 	of_node_put(args.np);
1298e22978cSAlexander Shishkin 
130ef12da91STomeu Vizoso 	if (!misc_pdev || !platform_get_drvdata(misc_pdev))
131f40017e0SStefan Agner 		return ERR_PTR(-EPROBE_DEFER);
132f40017e0SStefan Agner 
133f40017e0SStefan Agner 	data->dev = &misc_pdev->dev;
134f40017e0SStefan Agner 
1358e22978cSAlexander Shishkin 	if (of_find_property(np, "disable-over-current", NULL))
13605986ba9SSascha Hauer 		data->disable_oc = 1;
1378e22978cSAlexander Shishkin 
1389dba516eSLi Jun 	if (of_find_property(np, "over-current-active-high", NULL))
1399dba516eSLi Jun 		data->oc_polarity = 1;
1409dba516eSLi Jun 
1418e22978cSAlexander Shishkin 	if (of_find_property(np, "external-vbus-divider", NULL))
14205986ba9SSascha Hauer 		data->evdo = 1;
1438e22978cSAlexander Shishkin 
144d13631bbSFabien Lahoudere 	if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
145d13631bbSFabien Lahoudere 		data->ulpi = 1;
146d13631bbSFabien Lahoudere 
14705986ba9SSascha Hauer 	return data;
1488e22978cSAlexander Shishkin }
1498e22978cSAlexander Shishkin 
1508e22978cSAlexander Shishkin /* End of common functions shared by usbmisc drivers*/
151ae3e57aeSPeter Chen static int imx_get_clks(struct device *dev)
152ae3e57aeSPeter Chen {
153ae3e57aeSPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
154ae3e57aeSPeter Chen 	int ret = 0;
155ae3e57aeSPeter Chen 
156ae3e57aeSPeter Chen 	data->clk_ipg = devm_clk_get(dev, "ipg");
157ae3e57aeSPeter Chen 	if (IS_ERR(data->clk_ipg)) {
158ae3e57aeSPeter Chen 		/* If the platform only needs one clocks */
159ae3e57aeSPeter Chen 		data->clk = devm_clk_get(dev, NULL);
160ae3e57aeSPeter Chen 		if (IS_ERR(data->clk)) {
161ae3e57aeSPeter Chen 			ret = PTR_ERR(data->clk);
162ae3e57aeSPeter Chen 			dev_err(dev,
163ae3e57aeSPeter Chen 				"Failed to get clks, err=%ld,%ld\n",
164ae3e57aeSPeter Chen 				PTR_ERR(data->clk), PTR_ERR(data->clk_ipg));
165ae3e57aeSPeter Chen 			return ret;
166ae3e57aeSPeter Chen 		}
167ae3e57aeSPeter Chen 		return ret;
168ae3e57aeSPeter Chen 	}
169ae3e57aeSPeter Chen 
170ae3e57aeSPeter Chen 	data->clk_ahb = devm_clk_get(dev, "ahb");
171ae3e57aeSPeter Chen 	if (IS_ERR(data->clk_ahb)) {
172ae3e57aeSPeter Chen 		ret = PTR_ERR(data->clk_ahb);
173ae3e57aeSPeter Chen 		dev_err(dev,
174ae3e57aeSPeter Chen 			"Failed to get ahb clock, err=%d\n", ret);
175ae3e57aeSPeter Chen 		return ret;
176ae3e57aeSPeter Chen 	}
177ae3e57aeSPeter Chen 
178ae3e57aeSPeter Chen 	data->clk_per = devm_clk_get(dev, "per");
179ae3e57aeSPeter Chen 	if (IS_ERR(data->clk_per)) {
180ae3e57aeSPeter Chen 		ret = PTR_ERR(data->clk_per);
181ae3e57aeSPeter Chen 		dev_err(dev,
182ae3e57aeSPeter Chen 			"Failed to get per clock, err=%d\n", ret);
183ae3e57aeSPeter Chen 		return ret;
184ae3e57aeSPeter Chen 	}
185ae3e57aeSPeter Chen 
186ae3e57aeSPeter Chen 	data->need_three_clks = true;
187ae3e57aeSPeter Chen 	return ret;
188ae3e57aeSPeter Chen }
189ae3e57aeSPeter Chen 
190ae3e57aeSPeter Chen static int imx_prepare_enable_clks(struct device *dev)
191ae3e57aeSPeter Chen {
192ae3e57aeSPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
193ae3e57aeSPeter Chen 	int ret = 0;
194ae3e57aeSPeter Chen 
195ae3e57aeSPeter Chen 	if (data->need_three_clks) {
196ae3e57aeSPeter Chen 		ret = clk_prepare_enable(data->clk_ipg);
197ae3e57aeSPeter Chen 		if (ret) {
198ae3e57aeSPeter Chen 			dev_err(dev,
199ae3e57aeSPeter Chen 				"Failed to prepare/enable ipg clk, err=%d\n",
200ae3e57aeSPeter Chen 				ret);
201ae3e57aeSPeter Chen 			return ret;
202ae3e57aeSPeter Chen 		}
203ae3e57aeSPeter Chen 
204ae3e57aeSPeter Chen 		ret = clk_prepare_enable(data->clk_ahb);
205ae3e57aeSPeter Chen 		if (ret) {
206ae3e57aeSPeter Chen 			dev_err(dev,
207ae3e57aeSPeter Chen 				"Failed to prepare/enable ahb clk, err=%d\n",
208ae3e57aeSPeter Chen 				ret);
209ae3e57aeSPeter Chen 			clk_disable_unprepare(data->clk_ipg);
210ae3e57aeSPeter Chen 			return ret;
211ae3e57aeSPeter Chen 		}
212ae3e57aeSPeter Chen 
213ae3e57aeSPeter Chen 		ret = clk_prepare_enable(data->clk_per);
214ae3e57aeSPeter Chen 		if (ret) {
215ae3e57aeSPeter Chen 			dev_err(dev,
216ae3e57aeSPeter Chen 				"Failed to prepare/enable per clk, err=%d\n",
217ae3e57aeSPeter Chen 				ret);
218ae3e57aeSPeter Chen 			clk_disable_unprepare(data->clk_ahb);
219ae3e57aeSPeter Chen 			clk_disable_unprepare(data->clk_ipg);
220ae3e57aeSPeter Chen 			return ret;
221ae3e57aeSPeter Chen 		}
222ae3e57aeSPeter Chen 	} else {
223ae3e57aeSPeter Chen 		ret = clk_prepare_enable(data->clk);
224ae3e57aeSPeter Chen 		if (ret) {
225ae3e57aeSPeter Chen 			dev_err(dev,
226ae3e57aeSPeter Chen 				"Failed to prepare/enable clk, err=%d\n",
227ae3e57aeSPeter Chen 				ret);
228ae3e57aeSPeter Chen 			return ret;
229ae3e57aeSPeter Chen 		}
230ae3e57aeSPeter Chen 	}
231ae3e57aeSPeter Chen 
232ae3e57aeSPeter Chen 	return ret;
233ae3e57aeSPeter Chen }
234ae3e57aeSPeter Chen 
235ae3e57aeSPeter Chen static void imx_disable_unprepare_clks(struct device *dev)
236ae3e57aeSPeter Chen {
237ae3e57aeSPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
238ae3e57aeSPeter Chen 
239ae3e57aeSPeter Chen 	if (data->need_three_clks) {
240ae3e57aeSPeter Chen 		clk_disable_unprepare(data->clk_per);
241ae3e57aeSPeter Chen 		clk_disable_unprepare(data->clk_ahb);
242ae3e57aeSPeter Chen 		clk_disable_unprepare(data->clk_ipg);
243ae3e57aeSPeter Chen 	} else {
244ae3e57aeSPeter Chen 		clk_disable_unprepare(data->clk);
245ae3e57aeSPeter Chen 	}
246ae3e57aeSPeter Chen }
2478e22978cSAlexander Shishkin 
2488e22978cSAlexander Shishkin static int ci_hdrc_imx_probe(struct platform_device *pdev)
2498e22978cSAlexander Shishkin {
2508e22978cSAlexander Shishkin 	struct ci_hdrc_imx_data *data;
2518e22978cSAlexander Shishkin 	struct ci_hdrc_platform_data pdata = {
252c844d6c8SAlexander Shiyan 		.name		= dev_name(&pdev->dev),
2538e22978cSAlexander Shishkin 		.capoffset	= DEF_CAPOFFSET,
2548e22978cSAlexander Shishkin 	};
2558e22978cSAlexander Shishkin 	int ret;
2566f51bc34SLABBE Corentin 	const struct of_device_id *of_id;
2576f51bc34SLABBE Corentin 	const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
258be9cae24SSebastian Reichel 	struct device_node *np = pdev->dev.of_node;
2596f51bc34SLABBE Corentin 
2606f51bc34SLABBE Corentin 	of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
2616f51bc34SLABBE Corentin 	if (!of_id)
2626f51bc34SLABBE Corentin 		return -ENODEV;
2636f51bc34SLABBE Corentin 
2646f51bc34SLABBE Corentin 	imx_platform_flag = of_id->data;
2658e22978cSAlexander Shishkin 
2668e22978cSAlexander Shishkin 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
26773529828SFabio Estevam 	if (!data)
2688e22978cSAlexander Shishkin 		return -ENOMEM;
2698e22978cSAlexander Shishkin 
270ae3e57aeSPeter Chen 	platform_set_drvdata(pdev, data);
27105986ba9SSascha Hauer 	data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
27205986ba9SSascha Hauer 	if (IS_ERR(data->usbmisc_data))
27305986ba9SSascha Hauer 		return PTR_ERR(data->usbmisc_data);
27405986ba9SSascha Hauer 
275ae3e57aeSPeter Chen 	ret = imx_get_clks(&pdev->dev);
276ae3e57aeSPeter Chen 	if (ret)
2778e22978cSAlexander Shishkin 		return ret;
278ae3e57aeSPeter Chen 
279ae3e57aeSPeter Chen 	ret = imx_prepare_enable_clks(&pdev->dev);
280ae3e57aeSPeter Chen 	if (ret)
281ae3e57aeSPeter Chen 		return ret;
2828e22978cSAlexander Shishkin 
283046916deSFabio Estevam 	data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
284af59a8b1SPeter Chen 	if (IS_ERR(data->phy)) {
285af59a8b1SPeter Chen 		ret = PTR_ERR(data->phy);
28616853d7bSMarkus Pargmann 		/* Return -EINVAL if no usbphy is available */
28716853d7bSMarkus Pargmann 		if (ret == -ENODEV)
28816853d7bSMarkus Pargmann 			ret = -EINVAL;
2898e22978cSAlexander Shishkin 		goto err_clk;
2908e22978cSAlexander Shishkin 	}
2918e22978cSAlexander Shishkin 
292ef44cb42SAntoine Tenart 	pdata.usb_phy = data->phy;
293be9cae24SSebastian Reichel 
29403e6275aSAndrey Smirnov 	if ((of_device_is_compatible(np, "fsl,imx53-usb") ||
29503e6275aSAndrey Smirnov 	     of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy &&
296be9cae24SSebastian Reichel 	    of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) {
297be9cae24SSebastian Reichel 		pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL;
298be9cae24SSebastian Reichel 		data->override_phy_control = true;
299be9cae24SSebastian Reichel 		usb_phy_init(pdata.usb_phy);
300be9cae24SSebastian Reichel 	}
301be9cae24SSebastian Reichel 
30256040087SPeter Chen 	pdata.flags |= imx_platform_flag->flags;
303e14db48dSPeter Chen 	if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
304e14db48dSPeter Chen 		data->supports_runtime_pm = true;
3051071055eSPeter Chen 
30605986ba9SSascha Hauer 	ret = imx_usbmisc_init(data->usbmisc_data);
3078e22978cSAlexander Shishkin 	if (ret) {
308a4cf1b14SPeter Chen 		dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
309af59a8b1SPeter Chen 		goto err_clk;
3108e22978cSAlexander Shishkin 	}
3118e22978cSAlexander Shishkin 
3128e22978cSAlexander Shishkin 	data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
3138e22978cSAlexander Shishkin 				pdev->resource, pdev->num_resources,
3148e22978cSAlexander Shishkin 				&pdata);
3158e22978cSAlexander Shishkin 	if (IS_ERR(data->ci_pdev)) {
3168e22978cSAlexander Shishkin 		ret = PTR_ERR(data->ci_pdev);
317d3d8425aSStefan Agner 		if (ret != -EPROBE_DEFER)
3188e22978cSAlexander Shishkin 			dev_err(&pdev->dev,
319d3d8425aSStefan Agner 				"ci_hdrc_add_device failed, err=%d\n", ret);
320af59a8b1SPeter Chen 		goto err_clk;
3218e22978cSAlexander Shishkin 	}
3228e22978cSAlexander Shishkin 
32305986ba9SSascha Hauer 	ret = imx_usbmisc_init_post(data->usbmisc_data);
3248e22978cSAlexander Shishkin 	if (ret) {
325a4cf1b14SPeter Chen 		dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
3268e22978cSAlexander Shishkin 		goto disable_device;
3278e22978cSAlexander Shishkin 	}
3288e22978cSAlexander Shishkin 
329e14db48dSPeter Chen 	if (data->supports_runtime_pm) {
330e14db48dSPeter Chen 		pm_runtime_set_active(&pdev->dev);
3318e22978cSAlexander Shishkin 		pm_runtime_enable(&pdev->dev);
332e14db48dSPeter Chen 	}
3338e22978cSAlexander Shishkin 
3346d653110SPeter Chen 	device_set_wakeup_capable(&pdev->dev, true);
3356d653110SPeter Chen 
3368e22978cSAlexander Shishkin 	return 0;
3378e22978cSAlexander Shishkin 
3388e22978cSAlexander Shishkin disable_device:
3398e22978cSAlexander Shishkin 	ci_hdrc_remove_device(data->ci_pdev);
3408e22978cSAlexander Shishkin err_clk:
341ae3e57aeSPeter Chen 	imx_disable_unprepare_clks(&pdev->dev);
3428e22978cSAlexander Shishkin 	return ret;
3438e22978cSAlexander Shishkin }
3448e22978cSAlexander Shishkin 
3458e22978cSAlexander Shishkin static int ci_hdrc_imx_remove(struct platform_device *pdev)
3468e22978cSAlexander Shishkin {
3478e22978cSAlexander Shishkin 	struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
3488e22978cSAlexander Shishkin 
349e14db48dSPeter Chen 	if (data->supports_runtime_pm) {
350e14db48dSPeter Chen 		pm_runtime_get_sync(&pdev->dev);
3518e22978cSAlexander Shishkin 		pm_runtime_disable(&pdev->dev);
352e14db48dSPeter Chen 		pm_runtime_put_noidle(&pdev->dev);
353e14db48dSPeter Chen 	}
3548e22978cSAlexander Shishkin 	ci_hdrc_remove_device(data->ci_pdev);
355be9cae24SSebastian Reichel 	if (data->override_phy_control)
356be9cae24SSebastian Reichel 		usb_phy_shutdown(data->phy);
357ae3e57aeSPeter Chen 	imx_disable_unprepare_clks(&pdev->dev);
3588e22978cSAlexander Shishkin 
3598e22978cSAlexander Shishkin 	return 0;
3608e22978cSAlexander Shishkin }
3618e22978cSAlexander Shishkin 
362b09b5224SAndreas Fenkart static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
363b09b5224SAndreas Fenkart {
364b09b5224SAndreas Fenkart 	ci_hdrc_imx_remove(pdev);
365b09b5224SAndreas Fenkart }
366b09b5224SAndreas Fenkart 
367*9f644a64SMarcus Folkesson static int __maybe_unused imx_controller_suspend(struct device *dev)
3682558c1f5SPeter Chen {
3692558c1f5SPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
3702558c1f5SPeter Chen 
3712558c1f5SPeter Chen 	dev_dbg(dev, "at %s\n", __func__);
3722558c1f5SPeter Chen 
373ae3e57aeSPeter Chen 	imx_disable_unprepare_clks(dev);
374e14db48dSPeter Chen 	data->in_lpm = true;
3752558c1f5SPeter Chen 
3762558c1f5SPeter Chen 	return 0;
3772558c1f5SPeter Chen }
3782558c1f5SPeter Chen 
379*9f644a64SMarcus Folkesson static int __maybe_unused imx_controller_resume(struct device *dev)
3802558c1f5SPeter Chen {
3812558c1f5SPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
382e14db48dSPeter Chen 	int ret = 0;
3832558c1f5SPeter Chen 
3842558c1f5SPeter Chen 	dev_dbg(dev, "at %s\n", __func__);
3852558c1f5SPeter Chen 
386e14db48dSPeter Chen 	if (!data->in_lpm) {
387e14db48dSPeter Chen 		WARN_ON(1);
388e14db48dSPeter Chen 		return 0;
3892558c1f5SPeter Chen 	}
3902558c1f5SPeter Chen 
391ae3e57aeSPeter Chen 	ret = imx_prepare_enable_clks(dev);
392e14db48dSPeter Chen 	if (ret)
393e14db48dSPeter Chen 		return ret;
394e14db48dSPeter Chen 
395e14db48dSPeter Chen 	data->in_lpm = false;
396e14db48dSPeter Chen 
397e14db48dSPeter Chen 	ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false);
398e14db48dSPeter Chen 	if (ret) {
399e14db48dSPeter Chen 		dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
400e14db48dSPeter Chen 		goto clk_disable;
401e14db48dSPeter Chen 	}
402e14db48dSPeter Chen 
403e14db48dSPeter Chen 	return 0;
404e14db48dSPeter Chen 
405e14db48dSPeter Chen clk_disable:
406ae3e57aeSPeter Chen 	imx_disable_unprepare_clks(dev);
407e14db48dSPeter Chen 	return ret;
408e14db48dSPeter Chen }
409e14db48dSPeter Chen 
410*9f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev)
4112558c1f5SPeter Chen {
4126d653110SPeter Chen 	int ret;
4136d653110SPeter Chen 
414e14db48dSPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
415e14db48dSPeter Chen 
416e14db48dSPeter Chen 	if (data->in_lpm)
417e14db48dSPeter Chen 		/* The core's suspend doesn't run */
418e14db48dSPeter Chen 		return 0;
419e14db48dSPeter Chen 
4206d653110SPeter Chen 	if (device_may_wakeup(dev)) {
4216d653110SPeter Chen 		ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
4226d653110SPeter Chen 		if (ret) {
4236d653110SPeter Chen 			dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n",
4246d653110SPeter Chen 					ret);
4256d653110SPeter Chen 			return ret;
4266d653110SPeter Chen 		}
4276d653110SPeter Chen 	}
4286d653110SPeter Chen 
4292558c1f5SPeter Chen 	return imx_controller_suspend(dev);
4302558c1f5SPeter Chen }
4312558c1f5SPeter Chen 
432*9f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
4332558c1f5SPeter Chen {
434e14db48dSPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
435e14db48dSPeter Chen 	int ret;
436e14db48dSPeter Chen 
437e14db48dSPeter Chen 	ret = imx_controller_resume(dev);
438e14db48dSPeter Chen 	if (!ret && data->supports_runtime_pm) {
439e14db48dSPeter Chen 		pm_runtime_disable(dev);
440e14db48dSPeter Chen 		pm_runtime_set_active(dev);
441e14db48dSPeter Chen 		pm_runtime_enable(dev);
442e14db48dSPeter Chen 	}
443e14db48dSPeter Chen 
444e14db48dSPeter Chen 	return ret;
4452558c1f5SPeter Chen }
4462558c1f5SPeter Chen 
447*9f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev)
448e14db48dSPeter Chen {
449e14db48dSPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
450e14db48dSPeter Chen 	int ret;
451e14db48dSPeter Chen 
452e14db48dSPeter Chen 	if (data->in_lpm) {
453e14db48dSPeter Chen 		WARN_ON(1);
454e14db48dSPeter Chen 		return 0;
455e14db48dSPeter Chen 	}
456e14db48dSPeter Chen 
457e14db48dSPeter Chen 	ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
458e14db48dSPeter Chen 	if (ret) {
459e14db48dSPeter Chen 		dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
460e14db48dSPeter Chen 		return ret;
461e14db48dSPeter Chen 	}
462e14db48dSPeter Chen 
463e14db48dSPeter Chen 	return imx_controller_suspend(dev);
464e14db48dSPeter Chen }
465e14db48dSPeter Chen 
466*9f644a64SMarcus Folkesson static int __maybe_unused ci_hdrc_imx_runtime_resume(struct device *dev)
467e14db48dSPeter Chen {
468e14db48dSPeter Chen 	return imx_controller_resume(dev);
469e14db48dSPeter Chen }
470e14db48dSPeter Chen 
4712558c1f5SPeter Chen static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
4722558c1f5SPeter Chen 	SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
473e14db48dSPeter Chen 	SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
474e14db48dSPeter Chen 			ci_hdrc_imx_runtime_resume, NULL)
4752558c1f5SPeter Chen };
4768e22978cSAlexander Shishkin static struct platform_driver ci_hdrc_imx_driver = {
4778e22978cSAlexander Shishkin 	.probe = ci_hdrc_imx_probe,
4788e22978cSAlexander Shishkin 	.remove = ci_hdrc_imx_remove,
479b09b5224SAndreas Fenkart 	.shutdown = ci_hdrc_imx_shutdown,
4808e22978cSAlexander Shishkin 	.driver = {
4818e22978cSAlexander Shishkin 		.name = "imx_usb",
4828e22978cSAlexander Shishkin 		.of_match_table = ci_hdrc_imx_dt_ids,
4832558c1f5SPeter Chen 		.pm = &ci_hdrc_imx_pm_ops,
4848e22978cSAlexander Shishkin 	 },
4858e22978cSAlexander Shishkin };
4868e22978cSAlexander Shishkin 
4878e22978cSAlexander Shishkin module_platform_driver(ci_hdrc_imx_driver);
4888e22978cSAlexander Shishkin 
4898e22978cSAlexander Shishkin MODULE_ALIAS("platform:imx-usb");
4908e22978cSAlexander Shishkin MODULE_LICENSE("GPL v2");
4918e22978cSAlexander Shishkin MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
4928e22978cSAlexander Shishkin MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
4938e22978cSAlexander Shishkin MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
494