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