1*c7fb772bSthorpej /* $NetBSD: pq3ehci.c,v 1.12 2021/08/07 16:19:02 thorpej Exp $ */
2b8ea2c8cSmatt /*-
3b8ea2c8cSmatt * Copyright (c) 2010, 2011 The NetBSD Foundation, Inc.
4b8ea2c8cSmatt * All rights reserved.
5b8ea2c8cSmatt *
6b8ea2c8cSmatt * This code is derived from software contributed to The NetBSD Foundation
7b8ea2c8cSmatt * by Matt Thomas of 3am Software Foundry.
8b8ea2c8cSmatt *
9b8ea2c8cSmatt * Redistribution and use in source and binary forms, with or without
10b8ea2c8cSmatt * modification, are permitted provided that the following conditions
11b8ea2c8cSmatt * are met:
12b8ea2c8cSmatt * 1. Redistributions of source code must retain the above copyright
13b8ea2c8cSmatt * notice, this list of conditions and the following disclaimer.
14b8ea2c8cSmatt * 2. Redistributions in binary form must reproduce the above copyright
15b8ea2c8cSmatt * notice, this list of conditions and the following disclaimer in the
16b8ea2c8cSmatt * documentation and/or other materials provided with the distribution.
17b8ea2c8cSmatt *
18b8ea2c8cSmatt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19b8ea2c8cSmatt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20b8ea2c8cSmatt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21b8ea2c8cSmatt * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22b8ea2c8cSmatt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23b8ea2c8cSmatt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24b8ea2c8cSmatt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25b8ea2c8cSmatt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26b8ea2c8cSmatt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27b8ea2c8cSmatt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28b8ea2c8cSmatt * POSSIBILITY OF SUCH DAMAGE.
29b8ea2c8cSmatt */
30b8ea2c8cSmatt
31b8ea2c8cSmatt #include <sys/cdefs.h>
32*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: pq3ehci.c,v 1.12 2021/08/07 16:19:02 thorpej Exp $");
334cbd6ad8Smatt
3416031f7dSrin #ifdef _KERNEL_OPT
354cbd6ad8Smatt #include "opt_usb.h"
3616031f7dSrin #endif
37b8ea2c8cSmatt
38b8ea2c8cSmatt #include <sys/param.h>
39b8ea2c8cSmatt #include <sys/systm.h>
40b8ea2c8cSmatt #include <sys/device.h>
41b8ea2c8cSmatt #include <sys/kernel.h>
42b8ea2c8cSmatt #include <sys/proc.h>
43b8ea2c8cSmatt #include <sys/queue.h>
44b8ea2c8cSmatt
45b8ea2c8cSmatt #include <sys/bus.h>
46b8ea2c8cSmatt
47b8ea2c8cSmatt #include <powerpc/booke/cpuvar.h>
48b8ea2c8cSmatt #include <powerpc/booke/e500var.h>
49b8ea2c8cSmatt #include <powerpc/booke/e500reg.h>
50b8ea2c8cSmatt
51b8ea2c8cSmatt #include <dev/usb/usb.h>
52b8ea2c8cSmatt #include <dev/usb/usbdi.h>
53b8ea2c8cSmatt #include <dev/usb/usbdivar.h>
54b8ea2c8cSmatt #include <dev/usb/usb_mem.h>
55b8ea2c8cSmatt
56b8ea2c8cSmatt #include <dev/usb/ehcireg.h>
57b8ea2c8cSmatt #include <dev/usb/ehcivar.h>
58b8ea2c8cSmatt
590363056bSskrll /*
608940d9f0Smsaitoh * This is relative to the start of the unreserved registers in USB controller
610363056bSskrll * block and not the full USB block which would be 0x1a8.
620363056bSskrll */
630363056bSskrll #define PQ3_USBMODE 0xa8 /* USB mode */
640363056bSskrll #define USBMODE_CM __BITS(0,1) /* Controller Mode */
650363056bSskrll #define USBMODE_CM_IDLE __SHIFTIN(0,USBMODE_CM) /* Idle (both) */
660363056bSskrll #define USBMODE_CM_DEVICE __SHIFTIN(2,USBMODE_CM) /* Device Controller */
670363056bSskrll #define USBMODE_CM_HOST __SHIFTIN(3,USBMODE_CM) /* Host Controller */
680363056bSskrll
69b8ea2c8cSmatt #ifdef EHCI_DEBUG
70b8ea2c8cSmatt #define DPRINTF(x) if (ehcidebug) printf x
71b8ea2c8cSmatt extern int ehcidebug;
72b8ea2c8cSmatt #else
73b8ea2c8cSmatt #define DPRINTF(x)
74b8ea2c8cSmatt #endif
75b8ea2c8cSmatt
76b8ea2c8cSmatt static int pq3ehci_match(device_t, cfdata_t, void *);
77b8ea2c8cSmatt static void pq3ehci_attach(device_t, device_t, void *);
78b8ea2c8cSmatt
79b8ea2c8cSmatt struct pq3ehci_softc {
80b8ea2c8cSmatt ehci_softc_t sc;
81b8ea2c8cSmatt void *sc_ih; /* interrupt vectoring */
82b8ea2c8cSmatt };
83b8ea2c8cSmatt
840363056bSskrll static void pq3ehci_init(struct ehci_softc *);
850363056bSskrll
86b8ea2c8cSmatt CFATTACH_DECL_NEW(pq3ehci, sizeof(struct pq3ehci_softc),
87b8ea2c8cSmatt pq3ehci_match, pq3ehci_attach, NULL, NULL);
88b8ea2c8cSmatt
89b8ea2c8cSmatt static int
pq3ehci_match(device_t parent,cfdata_t cf,void * aux)90b8ea2c8cSmatt pq3ehci_match(device_t parent, cfdata_t cf, void *aux)
91b8ea2c8cSmatt {
92b8ea2c8cSmatt
93b8ea2c8cSmatt if (!e500_cpunode_submatch(parent, cf, cf->cf_name, aux))
94b8ea2c8cSmatt return 0;
95b8ea2c8cSmatt
96b8ea2c8cSmatt return 1;
97b8ea2c8cSmatt }
98b8ea2c8cSmatt
99b8ea2c8cSmatt static void
pq3ehci_attach(device_t parent,device_t self,void * aux)100b8ea2c8cSmatt pq3ehci_attach(device_t parent, device_t self, void *aux)
101b8ea2c8cSmatt {
102b8ea2c8cSmatt struct cpunode_softc * const psc = device_private(parent);
103b8ea2c8cSmatt struct pq3ehci_softc * const sc = device_private(self);
104b8ea2c8cSmatt struct cpunode_attach_args * const cna = aux;
105b8ea2c8cSmatt struct cpunode_locators * const cnl = &cna->cna_locs;
106b8ea2c8cSmatt int error;
107b8ea2c8cSmatt
108b8ea2c8cSmatt psc->sc_children |= cna->cna_childmask;
109571f5798Smatt sc->sc.iot = cna->cna_le_memt; /* EHCI registers are little endian */
110b8ea2c8cSmatt sc->sc.sc_dev = self;
1114e8e6643Sskrll sc->sc.sc_bus.ub_dmatag = cna->cna_dmat;
1124e8e6643Sskrll sc->sc.sc_bus.ub_hcpriv = sc;
1134e8e6643Sskrll sc->sc.sc_bus.ub_revision = USBREV_2_0;
114b8ea2c8cSmatt sc->sc.sc_ncomp = 0;
115b8ea2c8cSmatt sc->sc.sc_flags |= EHCIF_ETTF;
1160363056bSskrll sc->sc.sc_vendor_init = pq3ehci_init;
117b8ea2c8cSmatt
118b8ea2c8cSmatt aprint_naive(": USB controller\n");
119b8ea2c8cSmatt aprint_normal(": USB controller\n");
120b8ea2c8cSmatt
121b8ea2c8cSmatt error = bus_space_map(sc->sc.iot, cnl->cnl_addr, cnl->cnl_size, 0,
122b8ea2c8cSmatt &sc->sc.ioh);
123b8ea2c8cSmatt if (error) {
124b8ea2c8cSmatt aprint_error_dev(self,
125b8ea2c8cSmatt "can't map registers for %s#%u: %d\n",
126b8ea2c8cSmatt cnl->cnl_name, cnl->cnl_instance, error);
127b8ea2c8cSmatt return;
128b8ea2c8cSmatt }
129b8ea2c8cSmatt sc->sc.sc_size = cnl->cnl_size;
130b8ea2c8cSmatt
131571f5798Smatt /*
132571f5798Smatt * We need to tell the USB interface to snoop all off RAM starting
133571f5798Smatt * at 0. Since it can do it by powers of 2, get the highest RAM
134571f5798Smatt * address and roughly round it to the next power of 2 and find
135571f5798Smatt * the number of leading zero bits.
136571f5798Smatt */
137571f5798Smatt cpu_write_4(cnl->cnl_addr + USB_SNOOP1,
138571f5798Smatt SNOOP_2GB - __builtin_clz(curcpu()->ci_softc->cpu_highmem * 2 - 1));
1394cbd6ad8Smatt cpu_write_4(cnl->cnl_addr + USB_CONTROL, USB_EN);
140571f5798Smatt
141571f5798Smatt sc->sc_ih = intr_establish(cnl->cnl_intrs[0], IPL_USB, IST_ONCHIP,
142b8ea2c8cSmatt ehci_intr, sc);
143b8ea2c8cSmatt if (sc->sc_ih == NULL) {
144b8ea2c8cSmatt aprint_error_dev(self, "failed to establish interrupt %d\n",
145b8ea2c8cSmatt cnl->cnl_intrs[0]);
146b8ea2c8cSmatt goto fail;
147b8ea2c8cSmatt }
148b8ea2c8cSmatt aprint_normal_dev(self, "interrupting on irq %d\n",
149b8ea2c8cSmatt cnl->cnl_intrs[0]);
150b8ea2c8cSmatt
151b8ea2c8cSmatt /* offs is needed for EOWRITEx */
152b8ea2c8cSmatt sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH);
153b8ea2c8cSmatt
154b8ea2c8cSmatt /* Disable interrupts, so we don't get any spurious ones. */
155b8ea2c8cSmatt DPRINTF(("%s: offs=%d\n", device_xname(self), sc->sc.sc_offs));
15609fca0b7Smatt EOWRITE4(&sc->sc, EHCI_USBINTR, 0);
157b8ea2c8cSmatt
158b8ea2c8cSmatt error = ehci_init(&sc->sc);
1594e8e6643Sskrll if (error) {
160b8ea2c8cSmatt aprint_error_dev(self, "init failed, error=%d\n", error);
161b8ea2c8cSmatt goto fail;
162b8ea2c8cSmatt }
163b8ea2c8cSmatt
164b8ea2c8cSmatt /* Attach usb device. */
1652685996bSthorpej sc->sc.sc_child = config_found(self, &sc->sc.sc_bus, usbctlprint,
166*c7fb772bSthorpej CFARGS_NONE);
167b8ea2c8cSmatt return;
168b8ea2c8cSmatt
169b8ea2c8cSmatt fail:
170b8ea2c8cSmatt if (sc->sc_ih) {
171b8ea2c8cSmatt intr_disestablish(sc->sc_ih);
172b8ea2c8cSmatt sc->sc_ih = NULL;
173b8ea2c8cSmatt }
174b8ea2c8cSmatt if (sc->sc.sc_size) {
175b8ea2c8cSmatt bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
176b8ea2c8cSmatt sc->sc.sc_size = 0;
177b8ea2c8cSmatt }
178b8ea2c8cSmatt return;
179b8ea2c8cSmatt }
1800363056bSskrll
1810363056bSskrll static void
pq3ehci_init(struct ehci_softc * hsc)1820363056bSskrll pq3ehci_init(struct ehci_softc *hsc)
1830363056bSskrll {
1840363056bSskrll uint32_t old = bus_space_read_4(hsc->iot, hsc->ioh, PQ3_USBMODE);
1850363056bSskrll uint32_t reg = old;
1860363056bSskrll
1870363056bSskrll reg &= ~USBMODE_CM;
1880363056bSskrll reg |= USBMODE_CM_HOST;
1890363056bSskrll if (reg != old)
1900363056bSskrll bus_space_write_4(hsc->iot, hsc->ioh, PQ3_USBMODE, reg);
1910363056bSskrll }
192