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