Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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/usb/of.h>
 22#include <linux/clk.h>
 23
 24#include "ci.h"
 25#include "ci_hdrc_imx.h"
 26
 27struct ci_hdrc_imx_platform_flag {
 28	unsigned int flags;
 29	bool runtime_pm;
 30};
 31
 32static const struct ci_hdrc_imx_platform_flag imx23_usb_data = {
 33	.flags = CI_HDRC_TURN_VBUS_EARLY_ON |
 34		CI_HDRC_DISABLE_STREAMING,
 35};
 36
 37static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
 38		CI_HDRC_DISABLE_STREAMING,
 39};
 40
 41static const struct ci_hdrc_imx_platform_flag imx28_usb_data = {
 42	.flags = CI_HDRC_IMX28_WRITE_FIX |
 43		CI_HDRC_TURN_VBUS_EARLY_ON |
 44		CI_HDRC_DISABLE_STREAMING,
 45};
 46
 47static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = {
 48	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
 49		CI_HDRC_TURN_VBUS_EARLY_ON |
 50		CI_HDRC_DISABLE_STREAMING,
 51};
 52
 53static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = {
 54	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
 55		CI_HDRC_TURN_VBUS_EARLY_ON |
 56		CI_HDRC_DISABLE_HOST_STREAMING,
 57};
 58
 59static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = {
 60	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
 61		CI_HDRC_TURN_VBUS_EARLY_ON |
 62		CI_HDRC_DISABLE_HOST_STREAMING,
 63};
 64
 65static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = {
 66	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
 67		CI_HDRC_TURN_VBUS_EARLY_ON,
 68};
 69
 70static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
 71	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM,
 72};
 73
 74static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
 75	{ .compatible = "fsl,imx23-usb", .data = &imx23_usb_data},
 76	{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
 77	{ .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
 78	{ .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
 79	{ .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
 80	{ .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data},
 81	{ .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data},
 82	{ .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
 83	{ /* sentinel */ }
 84};
 85MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
 86
 87struct ci_hdrc_imx_data {
 88	struct usb_phy *phy;
 89	struct platform_device *ci_pdev;
 90	struct clk *clk;
 91	struct imx_usbmisc_data *usbmisc_data;
 92	bool supports_runtime_pm;
 93	bool in_lpm;
 94	/* SoC before i.mx6 (except imx23/imx28) needs three clks */
 95	bool need_three_clks;
 96	struct clk *clk_ipg;
 97	struct clk *clk_ahb;
 98	struct clk *clk_per;
 99	/* --------------------------------- */
100};
101
102/* Common functions shared by usbmisc drivers */
103
104static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
105{
106	struct platform_device *misc_pdev;
107	struct device_node *np = dev->of_node;
108	struct of_phandle_args args;
109	struct imx_usbmisc_data *data;
110	int ret;
111
112	/*
113	 * In case the fsl,usbmisc property is not present this device doesn't
114	 * need usbmisc. Return NULL (which is no error here)
115	 */
116	if (!of_get_property(np, "fsl,usbmisc", NULL))
117		return NULL;
118
119	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
120	if (!data)
121		return ERR_PTR(-ENOMEM);
122
123	ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
124					0, &args);
125	if (ret) {
126		dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
127			ret);
128		return ERR_PTR(ret);
129	}
130
131	data->index = args.args[0];
132
133	misc_pdev = of_find_device_by_node(args.np);
134	of_node_put(args.np);
135
136	if (!misc_pdev || !platform_get_drvdata(misc_pdev))
137		return ERR_PTR(-EPROBE_DEFER);
138
139	data->dev = &misc_pdev->dev;
140
141	if (of_find_property(np, "disable-over-current", NULL))
142		data->disable_oc = 1;
143
144	if (of_find_property(np, "over-current-active-high", NULL))
145		data->oc_polarity = 1;
146
147	if (of_find_property(np, "external-vbus-divider", NULL))
148		data->evdo = 1;
149
150	if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
151		data->ulpi = 1;
152
153	return data;
154}
155
156/* End of common functions shared by usbmisc drivers*/
157static int imx_get_clks(struct device *dev)
158{
159	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
160	int ret = 0;
161
162	data->clk_ipg = devm_clk_get(dev, "ipg");
163	if (IS_ERR(data->clk_ipg)) {
164		/* If the platform only needs one clocks */
165		data->clk = devm_clk_get(dev, NULL);
166		if (IS_ERR(data->clk)) {
167			ret = PTR_ERR(data->clk);
168			dev_err(dev,
169				"Failed to get clks, err=%ld,%ld\n",
170				PTR_ERR(data->clk), PTR_ERR(data->clk_ipg));
171			return ret;
172		}
173		return ret;
174	}
175
176	data->clk_ahb = devm_clk_get(dev, "ahb");
177	if (IS_ERR(data->clk_ahb)) {
178		ret = PTR_ERR(data->clk_ahb);
179		dev_err(dev,
180			"Failed to get ahb clock, err=%d\n", ret);
181		return ret;
182	}
183
184	data->clk_per = devm_clk_get(dev, "per");
185	if (IS_ERR(data->clk_per)) {
186		ret = PTR_ERR(data->clk_per);
187		dev_err(dev,
188			"Failed to get per clock, err=%d\n", ret);
189		return ret;
190	}
191
192	data->need_three_clks = true;
193	return ret;
194}
195
196static int imx_prepare_enable_clks(struct device *dev)
197{
198	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
199	int ret = 0;
200
201	if (data->need_three_clks) {
202		ret = clk_prepare_enable(data->clk_ipg);
203		if (ret) {
204			dev_err(dev,
205				"Failed to prepare/enable ipg clk, err=%d\n",
206				ret);
207			return ret;
208		}
209
210		ret = clk_prepare_enable(data->clk_ahb);
211		if (ret) {
212			dev_err(dev,
213				"Failed to prepare/enable ahb clk, err=%d\n",
214				ret);
215			clk_disable_unprepare(data->clk_ipg);
216			return ret;
217		}
218
219		ret = clk_prepare_enable(data->clk_per);
220		if (ret) {
221			dev_err(dev,
222				"Failed to prepare/enable per clk, err=%d\n",
223				ret);
224			clk_disable_unprepare(data->clk_ahb);
225			clk_disable_unprepare(data->clk_ipg);
226			return ret;
227		}
228	} else {
229		ret = clk_prepare_enable(data->clk);
230		if (ret) {
231			dev_err(dev,
232				"Failed to prepare/enable clk, err=%d\n",
233				ret);
234			return ret;
235		}
236	}
237
238	return ret;
239}
240
241static void imx_disable_unprepare_clks(struct device *dev)
242{
243	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
244
245	if (data->need_three_clks) {
246		clk_disable_unprepare(data->clk_per);
247		clk_disable_unprepare(data->clk_ahb);
248		clk_disable_unprepare(data->clk_ipg);
249	} else {
250		clk_disable_unprepare(data->clk);
251	}
252}
253
254static int ci_hdrc_imx_probe(struct platform_device *pdev)
255{
256	struct ci_hdrc_imx_data *data;
257	struct ci_hdrc_platform_data pdata = {
258		.name		= dev_name(&pdev->dev),
259		.capoffset	= DEF_CAPOFFSET,
260	};
261	int ret;
262	const struct of_device_id *of_id;
263	const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
264
265	of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
266	if (!of_id)
267		return -ENODEV;
268
269	imx_platform_flag = of_id->data;
270
271	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
272	if (!data)
273		return -ENOMEM;
274
275	platform_set_drvdata(pdev, data);
276	data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
277	if (IS_ERR(data->usbmisc_data))
278		return PTR_ERR(data->usbmisc_data);
279
280	ret = imx_get_clks(&pdev->dev);
281	if (ret)
282		return ret;
283
284	ret = imx_prepare_enable_clks(&pdev->dev);
285	if (ret)
286		return ret;
287
288	data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
289	if (IS_ERR(data->phy)) {
290		ret = PTR_ERR(data->phy);
291		/* Return -EINVAL if no usbphy is available */
292		if (ret == -ENODEV)
293			ret = -EINVAL;
294		goto err_clk;
295	}
296
297	pdata.usb_phy = data->phy;
298	pdata.flags |= imx_platform_flag->flags;
299	if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
300		data->supports_runtime_pm = true;
301
302	ret = imx_usbmisc_init(data->usbmisc_data);
303	if (ret) {
304		dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
305		goto err_clk;
306	}
307
308	data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
309				pdev->resource, pdev->num_resources,
310				&pdata);
311	if (IS_ERR(data->ci_pdev)) {
312		ret = PTR_ERR(data->ci_pdev);
313		if (ret != -EPROBE_DEFER)
314			dev_err(&pdev->dev,
315				"ci_hdrc_add_device failed, err=%d\n", ret);
316		goto err_clk;
317	}
318
319	ret = imx_usbmisc_init_post(data->usbmisc_data);
320	if (ret) {
321		dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
322		goto disable_device;
323	}
324
325	if (data->supports_runtime_pm) {
326		pm_runtime_set_active(&pdev->dev);
327		pm_runtime_enable(&pdev->dev);
328	}
329
330	device_set_wakeup_capable(&pdev->dev, true);
331
332	return 0;
333
334disable_device:
335	ci_hdrc_remove_device(data->ci_pdev);
336err_clk:
337	imx_disable_unprepare_clks(&pdev->dev);
338	return ret;
339}
340
341static int ci_hdrc_imx_remove(struct platform_device *pdev)
342{
343	struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
344
345	if (data->supports_runtime_pm) {
346		pm_runtime_get_sync(&pdev->dev);
347		pm_runtime_disable(&pdev->dev);
348		pm_runtime_put_noidle(&pdev->dev);
349	}
350	ci_hdrc_remove_device(data->ci_pdev);
351	imx_disable_unprepare_clks(&pdev->dev);
352
353	return 0;
354}
355
356static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
357{
358	ci_hdrc_imx_remove(pdev);
359}
360
361#ifdef CONFIG_PM
362static int imx_controller_suspend(struct device *dev)
363{
364	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
365
366	dev_dbg(dev, "at %s\n", __func__);
367
368	imx_disable_unprepare_clks(dev);
369	data->in_lpm = true;
370
371	return 0;
372}
373
374static int imx_controller_resume(struct device *dev)
375{
376	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
377	int ret = 0;
378
379	dev_dbg(dev, "at %s\n", __func__);
380
381	if (!data->in_lpm) {
382		WARN_ON(1);
383		return 0;
384	}
385
386	ret = imx_prepare_enable_clks(dev);
387	if (ret)
388		return ret;
389
390	data->in_lpm = false;
391
392	ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false);
393	if (ret) {
394		dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
395		goto clk_disable;
396	}
397
398	return 0;
399
400clk_disable:
401	imx_disable_unprepare_clks(dev);
402	return ret;
403}
404
405#ifdef CONFIG_PM_SLEEP
406static int ci_hdrc_imx_suspend(struct device *dev)
407{
408	int ret;
409
410	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
411
412	if (data->in_lpm)
413		/* The core's suspend doesn't run */
414		return 0;
415
416	if (device_may_wakeup(dev)) {
417		ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
418		if (ret) {
419			dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n",
420					ret);
421			return ret;
422		}
423	}
424
425	return imx_controller_suspend(dev);
426}
427
428static int ci_hdrc_imx_resume(struct device *dev)
429{
430	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
431	int ret;
432
433	ret = imx_controller_resume(dev);
434	if (!ret && data->supports_runtime_pm) {
435		pm_runtime_disable(dev);
436		pm_runtime_set_active(dev);
437		pm_runtime_enable(dev);
438	}
439
440	return ret;
441}
442#endif /* CONFIG_PM_SLEEP */
443
444static int ci_hdrc_imx_runtime_suspend(struct device *dev)
445{
446	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
447	int ret;
448
449	if (data->in_lpm) {
450		WARN_ON(1);
451		return 0;
452	}
453
454	ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
455	if (ret) {
456		dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
457		return ret;
458	}
459
460	return imx_controller_suspend(dev);
461}
462
463static int ci_hdrc_imx_runtime_resume(struct device *dev)
464{
465	return imx_controller_resume(dev);
466}
467
468#endif /* CONFIG_PM */
469
470static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
471	SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
472	SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
473			ci_hdrc_imx_runtime_resume, NULL)
474};
475static struct platform_driver ci_hdrc_imx_driver = {
476	.probe = ci_hdrc_imx_probe,
477	.remove = ci_hdrc_imx_remove,
478	.shutdown = ci_hdrc_imx_shutdown,
479	.driver = {
480		.name = "imx_usb",
481		.of_match_table = ci_hdrc_imx_dt_ids,
482		.pm = &ci_hdrc_imx_pm_ops,
483	 },
484};
485
486module_platform_driver(ci_hdrc_imx_driver);
487
488MODULE_ALIAS("platform:imx-usb");
489MODULE_LICENSE("GPL v2");
490MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
491MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
492MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");