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/err.h>
15#include <linux/io.h>
16#include <linux/delay.h>
17
18#include "ci_hdrc_imx.h"
19
20#define MX25_USB_PHY_CTRL_OFFSET	0x08
21#define MX25_BM_EXTERNAL_VBUS_DIVIDER	BIT(23)
22
23#define MX25_EHCI_INTERFACE_SINGLE_UNI	(2 << 0)
24#define MX25_EHCI_INTERFACE_DIFF_UNI	(0 << 0)
25#define MX25_EHCI_INTERFACE_MASK	(0xf)
26
27#define MX25_OTG_SIC_SHIFT		29
28#define MX25_OTG_SIC_MASK		(0x3 << MX25_OTG_SIC_SHIFT)
29#define MX25_OTG_PM_BIT			BIT(24)
30#define MX25_OTG_PP_BIT			BIT(11)
31#define MX25_OTG_OCPOL_BIT		BIT(3)
32
33#define MX25_H1_SIC_SHIFT		21
34#define MX25_H1_SIC_MASK		(0x3 << MX25_H1_SIC_SHIFT)
35#define MX25_H1_PP_BIT			BIT(18)
36#define MX25_H1_PM_BIT			BIT(16)
37#define MX25_H1_IPPUE_UP_BIT		BIT(7)
38#define MX25_H1_IPPUE_DOWN_BIT		BIT(6)
39#define MX25_H1_TLL_BIT			BIT(5)
40#define MX25_H1_USBTE_BIT		BIT(4)
41#define MX25_H1_OCPOL_BIT		BIT(2)
42
43#define MX27_H1_PM_BIT			BIT(8)
44#define MX27_H2_PM_BIT			BIT(16)
45#define MX27_OTG_PM_BIT			BIT(24)
46
47#define MX53_USB_OTG_PHY_CTRL_0_OFFSET	0x08
48#define MX53_USB_OTG_PHY_CTRL_1_OFFSET	0x0c
49#define MX53_USB_UH2_CTRL_OFFSET	0x14
50#define MX53_USB_UH3_CTRL_OFFSET	0x18
51#define MX53_BM_OVER_CUR_DIS_H1		BIT(5)
52#define MX53_BM_OVER_CUR_DIS_OTG	BIT(8)
53#define MX53_BM_OVER_CUR_DIS_UHx	BIT(30)
54#define MX53_USB_PHYCTRL1_PLLDIV_MASK	0x3
55#define MX53_USB_PLL_DIV_24_MHZ		0x01
56
57#define MX6_BM_OVER_CUR_DIS		BIT(7)
58#define MX6_BM_WAKEUP_ENABLE		BIT(10)
59#define MX6_BM_ID_WAKEUP		BIT(16)
60#define MX6_BM_VBUS_WAKEUP		BIT(17)
61#define MX6SX_BM_DPDM_WAKEUP_EN		BIT(29)
62#define MX6_BM_WAKEUP_INTR		BIT(31)
63#define MX6_USB_OTG1_PHY_CTRL		0x18
64/* For imx6dql, it is host-only controller, for later imx6, it is otg's */
65#define MX6_USB_OTG2_PHY_CTRL		0x1c
66#define MX6SX_USB_VBUS_WAKEUP_SOURCE(v)	(v << 8)
67#define MX6SX_USB_VBUS_WAKEUP_SOURCE_VBUS	MX6SX_USB_VBUS_WAKEUP_SOURCE(0)
68#define MX6SX_USB_VBUS_WAKEUP_SOURCE_AVALID	MX6SX_USB_VBUS_WAKEUP_SOURCE(1)
69#define MX6SX_USB_VBUS_WAKEUP_SOURCE_BVALID	MX6SX_USB_VBUS_WAKEUP_SOURCE(2)
70#define MX6SX_USB_VBUS_WAKEUP_SOURCE_SESS_END	MX6SX_USB_VBUS_WAKEUP_SOURCE(3)
71
72#define VF610_OVER_CUR_DIS		BIT(7)
73
74struct usbmisc_ops {
75	/* It's called once when probe a usb device */
76	int (*init)(struct imx_usbmisc_data *data);
77	/* It's called once after adding a usb device */
78	int (*post)(struct imx_usbmisc_data *data);
79	/* It's called when we need to enable/disable usb wakeup */
80	int (*set_wakeup)(struct imx_usbmisc_data *data, bool enabled);
81};
82
83struct imx_usbmisc {
84	void __iomem *base;
85	spinlock_t lock;
86	const struct usbmisc_ops *ops;
87};
88
89static int usbmisc_imx25_init(struct imx_usbmisc_data *data)
90{
91	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
92	unsigned long flags;
93	u32 val = 0;
94
95	if (data->index > 1)
96		return -EINVAL;
97
98	spin_lock_irqsave(&usbmisc->lock, flags);
99	switch (data->index) {
100	case 0:
101		val = readl(usbmisc->base);
102		val &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PP_BIT);
103		val |= (MX25_EHCI_INTERFACE_DIFF_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT;
104		val |= (MX25_OTG_PM_BIT | MX25_OTG_OCPOL_BIT);
105		writel(val, usbmisc->base);
106		break;
107	case 1:
108		val = readl(usbmisc->base);
109		val &= ~(MX25_H1_SIC_MASK | MX25_H1_PP_BIT |  MX25_H1_IPPUE_UP_BIT);
110		val |= (MX25_EHCI_INTERFACE_SINGLE_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_H1_SIC_SHIFT;
111		val |= (MX25_H1_PM_BIT | MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT |
112			MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT);
113
114		writel(val, usbmisc->base);
115
116		break;
117	}
118	spin_unlock_irqrestore(&usbmisc->lock, flags);
119
120	return 0;
121}
122
123static int usbmisc_imx25_post(struct imx_usbmisc_data *data)
124{
125	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
126	void __iomem *reg;
127	unsigned long flags;
128	u32 val;
129
130	if (data->index > 2)
131		return -EINVAL;
132
133	if (data->evdo) {
134		spin_lock_irqsave(&usbmisc->lock, flags);
135		reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
136		val = readl(reg);
137		writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg);
138		spin_unlock_irqrestore(&usbmisc->lock, flags);
139		usleep_range(5000, 10000); /* needed to stabilize voltage */
140	}
141
142	return 0;
143}
144
145static int usbmisc_imx27_init(struct imx_usbmisc_data *data)
146{
147	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
148	unsigned long flags;
149	u32 val;
150
151	switch (data->index) {
152	case 0:
153		val = MX27_OTG_PM_BIT;
154		break;
155	case 1:
156		val = MX27_H1_PM_BIT;
157		break;
158	case 2:
159		val = MX27_H2_PM_BIT;
160		break;
161	default:
162		return -EINVAL;
163	};
164
165	spin_lock_irqsave(&usbmisc->lock, flags);
166	if (data->disable_oc)
167		val = readl(usbmisc->base) | val;
168	else
169		val = readl(usbmisc->base) & ~val;
170	writel(val, usbmisc->base);
171	spin_unlock_irqrestore(&usbmisc->lock, flags);
172
173	return 0;
174}
175
176static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
177{
178	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
179	void __iomem *reg = NULL;
180	unsigned long flags;
181	u32 val = 0;
182
183	if (data->index > 3)
184		return -EINVAL;
185
186	/* Select a 24 MHz reference clock for the PHY  */
187	val = readl(usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
188	val &= ~MX53_USB_PHYCTRL1_PLLDIV_MASK;
189	val |= MX53_USB_PLL_DIV_24_MHZ;
190	writel(val, usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
191
192	if (data->disable_oc) {
193		spin_lock_irqsave(&usbmisc->lock, flags);
194		switch (data->index) {
195		case 0:
196			reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
197			val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG;
198			break;
199		case 1:
200			reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
201			val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1;
202			break;
203		case 2:
204			reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
205			val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
206			break;
207		case 3:
208			reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
209			val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
210			break;
211		}
212		if (reg && val)
213			writel(val, reg);
214		spin_unlock_irqrestore(&usbmisc->lock, flags);
215	}
216
217	return 0;
218}
219
220static int usbmisc_imx6q_set_wakeup
221	(struct imx_usbmisc_data *data, bool enabled)
222{
223	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
224	unsigned long flags;
225	u32 val;
226	u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE |
227		MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP);
228	int ret = 0;
229
230	if (data->index > 3)
231		return -EINVAL;
232
233	spin_lock_irqsave(&usbmisc->lock, flags);
234	val = readl(usbmisc->base + data->index * 4);
235	if (enabled) {
236		val |= wakeup_setting;
237		writel(val, usbmisc->base + data->index * 4);
238	} else {
239		if (val & MX6_BM_WAKEUP_INTR)
240			pr_debug("wakeup int at ci_hdrc.%d\n", data->index);
241		val &= ~wakeup_setting;
242		writel(val, usbmisc->base + data->index * 4);
243	}
244	spin_unlock_irqrestore(&usbmisc->lock, flags);
245
246	return ret;
247}
248
249static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
250{
251	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
252	unsigned long flags;
253	u32 reg;
254
255	if (data->index > 3)
256		return -EINVAL;
257
258	if (data->disable_oc) {
259		spin_lock_irqsave(&usbmisc->lock, flags);
260		reg = readl(usbmisc->base + data->index * 4);
261		writel(reg | MX6_BM_OVER_CUR_DIS,
262			usbmisc->base + data->index * 4);
263		spin_unlock_irqrestore(&usbmisc->lock, flags);
264	}
265
266	usbmisc_imx6q_set_wakeup(data, false);
267
268	return 0;
269}
270
271static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data)
272{
273	void __iomem *reg = NULL;
274	unsigned long flags;
275	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
276	u32 val;
277
278	usbmisc_imx6q_init(data);
279
280	if (data->index == 0 || data->index == 1) {
281		reg = usbmisc->base + MX6_USB_OTG1_PHY_CTRL + data->index * 4;
282		spin_lock_irqsave(&usbmisc->lock, flags);
283		/* Set vbus wakeup source as bvalid */
284		val = readl(reg);
285		writel(val | MX6SX_USB_VBUS_WAKEUP_SOURCE_BVALID, reg);
286		/*
287		 * Disable dp/dm wakeup in device mode when vbus is
288		 * not there.
289		 */
290		val = readl(usbmisc->base + data->index * 4);
291		writel(val & ~MX6SX_BM_DPDM_WAKEUP_EN,
292			usbmisc->base + data->index * 4);
293		spin_unlock_irqrestore(&usbmisc->lock, flags);
294	}
295
296	return 0;
297}
298
299static int usbmisc_vf610_init(struct imx_usbmisc_data *data)
300{
301	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
302	u32 reg;
303
304	/*
305	 * Vybrid only has one misc register set, but in two different
306	 * areas. These is reflected in two instances of this driver.
307	 */
308	if (data->index >= 1)
309		return -EINVAL;
310
311	if (data->disable_oc) {
312		reg = readl(usbmisc->base);
313		writel(reg | VF610_OVER_CUR_DIS, usbmisc->base);
314	}
315
316	return 0;
317}
318
319static const struct usbmisc_ops imx25_usbmisc_ops = {
320	.init = usbmisc_imx25_init,
321	.post = usbmisc_imx25_post,
322};
323
324static const struct usbmisc_ops imx27_usbmisc_ops = {
325	.init = usbmisc_imx27_init,
326};
327
328static const struct usbmisc_ops imx53_usbmisc_ops = {
329	.init = usbmisc_imx53_init,
330};
331
332static const struct usbmisc_ops imx6q_usbmisc_ops = {
333	.set_wakeup = usbmisc_imx6q_set_wakeup,
334	.init = usbmisc_imx6q_init,
335};
336
337static const struct usbmisc_ops vf610_usbmisc_ops = {
338	.init = usbmisc_vf610_init,
339};
340
341static const struct usbmisc_ops imx6sx_usbmisc_ops = {
342	.set_wakeup = usbmisc_imx6q_set_wakeup,
343	.init = usbmisc_imx6sx_init,
344};
345
346int imx_usbmisc_init(struct imx_usbmisc_data *data)
347{
348	struct imx_usbmisc *usbmisc;
349
350	if (!data)
351		return 0;
352
353	usbmisc = dev_get_drvdata(data->dev);
354	if (!usbmisc->ops->init)
355		return 0;
356	return usbmisc->ops->init(data);
357}
358EXPORT_SYMBOL_GPL(imx_usbmisc_init);
359
360int imx_usbmisc_init_post(struct imx_usbmisc_data *data)
361{
362	struct imx_usbmisc *usbmisc;
363
364	if (!data)
365		return 0;
366
367	usbmisc = dev_get_drvdata(data->dev);
368	if (!usbmisc->ops->post)
369		return 0;
370	return usbmisc->ops->post(data);
371}
372EXPORT_SYMBOL_GPL(imx_usbmisc_init_post);
373
374int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled)
375{
376	struct imx_usbmisc *usbmisc;
377
378	if (!data)
379		return 0;
380
381	usbmisc = dev_get_drvdata(data->dev);
382	if (!usbmisc->ops->set_wakeup)
383		return 0;
384	return usbmisc->ops->set_wakeup(data, enabled);
385}
386EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup);
387
388static const struct of_device_id usbmisc_imx_dt_ids[] = {
389	{
390		.compatible = "fsl,imx25-usbmisc",
391		.data = &imx25_usbmisc_ops,
392	},
393	{
394		.compatible = "fsl,imx35-usbmisc",
395		.data = &imx25_usbmisc_ops,
396	},
397	{
398		.compatible = "fsl,imx27-usbmisc",
399		.data = &imx27_usbmisc_ops,
400	},
401	{
402		.compatible = "fsl,imx51-usbmisc",
403		.data = &imx53_usbmisc_ops,
404	},
405	{
406		.compatible = "fsl,imx53-usbmisc",
407		.data = &imx53_usbmisc_ops,
408	},
409	{
410		.compatible = "fsl,imx6q-usbmisc",
411		.data = &imx6q_usbmisc_ops,
412	},
413	{
414		.compatible = "fsl,vf610-usbmisc",
415		.data = &vf610_usbmisc_ops,
416	},
417	{
418		.compatible = "fsl,imx6sx-usbmisc",
419		.data = &imx6sx_usbmisc_ops,
420	},
421	{ /* sentinel */ }
422};
423MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
424
425static int usbmisc_imx_probe(struct platform_device *pdev)
426{
427	struct resource	*res;
428	struct imx_usbmisc *data;
429	struct of_device_id *tmp_dev;
430
431	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
432	if (!data)
433		return -ENOMEM;
434
435	spin_lock_init(&data->lock);
436
437	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
438	data->base = devm_ioremap_resource(&pdev->dev, res);
439	if (IS_ERR(data->base))
440		return PTR_ERR(data->base);
441
442	tmp_dev = (struct of_device_id *)
443		of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
444	data->ops = (const struct usbmisc_ops *)tmp_dev->data;
445	platform_set_drvdata(pdev, data);
446
447	return 0;
448}
449
450static int usbmisc_imx_remove(struct platform_device *pdev)
451{
452	return 0;
453}
454
455static struct platform_driver usbmisc_imx_driver = {
456	.probe = usbmisc_imx_probe,
457	.remove = usbmisc_imx_remove,
458	.driver = {
459		.name = "usbmisc_imx",
460		.of_match_table = usbmisc_imx_dt_ids,
461	 },
462};
463
464module_platform_driver(usbmisc_imx_driver);
465
466MODULE_ALIAS("platform:usbmisc-imx");
467MODULE_LICENSE("GPL v2");
468MODULE_DESCRIPTION("driver for imx usb non-core registers");
469MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
470