xref: /openbsd/sys/arch/armv7/omap/omehci.c (revision 73471bf0)
1 /*	$OpenBSD: omehci.c,v 1.9 2021/10/24 17:52:27 mpi Exp $ */
2 
3 /*
4  * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*-
20  * Copyright (c) 2011
21  *	Ben Gray <ben.r.gray@gmail.com>.
22  * All rights reserved.
23  *
24  * Redistribution and use in source and binary forms, with or without
25  * modification, are permitted provided that the following conditions
26  * are met:
27  * 1. Redistributions of source code must retain the above copyright
28  *    notice, this list of conditions and the following disclaimer.
29  * 2. Redistributions in binary form must reproduce the above copyright
30  *    notice, this list of conditions and the following disclaimer in the
31  *    documentation and/or other materials provided with the distribution.
32  *
33  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
34  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
37  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43  * SUCH DAMAGE.
44  */
45 
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/device.h>
49 #include <sys/kernel.h>
50 
51 #include <machine/intr.h>
52 #include <machine/bus.h>
53 #include <machine/fdt.h>
54 
55 #include <dev/usb/usb.h>
56 #include <dev/usb/usbdi.h>
57 #include <dev/usb/usbdivar.h>
58 
59 #include <armv7/omap/prcmvar.h>
60 #include <armv7/omap/omehcivar.h>
61 
62 #include <dev/ofw/openfirm.h>
63 #include <dev/ofw/ofw_misc.h>
64 #include <dev/ofw/fdt.h>
65 
66 #include <dev/usb/ehcireg.h>
67 #include <dev/usb/ehcivar.h>
68 
69 int	omehci_match(struct device *, void *, void *);
70 void	omehci_attach(struct device *, struct device *, void *);
71 int	omehci_detach(struct device *, int);
72 int	omehci_activate(struct device *, int);
73 
74 struct omehci_softc {
75 	struct ehci_softc	 sc;
76 	void			*sc_ih;
77 	bus_space_handle_t	 uhh_ioh;
78 	bus_space_handle_t	 tll_ioh;
79 
80 	uint32_t		 ehci_rev;
81 	uint32_t		 tll_avail;
82 
83 	uint32_t		 port_mode[OMAP_HS_USB_PORTS];
84 };
85 
86 int omehci_init(struct omehci_softc *);
87 void omehci_soft_phy_reset(struct omehci_softc *sc, unsigned int port);
88 void omehci_enable(struct omehci_softc *);
89 void omehci_disable(struct omehci_softc *);
90 void omehci_utmi_init(struct omehci_softc *sc, unsigned int en_mask);
91 void misc_setup(struct omehci_softc *sc);
92 void omehci_uhh_init(struct omehci_softc *sc);
93 
94 const struct cfattach omehci_ca = {
95 	sizeof (struct omehci_softc), omehci_match, omehci_attach,
96 	omehci_detach, omehci_activate
97 };
98 
99 struct cfdriver omehci_cd = {
100 	NULL, "omehci", DV_DULL
101 };
102 
103 int
104 omehci_match(struct device *parent, void *match, void *aux)
105 {
106 	struct fdt_attach_args *faa = aux;
107 
108 	return OF_is_compatible(faa->fa_node, "ti,usbhs-host");
109 }
110 
111 void
112 omehci_attach(struct device *parent, struct device *self, void *aux)
113 {
114 	struct omehci_softc	*sc = (struct omehci_softc *)self;
115 	struct fdt_attach_args *faa = aux;
116 	usbd_status		 r;
117 	char			*devname = sc->sc.sc_bus.bdev.dv_xname;
118 	uint32_t		 i;
119 	char			 port_mode[16];
120 	char			 name[32];
121 	int			 node;
122 	uint32_t		 reg[2];
123 
124 	if (faa->fa_nreg < 1)
125 		return;
126 
127 	sc->sc.iot = faa->fa_iot;
128 	sc->sc.sc_bus.dmatag = faa->fa_dmat;
129 
130 	/* set defaults */
131 	for (i = 0; i < OMAP_HS_USB_PORTS; i++)
132 		sc->port_mode[i] = EHCI_HCD_OMAP_MODE_UNKNOWN;
133 
134 	strlcpy(name, "portX-mode", sizeof(name));
135 
136 	for (i = 0; i < OMAP_HS_USB_PORTS; i++) {
137 		name[4] = '1' + i;
138 		memset(port_mode, 0, sizeof(port_mode));
139 
140 		if (OF_getprop(faa->fa_node, name, port_mode,
141 		    sizeof(port_mode)) == -1)
142 			continue;
143 
144 		if (strcmp(port_mode, "ehci-phy") == 0)
145 			sc->port_mode[i] = EHCI_HCD_OMAP_MODE_PHY;
146 		if (strcmp(port_mode, "ehci-hsic") == 0)
147 			sc->port_mode[i] = EHCI_HCD_OMAP_MODE_HSIC;
148 		if (strcmp(port_mode, "ehci-tll") == 0)
149 			sc->port_mode[i] = EHCI_HCD_OMAP_MODE_TLL ;
150 	}
151 
152 	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
153 		if (OF_is_compatible(node, "ti,ehci-omap"))
154 			break;
155 	}
156 
157 	if (node == 0)
158 		panic("could not find ehci child node");
159 
160 	if (OF_getpropintarray(node, "reg", reg, sizeof(reg)) != sizeof(reg))
161 		return;
162 
163 	/* Map I/O space */
164 	if (bus_space_map(sc->sc.iot, reg[0], reg[1], 0, &sc->sc.ioh)) {
165 		printf(": cannot map mem space\n");
166 		goto out;
167 	}
168 	sc->sc.sc_size = reg[1];
169 
170 	if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
171 	    0, &sc->uhh_ioh)) {
172 		printf(": cannot map mem space\n");
173 		goto mem0;
174 	}
175 
176 #if 0
177 	if (sc->tll_avail &&
178 	    bus_space_map(sc->sc.iot, aa->aa_dev->mem[2].addr,
179 	    aa->aa_dev->mem[2].size, 0, &sc->tll_ioh)) {
180 		printf(": cannot map mem space\n");
181 		goto mem1;
182 	}
183 #endif
184 
185 	printf("\n");
186 
187 	phy_enable_idx(node, 0);
188 
189 	if (omehci_init(sc))
190 		return;
191 
192 	/* Disable interrupts, so we don't get any spurious ones. */
193 	sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH);
194 	EOWRITE2(&sc->sc, EHCI_USBINTR, 0);
195 
196 	sc->sc_ih = arm_intr_establish_fdt(node, IPL_USB,
197 	    ehci_intr, &sc->sc, devname);
198 	if (sc->sc_ih == NULL) {
199 		printf(": unable to establish interrupt\n");
200 		printf("XXX - disable ehci and prcm");
201 		goto mem2;
202 	}
203 
204 	strlcpy(sc->sc.sc_vendor, "TI OMAP", sizeof(sc->sc.sc_vendor));
205 	r = ehci_init(&sc->sc);
206 	if (r != USBD_NORMAL_COMPLETION) {
207 		printf("%s: init failed, error=%d\n", devname, r);
208 		printf("XXX - disable ehci and prcm");
209 		goto intr;
210 	}
211 
212 	config_found(self, &sc->sc.sc_bus, usbctlprint);
213 
214 	goto out;
215 
216 intr:
217 	arm_intr_disestablish(sc->sc_ih);
218 	sc->sc_ih = NULL;
219 mem2:
220 #if 0
221 	bus_space_unmap(sc->sc.iot, sc->tll_ioh, aa->aa_dev->mem[2].size);
222 mem1:
223 #endif
224 	bus_space_unmap(sc->sc.iot, sc->uhh_ioh, faa->fa_reg[0].size);
225 mem0:
226 	bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
227 	sc->sc.sc_size = 0;
228 out:
229 	return;
230 }
231 
232 int
233 omehci_init(struct omehci_softc *sc)
234 {
235 	uint32_t i = 0, reg;
236 	uint32_t reset_performed = 0;
237 	uint32_t timeout = 0;
238 	uint32_t tll_ch_mask = 0;
239 
240 	/* enable high speed usb host clock */
241 	prcm_enablemodule(PRCM_USB);
242 
243 	/* Hold the PHY in RESET for enough time till DIR is high */
244 	if (reset_performed)
245 		delay(10);
246 
247 	/* Read the UHH revision */
248 	sc->ehci_rev = bus_space_read_4(sc->sc.iot, sc->uhh_ioh,
249 	    OMAP_USBHOST_UHH_REVISION);
250 
251 	/* Initialise the low level interface module(s) */
252 	if (sc->ehci_rev == OMAP_EHCI_REV1) {
253 		/* Enable the USB TLL */
254 		prcm_enablemodule(PRCM_USBTLL);
255 
256 		/* Perform TLL soft reset, and wait until reset is complete */
257 		bus_space_write_4(sc->sc.iot, sc->tll_ioh,
258 		    OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_SOFTRESET);
259 
260 		/* Set the timeout to 100ms*/
261 		timeout = (hz < 10) ? 1 : ((100 * hz) / 1000);
262 
263 		/* Wait for TLL reset to complete */
264 		while ((bus_space_read_4(sc->sc.iot, sc->tll_ioh,
265 		    OMAP_USBTLL_SYSSTATUS) & TLL_SYSSTATUS_RESETDONE)
266 		    == 0x00) {
267 
268 			/* Sleep for a tick */
269 			delay(10);
270 
271 			if (timeout-- == 0) {
272 				return 1;
273 			}
274 		}
275 
276 		bus_space_write_4(sc->sc.iot, sc->tll_ioh,
277 		    OMAP_USBTLL_SYSCONFIG,
278 		    TLL_SYSCONFIG_ENAWAKEUP | TLL_SYSCONFIG_AUTOIDLE |
279 		    TLL_SYSCONFIG_SIDLE_SMART_IDLE | TLL_SYSCONFIG_CACTIVITY);
280 	} else if (sc->ehci_rev == OMAP_EHCI_REV2) {
281 		/* For OMAP44xx devices you have to enable the per-port clocks:
282 		 *  PHY_MODE  - External ULPI clock
283 		 *  TTL_MODE  - Internal UTMI clock
284 		 *  HSIC_MODE - Internal 480Mhz and 60Mhz clocks
285 		 */
286 		if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) {
287 			//ti_prcm_clk_set_source(USBP1_PHY_CLK, EXT_CLK);
288 			prcm_enablemodule(PRCM_USBP1_PHY);
289 		} else if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
290 			prcm_enablemodule(PRCM_USBP1_UTMI);
291 		else if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC)
292 			prcm_enablemodule(PRCM_USBP1_HSIC);
293 
294 		if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) {
295 			//ti_prcm_clk_set_source(USBP2_PHY_CLK, EXT_CLK);
296 			prcm_enablemodule(PRCM_USBP2_PHY);
297 		} else if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
298 			prcm_enablemodule(PRCM_USBP2_UTMI);
299 		else if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC)
300 			prcm_enablemodule(PRCM_USBP2_HSIC);
301 	}
302 
303 	/* Put UHH in SmartIdle/SmartStandby mode */
304 	reg = bus_space_read_4(sc->sc.iot, sc->uhh_ioh,
305 	    OMAP_USBHOST_UHH_SYSCONFIG);
306 	if (sc->ehci_rev == OMAP_EHCI_REV1) {
307 		reg &= ~(UHH_SYSCONFIG_SIDLEMODE_MASK |
308 		         UHH_SYSCONFIG_MIDLEMODE_MASK);
309 		reg |= (UHH_SYSCONFIG_ENAWAKEUP |
310 		        UHH_SYSCONFIG_AUTOIDLE |
311 		        UHH_SYSCONFIG_CLOCKACTIVITY |
312 		        UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE |
313 		        UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY);
314 	} else if (sc->ehci_rev == OMAP_EHCI_REV2) {
315 		reg &= ~UHH_SYSCONFIG_IDLEMODE_MASK;
316 		reg |=  UHH_SYSCONFIG_IDLEMODE_NOIDLE;
317 		reg &= ~UHH_SYSCONFIG_STANDBYMODE_MASK;
318 		reg |=  UHH_SYSCONFIG_STANDBYMODE_NOSTDBY;
319 	}
320 	bus_space_write_4(sc->sc.iot, sc->uhh_ioh, OMAP_USBHOST_UHH_SYSCONFIG,
321 	    reg);
322 
323 	reg = bus_space_read_4(sc->sc.iot, sc->uhh_ioh,
324 	    OMAP_USBHOST_UHH_HOSTCONFIG);
325 
326 	/* Setup ULPI bypass and burst configurations */
327 	reg |= (UHH_HOSTCONFIG_ENA_INCR4 |
328 		UHH_HOSTCONFIG_ENA_INCR8 |
329 		UHH_HOSTCONFIG_ENA_INCR16);
330 	reg &= ~UHH_HOSTCONFIG_ENA_INCR_ALIGN;
331 
332 	if (sc->ehci_rev == OMAP_EHCI_REV1) {
333 		if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN)
334 			reg &= ~UHH_HOSTCONFIG_P1_CONNECT_STATUS;
335 		if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN)
336 			reg &= ~UHH_HOSTCONFIG_P2_CONNECT_STATUS;
337 		if (sc->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN)
338 			reg &= ~UHH_HOSTCONFIG_P3_CONNECT_STATUS;
339 
340 		/* Bypass the TLL module for PHY mode operation */
341 		if ((sc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) ||
342 		    (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) ||
343 		    (sc->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY))
344 			reg &= ~UHH_HOSTCONFIG_P1_ULPI_BYPASS;
345 		else
346 			reg |= UHH_HOSTCONFIG_P1_ULPI_BYPASS;
347 	} else if (sc->ehci_rev == OMAP_EHCI_REV2) {
348 		reg |=  UHH_HOSTCONFIG_APP_START_CLK;
349 
350 		/* Clear port mode fields for PHY mode*/
351 		reg &= ~UHH_HOSTCONFIG_P1_MODE_MASK;
352 		reg &= ~UHH_HOSTCONFIG_P2_MODE_MASK;
353 
354 		if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
355 			reg |= UHH_HOSTCONFIG_P1_MODE_UTMI_PHY;
356 		else if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC)
357 			reg |= UHH_HOSTCONFIG_P1_MODE_HSIC;
358 
359 		if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
360 			reg |= UHH_HOSTCONFIG_P2_MODE_UTMI_PHY;
361 		else if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC)
362 			reg |= UHH_HOSTCONFIG_P2_MODE_HSIC;
363 	}
364 
365 	bus_space_write_4(sc->sc.iot, sc->uhh_ioh, OMAP_USBHOST_UHH_HOSTCONFIG, reg);
366 
367 	/* If any of the ports are configured in TLL mode, enable them */
368 	for (i = 0; i < OMAP_HS_USB_PORTS; i++)
369 		if (sc->port_mode[i] == EHCI_HCD_OMAP_MODE_PHY)
370 			tll_ch_mask |= 1 << i;
371 
372 	/* Enable UTMI mode for required TLL channels */
373 #ifdef notyet
374 	if (tll_ch_mask)
375 		omap_ehci_utmi_init(sc, tll_ch_mask);
376 #endif
377 
378 	/* Set the interrupt threshold control, it controls the maximum rate at
379 	 * which the host controller issues interrupts.  We set it to 1 microframe
380 	 * at startup - the default is 8 mircoframes (equates to 1ms).
381 	 */
382 	reg = bus_space_read_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_USBCMD);
383 	reg &= 0xff00ffff;
384 	reg |= (1 << 16);
385 	bus_space_write_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_USBCMD, reg);
386 
387 	/* Soft reset the PHY using PHY reset command over ULPI */
388 	for (i = 0; i < OMAP_HS_USB_PORTS; i++)
389 		if (sc->port_mode[i] == EHCI_HCD_OMAP_MODE_PHY)
390 			omehci_soft_phy_reset(sc, i);
391 
392 	return(0);
393 }
394 
395 void
396 omehci_soft_phy_reset(struct omehci_softc *sc, unsigned int port)
397 {
398 	unsigned long timeout = (hz < 10) ? 1 : ((100 * hz) / 1000);
399 	uint32_t reg;
400 
401 	reg = ULPI_FUNC_CTRL_RESET
402 		/* FUNCTION_CTRL_SET register */
403 		| (ULPI_SET(ULPI_FUNC_CTRL) << OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT)
404 		/* Write */
405 		| (2 << OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT)
406 		/* PORTn */
407 		| ((port + 1) << OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT)
408 		/* start ULPI access*/
409 		| (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT);
410 
411 	bus_space_write_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_INSNREG05_ULPI, reg);
412 
413 	timeout += 1000000;
414 	/* Wait for ULPI access completion */
415 	while ((bus_space_read_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_INSNREG05_ULPI)
416 			& (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT))) {
417 
418 		/* Sleep for a tick */
419 		delay(10);
420 
421 		if (timeout-- == 0) {
422 			printf("PHY reset operation timed out\n");
423 			break;
424 		}
425 	}
426 }
427 
428 int
429 omehci_detach(struct device *self, int flags)
430 {
431 	struct omehci_softc		*sc = (struct omehci_softc *)self;
432 	int				rv;
433 
434 	rv = ehci_detach(self, flags);
435 	if (rv)
436 		return (rv);
437 
438 	if (sc->sc_ih != NULL) {
439 		arm_intr_disestablish(sc->sc_ih);
440 		sc->sc_ih = NULL;
441 	}
442 
443 	if (sc->sc.sc_size) {
444 		bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
445 		sc->sc.sc_size = 0;
446 	}
447 
448 	/* XXX: stop clock */
449 
450 	return (0);
451 }
452 
453 int
454 omehci_activate(struct device *self, int act)
455 {
456 	struct omehci_softc *sc = (struct omehci_softc *)self;
457 
458 	switch (act) {
459 	case DVACT_SUSPEND:
460 		sc->sc.sc_bus.use_polling++;
461 		/* FIXME */
462 		sc->sc.sc_bus.use_polling--;
463 		break;
464 	case DVACT_RESUME:
465 		sc->sc.sc_bus.use_polling++;
466 		/* FIXME */
467 		sc->sc.sc_bus.use_polling--;
468 		break;
469 	case DVACT_POWERDOWN:
470 		ehci_reset(&sc->sc);
471 		break;
472 	}
473 	return 0;
474 }
475