xref: /linux/drivers/usb/chipidea/ci_hdrc_imx.c (revision 9cfc5c90ad38c8fc11bfd39de42a107da00871ba)
1 /*
2  * Copyright 2012 Freescale Semiconductor, Inc.
3  * Copyright (C) 2012 Marek Vasut <marex@denx.de>
4  * on behalf of DENX Software Engineering GmbH
5  *
6  * The code contained herein is licensed under the GNU General Public
7  * License. You may obtain a copy of the GNU General Public License
8  * Version 2 or later at the following locations:
9  *
10  * http://www.opensource.org/licenses/gpl-license.html
11  * http://www.gnu.org/copyleft/gpl.html
12  */
13 
14 #include <linux/module.h>
15 #include <linux/of_platform.h>
16 #include <linux/of_gpio.h>
17 #include <linux/platform_device.h>
18 #include <linux/pm_runtime.h>
19 #include <linux/dma-mapping.h>
20 #include <linux/usb/chipidea.h>
21 #include <linux/clk.h>
22 
23 #include "ci.h"
24 #include "ci_hdrc_imx.h"
25 
26 struct ci_hdrc_imx_platform_flag {
27 	unsigned int flags;
28 	bool runtime_pm;
29 };
30 
31 static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
32 		CI_HDRC_DISABLE_STREAMING,
33 };
34 
35 static const struct ci_hdrc_imx_platform_flag imx28_usb_data = {
36 	.flags = CI_HDRC_IMX28_WRITE_FIX |
37 		CI_HDRC_TURN_VBUS_EARLY_ON |
38 		CI_HDRC_DISABLE_STREAMING,
39 };
40 
41 static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = {
42 	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
43 		CI_HDRC_TURN_VBUS_EARLY_ON |
44 		CI_HDRC_DISABLE_STREAMING,
45 };
46 
47 static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = {
48 	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
49 		CI_HDRC_TURN_VBUS_EARLY_ON |
50 		CI_HDRC_DISABLE_HOST_STREAMING,
51 };
52 
53 static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = {
54 	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
55 		CI_HDRC_TURN_VBUS_EARLY_ON |
56 		CI_HDRC_DISABLE_HOST_STREAMING,
57 };
58 
59 static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = {
60 	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
61 		CI_HDRC_TURN_VBUS_EARLY_ON,
62 };
63 
64 static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
65 	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM,
66 };
67 
68 static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
69 	{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
70 	{ .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
71 	{ .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
72 	{ .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
73 	{ .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data},
74 	{ .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data},
75 	{ .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
76 	{ /* sentinel */ }
77 };
78 MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
79 
80 struct ci_hdrc_imx_data {
81 	struct usb_phy *phy;
82 	struct platform_device *ci_pdev;
83 	struct clk *clk;
84 	struct imx_usbmisc_data *usbmisc_data;
85 	bool supports_runtime_pm;
86 	bool in_lpm;
87 };
88 
89 /* Common functions shared by usbmisc drivers */
90 
91 static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
92 {
93 	struct platform_device *misc_pdev;
94 	struct device_node *np = dev->of_node;
95 	struct of_phandle_args args;
96 	struct imx_usbmisc_data *data;
97 	int ret;
98 
99 	/*
100 	 * In case the fsl,usbmisc property is not present this device doesn't
101 	 * need usbmisc. Return NULL (which is no error here)
102 	 */
103 	if (!of_get_property(np, "fsl,usbmisc", NULL))
104 		return NULL;
105 
106 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
107 	if (!data)
108 		return ERR_PTR(-ENOMEM);
109 
110 	ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
111 					0, &args);
112 	if (ret) {
113 		dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
114 			ret);
115 		return ERR_PTR(ret);
116 	}
117 
118 	data->index = args.args[0];
119 
120 	misc_pdev = of_find_device_by_node(args.np);
121 	of_node_put(args.np);
122 
123 	if (!misc_pdev || !platform_get_drvdata(misc_pdev))
124 		return ERR_PTR(-EPROBE_DEFER);
125 
126 	data->dev = &misc_pdev->dev;
127 
128 	if (of_find_property(np, "disable-over-current", NULL))
129 		data->disable_oc = 1;
130 
131 	if (of_find_property(np, "external-vbus-divider", NULL))
132 		data->evdo = 1;
133 
134 	return data;
135 }
136 
137 /* End of common functions shared by usbmisc drivers*/
138 
139 static int ci_hdrc_imx_probe(struct platform_device *pdev)
140 {
141 	struct ci_hdrc_imx_data *data;
142 	struct ci_hdrc_platform_data pdata = {
143 		.name		= dev_name(&pdev->dev),
144 		.capoffset	= DEF_CAPOFFSET,
145 		.flags		= CI_HDRC_SET_NON_ZERO_TTHA,
146 	};
147 	int ret;
148 	const struct of_device_id *of_id =
149 			of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
150 	const struct ci_hdrc_imx_platform_flag *imx_platform_flag = of_id->data;
151 
152 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
153 	if (!data)
154 		return -ENOMEM;
155 
156 	data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
157 	if (IS_ERR(data->usbmisc_data))
158 		return PTR_ERR(data->usbmisc_data);
159 
160 	data->clk = devm_clk_get(&pdev->dev, NULL);
161 	if (IS_ERR(data->clk)) {
162 		dev_err(&pdev->dev,
163 			"Failed to get clock, err=%ld\n", PTR_ERR(data->clk));
164 		return PTR_ERR(data->clk);
165 	}
166 
167 	ret = clk_prepare_enable(data->clk);
168 	if (ret) {
169 		dev_err(&pdev->dev,
170 			"Failed to prepare or enable clock, err=%d\n", ret);
171 		return ret;
172 	}
173 
174 	data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
175 	if (IS_ERR(data->phy)) {
176 		ret = PTR_ERR(data->phy);
177 		/* Return -EINVAL if no usbphy is available */
178 		if (ret == -ENODEV)
179 			ret = -EINVAL;
180 		goto err_clk;
181 	}
182 
183 	pdata.usb_phy = data->phy;
184 	pdata.flags |= imx_platform_flag->flags;
185 	if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
186 		data->supports_runtime_pm = true;
187 
188 	ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
189 	if (ret)
190 		goto err_clk;
191 
192 	ret = imx_usbmisc_init(data->usbmisc_data);
193 	if (ret) {
194 		dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
195 		goto err_clk;
196 	}
197 
198 	data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
199 				pdev->resource, pdev->num_resources,
200 				&pdata);
201 	if (IS_ERR(data->ci_pdev)) {
202 		ret = PTR_ERR(data->ci_pdev);
203 		dev_err(&pdev->dev,
204 			"Can't register ci_hdrc platform device, err=%d\n",
205 			ret);
206 		goto err_clk;
207 	}
208 
209 	ret = imx_usbmisc_init_post(data->usbmisc_data);
210 	if (ret) {
211 		dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
212 		goto disable_device;
213 	}
214 
215 	platform_set_drvdata(pdev, data);
216 
217 	if (data->supports_runtime_pm) {
218 		pm_runtime_set_active(&pdev->dev);
219 		pm_runtime_enable(&pdev->dev);
220 	}
221 
222 	device_set_wakeup_capable(&pdev->dev, true);
223 
224 	return 0;
225 
226 disable_device:
227 	ci_hdrc_remove_device(data->ci_pdev);
228 err_clk:
229 	clk_disable_unprepare(data->clk);
230 	return ret;
231 }
232 
233 static int ci_hdrc_imx_remove(struct platform_device *pdev)
234 {
235 	struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
236 
237 	if (data->supports_runtime_pm) {
238 		pm_runtime_get_sync(&pdev->dev);
239 		pm_runtime_disable(&pdev->dev);
240 		pm_runtime_put_noidle(&pdev->dev);
241 	}
242 	ci_hdrc_remove_device(data->ci_pdev);
243 	clk_disable_unprepare(data->clk);
244 
245 	return 0;
246 }
247 
248 #ifdef CONFIG_PM
249 static int imx_controller_suspend(struct device *dev)
250 {
251 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
252 
253 	dev_dbg(dev, "at %s\n", __func__);
254 
255 	clk_disable_unprepare(data->clk);
256 	data->in_lpm = true;
257 
258 	return 0;
259 }
260 
261 static int imx_controller_resume(struct device *dev)
262 {
263 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
264 	int ret = 0;
265 
266 	dev_dbg(dev, "at %s\n", __func__);
267 
268 	if (!data->in_lpm) {
269 		WARN_ON(1);
270 		return 0;
271 	}
272 
273 	ret = clk_prepare_enable(data->clk);
274 	if (ret)
275 		return ret;
276 
277 	data->in_lpm = false;
278 
279 	ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false);
280 	if (ret) {
281 		dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
282 		goto clk_disable;
283 	}
284 
285 	return 0;
286 
287 clk_disable:
288 	clk_disable_unprepare(data->clk);
289 	return ret;
290 }
291 
292 #ifdef CONFIG_PM_SLEEP
293 static int ci_hdrc_imx_suspend(struct device *dev)
294 {
295 	int ret;
296 
297 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
298 
299 	if (data->in_lpm)
300 		/* The core's suspend doesn't run */
301 		return 0;
302 
303 	if (device_may_wakeup(dev)) {
304 		ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
305 		if (ret) {
306 			dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n",
307 					ret);
308 			return ret;
309 		}
310 	}
311 
312 	return imx_controller_suspend(dev);
313 }
314 
315 static int ci_hdrc_imx_resume(struct device *dev)
316 {
317 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
318 	int ret;
319 
320 	ret = imx_controller_resume(dev);
321 	if (!ret && data->supports_runtime_pm) {
322 		pm_runtime_disable(dev);
323 		pm_runtime_set_active(dev);
324 		pm_runtime_enable(dev);
325 	}
326 
327 	return ret;
328 }
329 #endif /* CONFIG_PM_SLEEP */
330 
331 static int ci_hdrc_imx_runtime_suspend(struct device *dev)
332 {
333 	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
334 	int ret;
335 
336 	if (data->in_lpm) {
337 		WARN_ON(1);
338 		return 0;
339 	}
340 
341 	ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
342 	if (ret) {
343 		dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
344 		return ret;
345 	}
346 
347 	return imx_controller_suspend(dev);
348 }
349 
350 static int ci_hdrc_imx_runtime_resume(struct device *dev)
351 {
352 	return imx_controller_resume(dev);
353 }
354 
355 #endif /* CONFIG_PM */
356 
357 static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
358 	SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
359 	SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
360 			ci_hdrc_imx_runtime_resume, NULL)
361 };
362 static struct platform_driver ci_hdrc_imx_driver = {
363 	.probe = ci_hdrc_imx_probe,
364 	.remove = ci_hdrc_imx_remove,
365 	.driver = {
366 		.name = "imx_usb",
367 		.of_match_table = ci_hdrc_imx_dt_ids,
368 		.pm = &ci_hdrc_imx_pm_ops,
369 	 },
370 };
371 
372 module_platform_driver(ci_hdrc_imx_driver);
373 
374 MODULE_ALIAS("platform:imx-usb");
375 MODULE_LICENSE("GPL v2");
376 MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
377 MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
378 MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
379