Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Copyright 2012 Freescale Semiconductor, Inc.
  3 *
  4 * The code contained herein is licensed under the GNU General Public
  5 * License. You may obtain a copy of the GNU General Public License
  6 * Version 2 or later at the following locations:
  7 *
  8 * http://www.opensource.org/licenses/gpl-license.html
  9 * http://www.gnu.org/copyleft/gpl.html
 10 */
 11
 12#include <linux/module.h>
 13#include <linux/of_platform.h>
 14#include <linux/clk.h>
 15#include <linux/err.h>
 16#include <linux/io.h>
 17#include <linux/delay.h>
 18
 19#include "ci_hdrc_imx.h"
 20
 21#define MX25_USB_PHY_CTRL_OFFSET	0x08
 22#define MX25_BM_EXTERNAL_VBUS_DIVIDER	BIT(23)
 23
 24#define MX27_H1_PM_BIT			BIT(8)
 25#define MX27_H2_PM_BIT			BIT(16)
 26#define MX27_OTG_PM_BIT			BIT(24)
 27
 28#define MX53_USB_OTG_PHY_CTRL_0_OFFSET	0x08
 29#define MX53_USB_UH2_CTRL_OFFSET	0x14
 30#define MX53_USB_UH3_CTRL_OFFSET	0x18
 31#define MX53_BM_OVER_CUR_DIS_H1		BIT(5)
 32#define MX53_BM_OVER_CUR_DIS_OTG	BIT(8)
 33#define MX53_BM_OVER_CUR_DIS_UHx	BIT(30)
 34
 35#define MX6_BM_OVER_CUR_DIS		BIT(7)
 36
 37struct usbmisc_ops {
 38	/* It's called once when probe a usb device */
 39	int (*init)(struct imx_usbmisc_data *data);
 40	/* It's called once after adding a usb device */
 41	int (*post)(struct imx_usbmisc_data *data);
 42};
 43
 44struct imx_usbmisc {
 45	void __iomem *base;
 46	spinlock_t lock;
 47	struct clk *clk;
 48	const struct usbmisc_ops *ops;
 49};
 50
 51static struct imx_usbmisc *usbmisc;
 52
 53static int usbmisc_imx25_post(struct imx_usbmisc_data *data)
 54{
 55	void __iomem *reg;
 56	unsigned long flags;
 57	u32 val;
 58
 59	if (data->index > 2)
 60		return -EINVAL;
 61
 62	reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
 63
 64	if (data->evdo) {
 65		spin_lock_irqsave(&usbmisc->lock, flags);
 66		val = readl(reg);
 67		writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg);
 68		spin_unlock_irqrestore(&usbmisc->lock, flags);
 69		usleep_range(5000, 10000); /* needed to stabilize voltage */
 70	}
 71
 72	return 0;
 73}
 74
 75static int usbmisc_imx27_init(struct imx_usbmisc_data *data)
 76{
 77	unsigned long flags;
 78	u32 val;
 79
 80	switch (data->index) {
 81	case 0:
 82		val = MX27_OTG_PM_BIT;
 83		break;
 84	case 1:
 85		val = MX27_H1_PM_BIT;
 86		break;
 87	case 2:
 88		val = MX27_H2_PM_BIT;
 89		break;
 90	default:
 91		return -EINVAL;
 92	};
 93
 94	spin_lock_irqsave(&usbmisc->lock, flags);
 95	if (data->disable_oc)
 96		val = readl(usbmisc->base) | val;
 97	else
 98		val = readl(usbmisc->base) & ~val;
 99	writel(val, usbmisc->base);
100	spin_unlock_irqrestore(&usbmisc->lock, flags);
101
102	return 0;
103}
104
105static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
106{
107	void __iomem *reg = NULL;
108	unsigned long flags;
109	u32 val = 0;
110
111	if (data->index > 3)
112		return -EINVAL;
113
114	if (data->disable_oc) {
115		spin_lock_irqsave(&usbmisc->lock, flags);
116		switch (data->index) {
117		case 0:
118			reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
119			val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG;
120			break;
121		case 1:
122			reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
123			val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1;
124			break;
125		case 2:
126			reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
127			val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
128			break;
129		case 3:
130			reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
131			val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
132			break;
133		}
134		if (reg && val)
135			writel(val, reg);
136		spin_unlock_irqrestore(&usbmisc->lock, flags);
137	}
138
139	return 0;
140}
141
142static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
143{
144	unsigned long flags;
145	u32 reg;
146
147	if (data->index > 3)
148		return -EINVAL;
149
150	if (data->disable_oc) {
151		spin_lock_irqsave(&usbmisc->lock, flags);
152		reg = readl(usbmisc->base + data->index * 4);
153		writel(reg | MX6_BM_OVER_CUR_DIS,
154			usbmisc->base + data->index * 4);
155		spin_unlock_irqrestore(&usbmisc->lock, flags);
156	}
157
158	return 0;
159}
160
161static const struct usbmisc_ops imx25_usbmisc_ops = {
162	.post = usbmisc_imx25_post,
163};
164
165static const struct usbmisc_ops imx27_usbmisc_ops = {
166	.init = usbmisc_imx27_init,
167};
168
169static const struct usbmisc_ops imx53_usbmisc_ops = {
170	.init = usbmisc_imx53_init,
171};
172
173static const struct usbmisc_ops imx6q_usbmisc_ops = {
174	.init = usbmisc_imx6q_init,
175};
176
177int imx_usbmisc_init(struct imx_usbmisc_data *data)
178{
179	if (!usbmisc)
180		return -EPROBE_DEFER;
181	if (!usbmisc->ops->init)
182		return 0;
183	return usbmisc->ops->init(data);
184}
185EXPORT_SYMBOL_GPL(imx_usbmisc_init);
186
187int imx_usbmisc_init_post(struct imx_usbmisc_data *data)
188{
189	if (!usbmisc)
190		return -EPROBE_DEFER;
191	if (!usbmisc->ops->post)
192		return 0;
193	return usbmisc->ops->post(data);
194}
195EXPORT_SYMBOL_GPL(imx_usbmisc_init_post);
196
197static const struct of_device_id usbmisc_imx_dt_ids[] = {
198	{
199		.compatible = "fsl,imx25-usbmisc",
200		.data = &imx25_usbmisc_ops,
201	},
202	{
203		.compatible = "fsl,imx27-usbmisc",
204		.data = &imx27_usbmisc_ops,
205	},
206	{
207		.compatible = "fsl,imx51-usbmisc",
208		.data = &imx53_usbmisc_ops,
209	},
210	{
211		.compatible = "fsl,imx53-usbmisc",
212		.data = &imx53_usbmisc_ops,
213	},
214	{
215		.compatible = "fsl,imx6q-usbmisc",
216		.data = &imx6q_usbmisc_ops,
217	},
218	{ /* sentinel */ }
219};
220MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
221
222static int usbmisc_imx_probe(struct platform_device *pdev)
223{
224	struct resource	*res;
225	struct imx_usbmisc *data;
226	int ret;
227	struct of_device_id *tmp_dev;
228
229	if (usbmisc)
230		return -EBUSY;
231
232	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
233	if (!data)
234		return -ENOMEM;
235
236	spin_lock_init(&data->lock);
237
238	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
239	data->base = devm_ioremap_resource(&pdev->dev, res);
240	if (IS_ERR(data->base))
241		return PTR_ERR(data->base);
242
243	data->clk = devm_clk_get(&pdev->dev, NULL);
244	if (IS_ERR(data->clk)) {
245		dev_err(&pdev->dev,
246			"failed to get clock, err=%ld\n", PTR_ERR(data->clk));
247		return PTR_ERR(data->clk);
248	}
249
250	ret = clk_prepare_enable(data->clk);
251	if (ret) {
252		dev_err(&pdev->dev,
253			"clk_prepare_enable failed, err=%d\n", ret);
254		return ret;
255	}
256
257	tmp_dev = (struct of_device_id *)
258		of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
259	data->ops = (const struct usbmisc_ops *)tmp_dev->data;
260	usbmisc = data;
261
262	return 0;
263}
264
265static int usbmisc_imx_remove(struct platform_device *pdev)
266{
267	clk_disable_unprepare(usbmisc->clk);
268	usbmisc = NULL;
269	return 0;
270}
271
272static struct platform_driver usbmisc_imx_driver = {
273	.probe = usbmisc_imx_probe,
274	.remove = usbmisc_imx_remove,
275	.driver = {
276		.name = "usbmisc_imx",
277		.owner = THIS_MODULE,
278		.of_match_table = usbmisc_imx_dt_ids,
279	 },
280};
281
282module_platform_driver(usbmisc_imx_driver);
283
284MODULE_ALIAS("platform:usbmisc-imx");
285MODULE_LICENSE("GPL v2");
286MODULE_DESCRIPTION("driver for imx usb non-core registers");
287MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");