1673a524bSIlya Yanok /*
2673a524bSIlya Yanok * Copyright (C) 2005-2007 by Texas Instruments
3673a524bSIlya Yanok * Some code has been taken from tusb6010.c
4673a524bSIlya Yanok * Copyrights for that are attributable to:
5673a524bSIlya Yanok * Copyright (C) 2006 Nokia Corporation
6673a524bSIlya Yanok * Tony Lindgren <tony@atomide.com>
7673a524bSIlya Yanok *
8673a524bSIlya Yanok * This file is part of the Inventra Controller Driver for Linux.
9673a524bSIlya Yanok *
105b8031ccSTom Rini * SPDX-License-Identifier: GPL-2.0
11673a524bSIlya Yanok */
12673a524bSIlya Yanok #include <common.h>
13b30beda2SAdam Ford #include <dm.h>
14b30beda2SAdam Ford #include <dm/device-internal.h>
15b30beda2SAdam Ford #include <dm/lists.h>
16b30beda2SAdam Ford #include <linux/usb/otg.h>
1727754d18SPaul Kocialkowski #include <asm/omap_common.h>
18673a524bSIlya Yanok #include <asm/omap_musb.h>
19673a524bSIlya Yanok #include <twl4030.h>
2027754d18SPaul Kocialkowski #include <twl6030.h>
21673a524bSIlya Yanok #include "linux-compat.h"
22673a524bSIlya Yanok #include "musb_core.h"
23673a524bSIlya Yanok #include "omap2430.h"
24b30beda2SAdam Ford #include "musb_uboot.h"
25673a524bSIlya Yanok
omap2430_low_level_exit(struct musb * musb)26673a524bSIlya Yanok static inline void omap2430_low_level_exit(struct musb *musb)
27673a524bSIlya Yanok {
28673a524bSIlya Yanok u32 l;
29673a524bSIlya Yanok
30673a524bSIlya Yanok /* in any role */
31673a524bSIlya Yanok l = musb_readl(musb->mregs, OTG_FORCESTDBY);
32673a524bSIlya Yanok l |= ENABLEFORCE; /* enable MSTANDBY */
33673a524bSIlya Yanok musb_writel(musb->mregs, OTG_FORCESTDBY, l);
34673a524bSIlya Yanok }
35673a524bSIlya Yanok
omap2430_low_level_init(struct musb * musb)36673a524bSIlya Yanok static inline void omap2430_low_level_init(struct musb *musb)
37673a524bSIlya Yanok {
38673a524bSIlya Yanok u32 l;
39673a524bSIlya Yanok
40673a524bSIlya Yanok l = musb_readl(musb->mregs, OTG_FORCESTDBY);
41673a524bSIlya Yanok l &= ~ENABLEFORCE; /* disable MSTANDBY */
42673a524bSIlya Yanok musb_writel(musb->mregs, OTG_FORCESTDBY, l);
43673a524bSIlya Yanok }
44673a524bSIlya Yanok
45673a524bSIlya Yanok
omap2430_musb_init(struct musb * musb)46673a524bSIlya Yanok static int omap2430_musb_init(struct musb *musb)
47673a524bSIlya Yanok {
48673a524bSIlya Yanok u32 l;
49673a524bSIlya Yanok int status = 0;
50193d7d15SPaul Kocialkowski unsigned long int start;
51b30beda2SAdam Ford
52673a524bSIlya Yanok struct omap_musb_board_data *data =
53673a524bSIlya Yanok (struct omap_musb_board_data *)musb->controller;
54673a524bSIlya Yanok
55193d7d15SPaul Kocialkowski /* Reset the controller */
56193d7d15SPaul Kocialkowski musb_writel(musb->mregs, OTG_SYSCONFIG, SOFTRST);
57193d7d15SPaul Kocialkowski
58193d7d15SPaul Kocialkowski start = get_timer(0);
59193d7d15SPaul Kocialkowski
60193d7d15SPaul Kocialkowski while (1) {
61193d7d15SPaul Kocialkowski l = musb_readl(musb->mregs, OTG_SYSCONFIG);
62193d7d15SPaul Kocialkowski if ((l & SOFTRST) == 0)
63193d7d15SPaul Kocialkowski break;
64193d7d15SPaul Kocialkowski
65193d7d15SPaul Kocialkowski if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) {
66193d7d15SPaul Kocialkowski dev_err(musb->controller, "MUSB reset is taking too long\n");
67193d7d15SPaul Kocialkowski return -ENODEV;
68193d7d15SPaul Kocialkowski }
69193d7d15SPaul Kocialkowski }
70673a524bSIlya Yanok
71673a524bSIlya Yanok l = musb_readl(musb->mregs, OTG_INTERFSEL);
72673a524bSIlya Yanok
73673a524bSIlya Yanok if (data->interface_type == MUSB_INTERFACE_UTMI) {
74673a524bSIlya Yanok /* OMAP4 uses Internal PHY GS70 which uses UTMI interface */
75673a524bSIlya Yanok l &= ~ULPI_12PIN; /* Disable ULPI */
76673a524bSIlya Yanok l |= UTMI_8BIT; /* Enable UTMI */
77673a524bSIlya Yanok } else {
78673a524bSIlya Yanok l |= ULPI_12PIN;
79673a524bSIlya Yanok }
80673a524bSIlya Yanok
81673a524bSIlya Yanok musb_writel(musb->mregs, OTG_INTERFSEL, l);
82673a524bSIlya Yanok
83673a524bSIlya Yanok pr_debug("HS USB OTG: revision 0x%x, sysconfig 0x%02x, "
84673a524bSIlya Yanok "sysstatus 0x%x, intrfsel 0x%x, simenable 0x%x\n",
85673a524bSIlya Yanok musb_readl(musb->mregs, OTG_REVISION),
86673a524bSIlya Yanok musb_readl(musb->mregs, OTG_SYSCONFIG),
87673a524bSIlya Yanok musb_readl(musb->mregs, OTG_SYSSTATUS),
88673a524bSIlya Yanok musb_readl(musb->mregs, OTG_INTERFSEL),
89673a524bSIlya Yanok musb_readl(musb->mregs, OTG_SIMENABLE));
90673a524bSIlya Yanok return 0;
91673a524bSIlya Yanok
92673a524bSIlya Yanok err1:
93673a524bSIlya Yanok return status;
94673a524bSIlya Yanok }
95673a524bSIlya Yanok
omap2430_musb_enable(struct musb * musb)9615837236SHans de Goede static int omap2430_musb_enable(struct musb *musb)
97673a524bSIlya Yanok {
98673a524bSIlya Yanok #ifdef CONFIG_TWL4030_USB
99673a524bSIlya Yanok if (twl4030_usb_ulpi_init()) {
100673a524bSIlya Yanok serial_printf("ERROR: %s Could not initialize PHY\n",
101673a524bSIlya Yanok __PRETTY_FUNCTION__);
102673a524bSIlya Yanok }
103673a524bSIlya Yanok #endif
10427754d18SPaul Kocialkowski
10527754d18SPaul Kocialkowski #ifdef CONFIG_TWL6030_POWER
10627754d18SPaul Kocialkowski twl6030_usb_device_settings();
10727754d18SPaul Kocialkowski #endif
10827754d18SPaul Kocialkowski
10977777f76STom Rini #ifdef CONFIG_OMAP44XX
11027754d18SPaul Kocialkowski u32 *usbotghs_control = (u32 *)((*ctrl)->control_usbotghs_ctrl);
11127754d18SPaul Kocialkowski *usbotghs_control = USBOTGHS_CONTROL_AVALID |
11227754d18SPaul Kocialkowski USBOTGHS_CONTROL_VBUSVALID | USBOTGHS_CONTROL_IDDIG;
11327754d18SPaul Kocialkowski #endif
11427754d18SPaul Kocialkowski
11515837236SHans de Goede return 0;
116673a524bSIlya Yanok }
117673a524bSIlya Yanok
omap2430_musb_disable(struct musb * musb)118673a524bSIlya Yanok static void omap2430_musb_disable(struct musb *musb)
119673a524bSIlya Yanok {
120673a524bSIlya Yanok
121673a524bSIlya Yanok }
122673a524bSIlya Yanok
omap2430_musb_exit(struct musb * musb)123673a524bSIlya Yanok static int omap2430_musb_exit(struct musb *musb)
124673a524bSIlya Yanok {
125673a524bSIlya Yanok del_timer_sync(&musb_idle_timer);
126673a524bSIlya Yanok
127673a524bSIlya Yanok omap2430_low_level_exit(musb);
128673a524bSIlya Yanok
129673a524bSIlya Yanok return 0;
130673a524bSIlya Yanok }
131673a524bSIlya Yanok
132673a524bSIlya Yanok const struct musb_platform_ops omap2430_ops = {
133673a524bSIlya Yanok .init = omap2430_musb_init,
134673a524bSIlya Yanok .exit = omap2430_musb_exit,
135673a524bSIlya Yanok .enable = omap2430_musb_enable,
136673a524bSIlya Yanok .disable = omap2430_musb_disable,
137673a524bSIlya Yanok };
138b30beda2SAdam Ford
1393739bf7eSSven Schwermer #if CONFIG_IS_ENABLED(DM_USB)
140b30beda2SAdam Ford
141b30beda2SAdam Ford struct omap2430_musb_platdata {
142b30beda2SAdam Ford void *base;
143b30beda2SAdam Ford void *ctrl_mod_base;
144b30beda2SAdam Ford struct musb_hdrc_platform_data plat;
145b30beda2SAdam Ford struct musb_hdrc_config musb_config;
146b30beda2SAdam Ford struct omap_musb_board_data otg_board_data;
147b30beda2SAdam Ford };
148b30beda2SAdam Ford
omap2430_musb_ofdata_to_platdata(struct udevice * dev)149b30beda2SAdam Ford static int omap2430_musb_ofdata_to_platdata(struct udevice *dev)
150b30beda2SAdam Ford {
151b30beda2SAdam Ford struct omap2430_musb_platdata *platdata = dev_get_platdata(dev);
152b30beda2SAdam Ford const void *fdt = gd->fdt_blob;
153b30beda2SAdam Ford int node = dev_of_offset(dev);
154b30beda2SAdam Ford
155b30beda2SAdam Ford platdata->base = (void *)dev_read_addr_ptr(dev);
156b30beda2SAdam Ford
157b30beda2SAdam Ford platdata->musb_config.multipoint = fdtdec_get_int(fdt, node,
158b30beda2SAdam Ford "multipoint",
159b30beda2SAdam Ford -1);
160b30beda2SAdam Ford if (platdata->musb_config.multipoint < 0) {
161b30beda2SAdam Ford pr_err("MUSB multipoint DT entry missing\n");
162b30beda2SAdam Ford return -ENOENT;
163b30beda2SAdam Ford }
164b30beda2SAdam Ford
165b30beda2SAdam Ford platdata->musb_config.dyn_fifo = 1;
166b30beda2SAdam Ford platdata->musb_config.num_eps = fdtdec_get_int(fdt, node,
167b30beda2SAdam Ford "num-eps", -1);
168b30beda2SAdam Ford if (platdata->musb_config.num_eps < 0) {
169b30beda2SAdam Ford pr_err("MUSB num-eps DT entry missing\n");
170b30beda2SAdam Ford return -ENOENT;
171b30beda2SAdam Ford }
172b30beda2SAdam Ford
173b30beda2SAdam Ford platdata->musb_config.ram_bits = fdtdec_get_int(fdt, node,
174b30beda2SAdam Ford "ram-bits", -1);
175b30beda2SAdam Ford if (platdata->musb_config.ram_bits < 0) {
176b30beda2SAdam Ford pr_err("MUSB ram-bits DT entry missing\n");
177b30beda2SAdam Ford return -ENOENT;
178b30beda2SAdam Ford }
179b30beda2SAdam Ford
180b30beda2SAdam Ford platdata->plat.power = fdtdec_get_int(fdt, node,
181b30beda2SAdam Ford "power", -1);
182b30beda2SAdam Ford if (platdata->plat.power < 0) {
183b30beda2SAdam Ford pr_err("MUSB power DT entry missing\n");
184b30beda2SAdam Ford return -ENOENT;
185b30beda2SAdam Ford }
186b30beda2SAdam Ford
187b30beda2SAdam Ford platdata->otg_board_data.interface_type = fdtdec_get_int(fdt, node,
188b30beda2SAdam Ford "interface-type", -1);
189b30beda2SAdam Ford if (platdata->otg_board_data.interface_type < 0) {
190b30beda2SAdam Ford pr_err("MUSB interface-type DT entry missing\n");
191b30beda2SAdam Ford return -ENOENT;
192b30beda2SAdam Ford }
193b30beda2SAdam Ford
194b30beda2SAdam Ford #if 0 /* In a perfect world, mode would be set to OTG, mode 3 from DT */
195b30beda2SAdam Ford platdata->plat.mode = fdtdec_get_int(fdt, node,
196b30beda2SAdam Ford "mode", -1);
197b30beda2SAdam Ford if (platdata->plat.mode < 0) {
198b30beda2SAdam Ford pr_err("MUSB mode DT entry missing\n");
199b30beda2SAdam Ford return -ENOENT;
200b30beda2SAdam Ford }
201b30beda2SAdam Ford #else /* MUSB_OTG, it doesn't work */
202b30beda2SAdam Ford #ifdef CONFIG_USB_MUSB_HOST /* Host seems to be the only option that works */
203b30beda2SAdam Ford platdata->plat.mode = MUSB_HOST;
204b30beda2SAdam Ford #else /* For that matter, MUSB_PERIPHERAL doesn't either */
205b30beda2SAdam Ford platdata->plat.mode = MUSB_PERIPHERAL;
206b30beda2SAdam Ford #endif
207b30beda2SAdam Ford #endif
208b30beda2SAdam Ford platdata->otg_board_data.dev = dev;
209b30beda2SAdam Ford platdata->plat.config = &platdata->musb_config;
210b30beda2SAdam Ford platdata->plat.platform_ops = &omap2430_ops;
211b30beda2SAdam Ford platdata->plat.board_data = &platdata->otg_board_data;
212b30beda2SAdam Ford return 0;
213b30beda2SAdam Ford }
214b30beda2SAdam Ford
omap2430_musb_probe(struct udevice * dev)215b30beda2SAdam Ford static int omap2430_musb_probe(struct udevice *dev)
216b30beda2SAdam Ford {
217b30beda2SAdam Ford #ifdef CONFIG_USB_MUSB_HOST
218b30beda2SAdam Ford struct musb_host_data *host = dev_get_priv(dev);
219b30beda2SAdam Ford #endif
220b30beda2SAdam Ford struct omap2430_musb_platdata *platdata = dev_get_platdata(dev);
221b30beda2SAdam Ford struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
222b30beda2SAdam Ford struct omap_musb_board_data *otg_board_data;
223b30beda2SAdam Ford int ret;
224b30beda2SAdam Ford void *base = dev_read_addr_ptr(dev);
225b30beda2SAdam Ford
226b30beda2SAdam Ford priv->desc_before_addr = true;
227b30beda2SAdam Ford
228b30beda2SAdam Ford otg_board_data = &platdata->otg_board_data;
229b30beda2SAdam Ford
230b30beda2SAdam Ford #ifdef CONFIG_USB_MUSB_HOST
231b30beda2SAdam Ford host->host = musb_init_controller(&platdata->plat,
232b30beda2SAdam Ford (struct device *)otg_board_data,
233b30beda2SAdam Ford platdata->base);
234b30beda2SAdam Ford if (!host->host) {
235b30beda2SAdam Ford return -EIO;
236b30beda2SAdam Ford }
237b30beda2SAdam Ford
238b30beda2SAdam Ford ret = musb_lowlevel_init(host);
239b30beda2SAdam Ford #else
240b30beda2SAdam Ford ret = musb_register(&platdata->plat,
241b30beda2SAdam Ford (struct device *)otg_board_data,
242b30beda2SAdam Ford platdata->base);
243b30beda2SAdam Ford #endif
244b30beda2SAdam Ford return ret;
245b30beda2SAdam Ford }
246b30beda2SAdam Ford
omap2430_musb_remove(struct udevice * dev)247b30beda2SAdam Ford static int omap2430_musb_remove(struct udevice *dev)
248b30beda2SAdam Ford {
249b30beda2SAdam Ford struct musb_host_data *host = dev_get_priv(dev);
250b30beda2SAdam Ford
251b30beda2SAdam Ford musb_stop(host->host);
252b30beda2SAdam Ford
253b30beda2SAdam Ford return 0;
254b30beda2SAdam Ford }
255b30beda2SAdam Ford
256b30beda2SAdam Ford static const struct udevice_id omap2430_musb_ids[] = {
257b30beda2SAdam Ford { .compatible = "ti,omap3-musb" },
258b30beda2SAdam Ford { .compatible = "ti,omap4-musb" },
259b30beda2SAdam Ford { }
260b30beda2SAdam Ford };
261b30beda2SAdam Ford
262b30beda2SAdam Ford U_BOOT_DRIVER(omap2430_musb) = {
263b30beda2SAdam Ford .name = "omap2430-musb",
264b30beda2SAdam Ford #ifdef CONFIG_USB_MUSB_HOST
265b30beda2SAdam Ford .id = UCLASS_USB,
266b30beda2SAdam Ford #else
267*20828bbaSJean-Jacques Hiblot .id = UCLASS_USB_GADGET_GENERIC,
268b30beda2SAdam Ford #endif
269b30beda2SAdam Ford .of_match = omap2430_musb_ids,
270b30beda2SAdam Ford .ofdata_to_platdata = omap2430_musb_ofdata_to_platdata,
271b30beda2SAdam Ford .probe = omap2430_musb_probe,
272b30beda2SAdam Ford .remove = omap2430_musb_remove,
273b30beda2SAdam Ford #ifdef CONFIG_USB_MUSB_HOST
274b30beda2SAdam Ford .ops = &musb_usb_ops,
275b30beda2SAdam Ford #endif
276b30beda2SAdam Ford .platdata_auto_alloc_size = sizeof(struct omap2430_musb_platdata),
277b30beda2SAdam Ford .priv_auto_alloc_size = sizeof(struct musb_host_data),
278b30beda2SAdam Ford };
279b30beda2SAdam Ford
2803739bf7eSSven Schwermer #endif /* CONFIG_IS_ENABLED(DM_USB) */
281