xref: /linux/drivers/usb/chipidea/ci_hdrc_imx.c (revision 5fd54ace4721fc5ce2bb5aef6318fcf17f421460)
1*5fd54aceSGreg 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  * The code contained herein is licensed under the GNU General Public
88e22978cSAlexander Shishkin  * License. You may obtain a copy of the GNU General Public License
98e22978cSAlexander Shishkin  * Version 2 or later at the following locations:
108e22978cSAlexander Shishkin  *
118e22978cSAlexander Shishkin  * http://www.opensource.org/licenses/gpl-license.html
128e22978cSAlexander Shishkin  * http://www.gnu.org/copyleft/gpl.html
138e22978cSAlexander Shishkin  */
148e22978cSAlexander Shishkin 
158e22978cSAlexander Shishkin #include <linux/module.h>
168e22978cSAlexander Shishkin #include <linux/of_platform.h>
178e22978cSAlexander Shishkin #include <linux/of_gpio.h>
188e22978cSAlexander Shishkin #include <linux/platform_device.h>
198e22978cSAlexander Shishkin #include <linux/pm_runtime.h>
208e22978cSAlexander Shishkin #include <linux/dma-mapping.h>
218e22978cSAlexander Shishkin #include <linux/usb/chipidea.h>
22d13631bbSFabien Lahoudere #include <linux/usb/of.h>
238e22978cSAlexander Shishkin #include <linux/clk.h>
248e22978cSAlexander Shishkin 
258e22978cSAlexander Shishkin #include "ci.h"
268e22978cSAlexander Shishkin #include "ci_hdrc_imx.h"
278e22978cSAlexander Shishkin 
281071055eSPeter Chen struct ci_hdrc_imx_platform_flag {
291071055eSPeter Chen 	unsigned int flags;
30e14db48dSPeter Chen 	bool runtime_pm;
311071055eSPeter Chen };
321071055eSPeter Chen 
3381345722SStefan Wahren static const struct ci_hdrc_imx_platform_flag imx23_usb_data = {
3481345722SStefan Wahren 	.flags = CI_HDRC_TURN_VBUS_EARLY_ON |
3581345722SStefan Wahren 		CI_HDRC_DISABLE_STREAMING,
3681345722SStefan Wahren };
3781345722SStefan Wahren 
381071055eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
390ef877a4SPeter Chen 		CI_HDRC_DISABLE_STREAMING,
401071055eSPeter Chen };
411071055eSPeter Chen 
421071055eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx28_usb_data = {
436adb9b7bSLi Jun 	.flags = CI_HDRC_IMX28_WRITE_FIX |
440ef877a4SPeter Chen 		CI_HDRC_TURN_VBUS_EARLY_ON |
450ef877a4SPeter Chen 		CI_HDRC_DISABLE_STREAMING,
461071055eSPeter Chen };
471071055eSPeter Chen 
48e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = {
496adb9b7bSLi Jun 	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
500ef877a4SPeter Chen 		CI_HDRC_TURN_VBUS_EARLY_ON |
510ef877a4SPeter Chen 		CI_HDRC_DISABLE_STREAMING,
52e14db48dSPeter Chen };
53e14db48dSPeter Chen 
54e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = {
556adb9b7bSLi Jun 	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
560ef877a4SPeter Chen 		CI_HDRC_TURN_VBUS_EARLY_ON |
570ef877a4SPeter Chen 		CI_HDRC_DISABLE_HOST_STREAMING,
58e14db48dSPeter Chen };
59e14db48dSPeter Chen 
60e14db48dSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = {
616adb9b7bSLi Jun 	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
620ef877a4SPeter Chen 		CI_HDRC_TURN_VBUS_EARLY_ON |
630ef877a4SPeter Chen 		CI_HDRC_DISABLE_HOST_STREAMING,
64e14db48dSPeter Chen };
65e14db48dSPeter Chen 
6652fe568eSPeter Chen static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = {
6752fe568eSPeter Chen 	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
6852fe568eSPeter Chen 		CI_HDRC_TURN_VBUS_EARLY_ON,
6952fe568eSPeter Chen };
7052fe568eSPeter Chen 
715cb377c5SPeter Chen static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
725cb377c5SPeter Chen 	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM,
735cb377c5SPeter Chen };
745cb377c5SPeter Chen 
751071055eSPeter Chen static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
7681345722SStefan Wahren 	{ .compatible = "fsl,imx23-usb", .data = &imx23_usb_data},
771071055eSPeter Chen 	{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
781071055eSPeter Chen 	{ .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
79e14db48dSPeter Chen 	{ .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
80e14db48dSPeter Chen 	{ .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
818315b77dSLi Jun 	{ .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data},
8252fe568eSPeter Chen 	{ .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data},
835cb377c5SPeter Chen 	{ .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
841071055eSPeter Chen 	{ /* sentinel */ }
851071055eSPeter Chen };
861071055eSPeter Chen MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
871071055eSPeter Chen 
888e22978cSAlexander Shishkin struct ci_hdrc_imx_data {
898e22978cSAlexander Shishkin 	struct usb_phy *phy;
908e22978cSAlexander Shishkin 	struct platform_device *ci_pdev;
918e22978cSAlexander Shishkin 	struct clk *clk;
9205986ba9SSascha Hauer 	struct imx_usbmisc_data *usbmisc_data;
93e14db48dSPeter Chen 	bool supports_runtime_pm;
94e14db48dSPeter Chen 	bool in_lpm;
95ae3e57aeSPeter Chen 	/* SoC before i.mx6 (except imx23/imx28) needs three clks */
96ae3e57aeSPeter Chen 	bool need_three_clks;
97ae3e57aeSPeter Chen 	struct clk *clk_ipg;
98ae3e57aeSPeter Chen 	struct clk *clk_ahb;
99ae3e57aeSPeter Chen 	struct clk *clk_per;
100ae3e57aeSPeter Chen 	/* --------------------------------- */
1018e22978cSAlexander Shishkin };
1028e22978cSAlexander Shishkin 
1038e22978cSAlexander Shishkin /* Common functions shared by usbmisc drivers */
1048e22978cSAlexander Shishkin 
10505986ba9SSascha Hauer static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
1068e22978cSAlexander Shishkin {
107f40017e0SStefan Agner 	struct platform_device *misc_pdev;
1088e22978cSAlexander Shishkin 	struct device_node *np = dev->of_node;
1098e22978cSAlexander Shishkin 	struct of_phandle_args args;
11005986ba9SSascha Hauer 	struct imx_usbmisc_data *data;
1118e22978cSAlexander Shishkin 	int ret;
1128e22978cSAlexander Shishkin 
11305986ba9SSascha Hauer 	/*
11405986ba9SSascha Hauer 	 * In case the fsl,usbmisc property is not present this device doesn't
11505986ba9SSascha Hauer 	 * need usbmisc. Return NULL (which is no error here)
11605986ba9SSascha Hauer 	 */
11705986ba9SSascha Hauer 	if (!of_get_property(np, "fsl,usbmisc", NULL))
11805986ba9SSascha Hauer 		return NULL;
11905986ba9SSascha Hauer 
12005986ba9SSascha Hauer 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
12105986ba9SSascha Hauer 	if (!data)
12205986ba9SSascha Hauer 		return ERR_PTR(-ENOMEM);
1238e22978cSAlexander Shishkin 
1248e22978cSAlexander Shishkin 	ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
1258e22978cSAlexander Shishkin 					0, &args);
1268e22978cSAlexander Shishkin 	if (ret) {
1278e22978cSAlexander Shishkin 		dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
1288e22978cSAlexander Shishkin 			ret);
12905986ba9SSascha Hauer 		return ERR_PTR(ret);
1308e22978cSAlexander Shishkin 	}
13105986ba9SSascha Hauer 
13205986ba9SSascha Hauer 	data->index = args.args[0];
133f40017e0SStefan Agner 
134f40017e0SStefan Agner 	misc_pdev = of_find_device_by_node(args.np);
1358e22978cSAlexander Shishkin 	of_node_put(args.np);
1368e22978cSAlexander Shishkin 
137ef12da91STomeu Vizoso 	if (!misc_pdev || !platform_get_drvdata(misc_pdev))
138f40017e0SStefan Agner 		return ERR_PTR(-EPROBE_DEFER);
139f40017e0SStefan Agner 
140f40017e0SStefan Agner 	data->dev = &misc_pdev->dev;
141f40017e0SStefan Agner 
1428e22978cSAlexander Shishkin 	if (of_find_property(np, "disable-over-current", NULL))
14305986ba9SSascha Hauer 		data->disable_oc = 1;
1448e22978cSAlexander Shishkin 
1459dba516eSLi Jun 	if (of_find_property(np, "over-current-active-high", NULL))
1469dba516eSLi Jun 		data->oc_polarity = 1;
1479dba516eSLi Jun 
1488e22978cSAlexander Shishkin 	if (of_find_property(np, "external-vbus-divider", NULL))
14905986ba9SSascha Hauer 		data->evdo = 1;
1508e22978cSAlexander Shishkin 
151d13631bbSFabien Lahoudere 	if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
152d13631bbSFabien Lahoudere 		data->ulpi = 1;
153d13631bbSFabien Lahoudere 
15405986ba9SSascha Hauer 	return data;
1558e22978cSAlexander Shishkin }
1568e22978cSAlexander Shishkin 
1578e22978cSAlexander Shishkin /* End of common functions shared by usbmisc drivers*/
158ae3e57aeSPeter Chen static int imx_get_clks(struct device *dev)
159ae3e57aeSPeter Chen {
160ae3e57aeSPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
161ae3e57aeSPeter Chen 	int ret = 0;
162ae3e57aeSPeter Chen 
163ae3e57aeSPeter Chen 	data->clk_ipg = devm_clk_get(dev, "ipg");
164ae3e57aeSPeter Chen 	if (IS_ERR(data->clk_ipg)) {
165ae3e57aeSPeter Chen 		/* If the platform only needs one clocks */
166ae3e57aeSPeter Chen 		data->clk = devm_clk_get(dev, NULL);
167ae3e57aeSPeter Chen 		if (IS_ERR(data->clk)) {
168ae3e57aeSPeter Chen 			ret = PTR_ERR(data->clk);
169ae3e57aeSPeter Chen 			dev_err(dev,
170ae3e57aeSPeter Chen 				"Failed to get clks, err=%ld,%ld\n",
171ae3e57aeSPeter Chen 				PTR_ERR(data->clk), PTR_ERR(data->clk_ipg));
172ae3e57aeSPeter Chen 			return ret;
173ae3e57aeSPeter Chen 		}
174ae3e57aeSPeter Chen 		return ret;
175ae3e57aeSPeter Chen 	}
176ae3e57aeSPeter Chen 
177ae3e57aeSPeter Chen 	data->clk_ahb = devm_clk_get(dev, "ahb");
178ae3e57aeSPeter Chen 	if (IS_ERR(data->clk_ahb)) {
179ae3e57aeSPeter Chen 		ret = PTR_ERR(data->clk_ahb);
180ae3e57aeSPeter Chen 		dev_err(dev,
181ae3e57aeSPeter Chen 			"Failed to get ahb clock, err=%d\n", ret);
182ae3e57aeSPeter Chen 		return ret;
183ae3e57aeSPeter Chen 	}
184ae3e57aeSPeter Chen 
185ae3e57aeSPeter Chen 	data->clk_per = devm_clk_get(dev, "per");
186ae3e57aeSPeter Chen 	if (IS_ERR(data->clk_per)) {
187ae3e57aeSPeter Chen 		ret = PTR_ERR(data->clk_per);
188ae3e57aeSPeter Chen 		dev_err(dev,
189ae3e57aeSPeter Chen 			"Failed to get per clock, err=%d\n", ret);
190ae3e57aeSPeter Chen 		return ret;
191ae3e57aeSPeter Chen 	}
192ae3e57aeSPeter Chen 
193ae3e57aeSPeter Chen 	data->need_three_clks = true;
194ae3e57aeSPeter Chen 	return ret;
195ae3e57aeSPeter Chen }
196ae3e57aeSPeter Chen 
197ae3e57aeSPeter Chen static int imx_prepare_enable_clks(struct device *dev)
198ae3e57aeSPeter Chen {
199ae3e57aeSPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
200ae3e57aeSPeter Chen 	int ret = 0;
201ae3e57aeSPeter Chen 
202ae3e57aeSPeter Chen 	if (data->need_three_clks) {
203ae3e57aeSPeter Chen 		ret = clk_prepare_enable(data->clk_ipg);
204ae3e57aeSPeter Chen 		if (ret) {
205ae3e57aeSPeter Chen 			dev_err(dev,
206ae3e57aeSPeter Chen 				"Failed to prepare/enable ipg clk, err=%d\n",
207ae3e57aeSPeter Chen 				ret);
208ae3e57aeSPeter Chen 			return ret;
209ae3e57aeSPeter Chen 		}
210ae3e57aeSPeter Chen 
211ae3e57aeSPeter Chen 		ret = clk_prepare_enable(data->clk_ahb);
212ae3e57aeSPeter Chen 		if (ret) {
213ae3e57aeSPeter Chen 			dev_err(dev,
214ae3e57aeSPeter Chen 				"Failed to prepare/enable ahb clk, err=%d\n",
215ae3e57aeSPeter Chen 				ret);
216ae3e57aeSPeter Chen 			clk_disable_unprepare(data->clk_ipg);
217ae3e57aeSPeter Chen 			return ret;
218ae3e57aeSPeter Chen 		}
219ae3e57aeSPeter Chen 
220ae3e57aeSPeter Chen 		ret = clk_prepare_enable(data->clk_per);
221ae3e57aeSPeter Chen 		if (ret) {
222ae3e57aeSPeter Chen 			dev_err(dev,
223ae3e57aeSPeter Chen 				"Failed to prepare/enable per clk, err=%d\n",
224ae3e57aeSPeter Chen 				ret);
225ae3e57aeSPeter Chen 			clk_disable_unprepare(data->clk_ahb);
226ae3e57aeSPeter Chen 			clk_disable_unprepare(data->clk_ipg);
227ae3e57aeSPeter Chen 			return ret;
228ae3e57aeSPeter Chen 		}
229ae3e57aeSPeter Chen 	} else {
230ae3e57aeSPeter Chen 		ret = clk_prepare_enable(data->clk);
231ae3e57aeSPeter Chen 		if (ret) {
232ae3e57aeSPeter Chen 			dev_err(dev,
233ae3e57aeSPeter Chen 				"Failed to prepare/enable clk, err=%d\n",
234ae3e57aeSPeter Chen 				ret);
235ae3e57aeSPeter Chen 			return ret;
236ae3e57aeSPeter Chen 		}
237ae3e57aeSPeter Chen 	}
238ae3e57aeSPeter Chen 
239ae3e57aeSPeter Chen 	return ret;
240ae3e57aeSPeter Chen }
241ae3e57aeSPeter Chen 
242ae3e57aeSPeter Chen static void imx_disable_unprepare_clks(struct device *dev)
243ae3e57aeSPeter Chen {
244ae3e57aeSPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
245ae3e57aeSPeter Chen 
246ae3e57aeSPeter Chen 	if (data->need_three_clks) {
247ae3e57aeSPeter Chen 		clk_disable_unprepare(data->clk_per);
248ae3e57aeSPeter Chen 		clk_disable_unprepare(data->clk_ahb);
249ae3e57aeSPeter Chen 		clk_disable_unprepare(data->clk_ipg);
250ae3e57aeSPeter Chen 	} else {
251ae3e57aeSPeter Chen 		clk_disable_unprepare(data->clk);
252ae3e57aeSPeter Chen 	}
253ae3e57aeSPeter Chen }
2548e22978cSAlexander Shishkin 
2558e22978cSAlexander Shishkin static int ci_hdrc_imx_probe(struct platform_device *pdev)
2568e22978cSAlexander Shishkin {
2578e22978cSAlexander Shishkin 	struct ci_hdrc_imx_data *data;
2588e22978cSAlexander Shishkin 	struct ci_hdrc_platform_data pdata = {
259c844d6c8SAlexander Shiyan 		.name		= dev_name(&pdev->dev),
2608e22978cSAlexander Shishkin 		.capoffset	= DEF_CAPOFFSET,
2618e22978cSAlexander Shishkin 	};
2628e22978cSAlexander Shishkin 	int ret;
2636f51bc34SLABBE Corentin 	const struct of_device_id *of_id;
2646f51bc34SLABBE Corentin 	const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
2656f51bc34SLABBE Corentin 
2666f51bc34SLABBE Corentin 	of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
2676f51bc34SLABBE Corentin 	if (!of_id)
2686f51bc34SLABBE Corentin 		return -ENODEV;
2696f51bc34SLABBE Corentin 
2706f51bc34SLABBE Corentin 	imx_platform_flag = of_id->data;
2718e22978cSAlexander Shishkin 
2728e22978cSAlexander Shishkin 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
27373529828SFabio Estevam 	if (!data)
2748e22978cSAlexander Shishkin 		return -ENOMEM;
2758e22978cSAlexander Shishkin 
276ae3e57aeSPeter Chen 	platform_set_drvdata(pdev, data);
27705986ba9SSascha Hauer 	data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
27805986ba9SSascha Hauer 	if (IS_ERR(data->usbmisc_data))
27905986ba9SSascha Hauer 		return PTR_ERR(data->usbmisc_data);
28005986ba9SSascha Hauer 
281ae3e57aeSPeter Chen 	ret = imx_get_clks(&pdev->dev);
282ae3e57aeSPeter Chen 	if (ret)
2838e22978cSAlexander Shishkin 		return ret;
284ae3e57aeSPeter Chen 
285ae3e57aeSPeter Chen 	ret = imx_prepare_enable_clks(&pdev->dev);
286ae3e57aeSPeter Chen 	if (ret)
287ae3e57aeSPeter Chen 		return ret;
2888e22978cSAlexander Shishkin 
289046916deSFabio Estevam 	data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
290af59a8b1SPeter Chen 	if (IS_ERR(data->phy)) {
291af59a8b1SPeter Chen 		ret = PTR_ERR(data->phy);
29216853d7bSMarkus Pargmann 		/* Return -EINVAL if no usbphy is available */
29316853d7bSMarkus Pargmann 		if (ret == -ENODEV)
29416853d7bSMarkus Pargmann 			ret = -EINVAL;
2958e22978cSAlexander Shishkin 		goto err_clk;
2968e22978cSAlexander Shishkin 	}
2978e22978cSAlexander Shishkin 
298ef44cb42SAntoine Tenart 	pdata.usb_phy = data->phy;
29956040087SPeter Chen 	pdata.flags |= imx_platform_flag->flags;
300e14db48dSPeter Chen 	if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
301e14db48dSPeter Chen 		data->supports_runtime_pm = true;
3021071055eSPeter Chen 
30305986ba9SSascha Hauer 	ret = imx_usbmisc_init(data->usbmisc_data);
3048e22978cSAlexander Shishkin 	if (ret) {
305a4cf1b14SPeter Chen 		dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
306af59a8b1SPeter Chen 		goto err_clk;
3078e22978cSAlexander Shishkin 	}
3088e22978cSAlexander Shishkin 
3098e22978cSAlexander Shishkin 	data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
3108e22978cSAlexander Shishkin 				pdev->resource, pdev->num_resources,
3118e22978cSAlexander Shishkin 				&pdata);
3128e22978cSAlexander Shishkin 	if (IS_ERR(data->ci_pdev)) {
3138e22978cSAlexander Shishkin 		ret = PTR_ERR(data->ci_pdev);
314d3d8425aSStefan Agner 		if (ret != -EPROBE_DEFER)
3158e22978cSAlexander Shishkin 			dev_err(&pdev->dev,
316d3d8425aSStefan Agner 				"ci_hdrc_add_device failed, err=%d\n", ret);
317af59a8b1SPeter Chen 		goto err_clk;
3188e22978cSAlexander Shishkin 	}
3198e22978cSAlexander Shishkin 
32005986ba9SSascha Hauer 	ret = imx_usbmisc_init_post(data->usbmisc_data);
3218e22978cSAlexander Shishkin 	if (ret) {
322a4cf1b14SPeter Chen 		dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
3238e22978cSAlexander Shishkin 		goto disable_device;
3248e22978cSAlexander Shishkin 	}
3258e22978cSAlexander Shishkin 
326e14db48dSPeter Chen 	if (data->supports_runtime_pm) {
327e14db48dSPeter Chen 		pm_runtime_set_active(&pdev->dev);
3288e22978cSAlexander Shishkin 		pm_runtime_enable(&pdev->dev);
329e14db48dSPeter Chen 	}
3308e22978cSAlexander Shishkin 
3316d653110SPeter Chen 	device_set_wakeup_capable(&pdev->dev, true);
3326d653110SPeter Chen 
3338e22978cSAlexander Shishkin 	return 0;
3348e22978cSAlexander Shishkin 
3358e22978cSAlexander Shishkin disable_device:
3368e22978cSAlexander Shishkin 	ci_hdrc_remove_device(data->ci_pdev);
3378e22978cSAlexander Shishkin err_clk:
338ae3e57aeSPeter Chen 	imx_disable_unprepare_clks(&pdev->dev);
3398e22978cSAlexander Shishkin 	return ret;
3408e22978cSAlexander Shishkin }
3418e22978cSAlexander Shishkin 
3428e22978cSAlexander Shishkin static int ci_hdrc_imx_remove(struct platform_device *pdev)
3438e22978cSAlexander Shishkin {
3448e22978cSAlexander Shishkin 	struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
3458e22978cSAlexander Shishkin 
346e14db48dSPeter Chen 	if (data->supports_runtime_pm) {
347e14db48dSPeter Chen 		pm_runtime_get_sync(&pdev->dev);
3488e22978cSAlexander Shishkin 		pm_runtime_disable(&pdev->dev);
349e14db48dSPeter Chen 		pm_runtime_put_noidle(&pdev->dev);
350e14db48dSPeter Chen 	}
3518e22978cSAlexander Shishkin 	ci_hdrc_remove_device(data->ci_pdev);
352ae3e57aeSPeter Chen 	imx_disable_unprepare_clks(&pdev->dev);
3538e22978cSAlexander Shishkin 
3548e22978cSAlexander Shishkin 	return 0;
3558e22978cSAlexander Shishkin }
3568e22978cSAlexander Shishkin 
357b09b5224SAndreas Fenkart static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
358b09b5224SAndreas Fenkart {
359b09b5224SAndreas Fenkart 	ci_hdrc_imx_remove(pdev);
360b09b5224SAndreas Fenkart }
361b09b5224SAndreas Fenkart 
362e14db48dSPeter Chen #ifdef CONFIG_PM
3632558c1f5SPeter Chen static int imx_controller_suspend(struct device *dev)
3642558c1f5SPeter Chen {
3652558c1f5SPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
3662558c1f5SPeter Chen 
3672558c1f5SPeter Chen 	dev_dbg(dev, "at %s\n", __func__);
3682558c1f5SPeter Chen 
369ae3e57aeSPeter Chen 	imx_disable_unprepare_clks(dev);
370e14db48dSPeter Chen 	data->in_lpm = true;
3712558c1f5SPeter Chen 
3722558c1f5SPeter Chen 	return 0;
3732558c1f5SPeter Chen }
3742558c1f5SPeter Chen 
3752558c1f5SPeter Chen static int imx_controller_resume(struct device *dev)
3762558c1f5SPeter Chen {
3772558c1f5SPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
378e14db48dSPeter Chen 	int ret = 0;
3792558c1f5SPeter Chen 
3802558c1f5SPeter Chen 	dev_dbg(dev, "at %s\n", __func__);
3812558c1f5SPeter Chen 
382e14db48dSPeter Chen 	if (!data->in_lpm) {
383e14db48dSPeter Chen 		WARN_ON(1);
384e14db48dSPeter Chen 		return 0;
3852558c1f5SPeter Chen 	}
3862558c1f5SPeter Chen 
387ae3e57aeSPeter Chen 	ret = imx_prepare_enable_clks(dev);
388e14db48dSPeter Chen 	if (ret)
389e14db48dSPeter Chen 		return ret;
390e14db48dSPeter Chen 
391e14db48dSPeter Chen 	data->in_lpm = false;
392e14db48dSPeter Chen 
393e14db48dSPeter Chen 	ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false);
394e14db48dSPeter Chen 	if (ret) {
395e14db48dSPeter Chen 		dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
396e14db48dSPeter Chen 		goto clk_disable;
397e14db48dSPeter Chen 	}
398e14db48dSPeter Chen 
399e14db48dSPeter Chen 	return 0;
400e14db48dSPeter Chen 
401e14db48dSPeter Chen clk_disable:
402ae3e57aeSPeter Chen 	imx_disable_unprepare_clks(dev);
403e14db48dSPeter Chen 	return ret;
404e14db48dSPeter Chen }
405e14db48dSPeter Chen 
406e14db48dSPeter Chen #ifdef CONFIG_PM_SLEEP
4072558c1f5SPeter Chen static int ci_hdrc_imx_suspend(struct device *dev)
4082558c1f5SPeter Chen {
4096d653110SPeter Chen 	int ret;
4106d653110SPeter Chen 
411e14db48dSPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
412e14db48dSPeter Chen 
413e14db48dSPeter Chen 	if (data->in_lpm)
414e14db48dSPeter Chen 		/* The core's suspend doesn't run */
415e14db48dSPeter Chen 		return 0;
416e14db48dSPeter Chen 
4176d653110SPeter Chen 	if (device_may_wakeup(dev)) {
4186d653110SPeter Chen 		ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
4196d653110SPeter Chen 		if (ret) {
4206d653110SPeter Chen 			dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n",
4216d653110SPeter Chen 					ret);
4226d653110SPeter Chen 			return ret;
4236d653110SPeter Chen 		}
4246d653110SPeter Chen 	}
4256d653110SPeter Chen 
4262558c1f5SPeter Chen 	return imx_controller_suspend(dev);
4272558c1f5SPeter Chen }
4282558c1f5SPeter Chen 
4292558c1f5SPeter Chen static int ci_hdrc_imx_resume(struct device *dev)
4302558c1f5SPeter Chen {
431e14db48dSPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
432e14db48dSPeter Chen 	int ret;
433e14db48dSPeter Chen 
434e14db48dSPeter Chen 	ret = imx_controller_resume(dev);
435e14db48dSPeter Chen 	if (!ret && data->supports_runtime_pm) {
436e14db48dSPeter Chen 		pm_runtime_disable(dev);
437e14db48dSPeter Chen 		pm_runtime_set_active(dev);
438e14db48dSPeter Chen 		pm_runtime_enable(dev);
439e14db48dSPeter Chen 	}
440e14db48dSPeter Chen 
441e14db48dSPeter Chen 	return ret;
4422558c1f5SPeter Chen }
4432558c1f5SPeter Chen #endif /* CONFIG_PM_SLEEP */
4442558c1f5SPeter Chen 
445e14db48dSPeter Chen static int ci_hdrc_imx_runtime_suspend(struct device *dev)
446e14db48dSPeter Chen {
447e14db48dSPeter Chen 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
448e14db48dSPeter Chen 	int ret;
449e14db48dSPeter Chen 
450e14db48dSPeter Chen 	if (data->in_lpm) {
451e14db48dSPeter Chen 		WARN_ON(1);
452e14db48dSPeter Chen 		return 0;
453e14db48dSPeter Chen 	}
454e14db48dSPeter Chen 
455e14db48dSPeter Chen 	ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
456e14db48dSPeter Chen 	if (ret) {
457e14db48dSPeter Chen 		dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
458e14db48dSPeter Chen 		return ret;
459e14db48dSPeter Chen 	}
460e14db48dSPeter Chen 
461e14db48dSPeter Chen 	return imx_controller_suspend(dev);
462e14db48dSPeter Chen }
463e14db48dSPeter Chen 
464e14db48dSPeter Chen static int ci_hdrc_imx_runtime_resume(struct device *dev)
465e14db48dSPeter Chen {
466e14db48dSPeter Chen 	return imx_controller_resume(dev);
467e14db48dSPeter Chen }
468e14db48dSPeter Chen 
469e14db48dSPeter Chen #endif /* CONFIG_PM */
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