1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-NetBSD
3 *
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (augustss@carlstedt.se) at
9 * Carlstedt Research & Technology.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD: releng/12.2/sys/dev/usb/controller/ehci_pci.c 358016 2020-02-17 09:57:03Z hselasky $");
35
36 /*
37 * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller.
38 *
39 * The EHCI 1.0 spec can be found at
40 * http://developer.intel.com/technology/usb/download/ehci-r10.pdf
41 * and the USB 2.0 spec at
42 * http://www.usb.org/developers/docs/usb_20.zip
43 */
44
45 /* The low level controller code for EHCI has been split into
46 * PCI probes and EHCI specific code. This was done to facilitate the
47 * sharing of code between *BSD's
48 */
49
50 #include "implementation/global_implementation.h"
51 #include "controller/ehci.h"
52 #include "controller/ehcireg.h"
53
54 static device_probe_t ehci_pci_probe;
55 static device_attach_t ehci_pci_attach;
56 static device_detach_t ehci_pci_detach;
57 static usb_take_controller_t ehci_pci_take_controller;
58
59 static void
hiehci_post_reset(struct ehci_softc * sc)60 hiehci_post_reset(struct ehci_softc *sc)
61 {
62 uint32_t usb_mode;
63
64 /* Force HOST mode */
65 usb_mode = EOREAD4(sc, EHCI_USBMODE_NOLPM);
66 usb_mode &= ~EHCI_UM_CM;
67 usb_mode |= EHCI_UM_CM_HOST;
68 EOWRITE4(sc, EHCI_USBMODE_NOLPM, usb_mode);
69 }
70
71 static const char *
ehci_pci_match(device_t self)72 ehci_pci_match(device_t self)
73 {
74 return ("EHCI (generic) USB 2.0 controller");
75 }
76
77 static int
ehci_pci_probe(device_t self)78 ehci_pci_probe(device_t self)
79 {
80 const char *desc = ehci_pci_match(self);
81
82 if (desc) {
83 device_set_desc(self, desc);
84 return (BUS_PROBE_DEFAULT);
85 } else {
86 return (ENXIO);
87 }
88 }
89
90 static int
ehci_pci_attach(device_t self)91 ehci_pci_attach(device_t self)
92 {
93 struct resource *res;
94 ehci_softc_t *sc = device_get_softc(self);
95 int err = ENXIO;
96
97 /* initialise some bus fields */
98 sc->sc_bus.parent = self;
99 sc->sc_bus.devices = sc->sc_devices;
100 sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
101 sc->sc_bus.dma_bits = 32;
102 sc->sc_bus.dma_parent_tag[0].dma_bits = 32;
103
104 /* get all DMA memory */
105 if (usb_bus_mem_alloc_all(&sc->sc_bus,
106 USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) {
107 err = ENOMEM;
108 goto error0;
109 }
110
111 res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &unit, 0);
112 if (res == NULL) {
113 goto error;
114 }
115
116 sc->sc_io_res = ioremap(res->start, res->count);
117 if (!sc->sc_io_res) {
118 goto error1;
119 }
120
121 sc->sc_io_tag = (void *)sc->sc_io_res;
122 sc->sc_io_hdl = (bus_space_handle_t)sc->sc_io_res;
123 sc->sc_io_size = res->count;
124
125 sc->sc_flags |= EHCI_SCFLG_DONTRESET;
126
127 /* Setup callbacks. */
128 sc->sc_vendor_post_reset = hiehci_post_reset;
129 sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc;
130
131 sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
132 if (!sc->sc_bus.bdev) {
133 device_printf(self, "Could not add USB device\n");
134 goto error1;
135 }
136 device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
137
138 /*
139 * ehci_pci_match will never return NULL if ehci_pci_probe
140 * succeeded
141 */
142 device_set_desc(sc->sc_bus.bdev, ehci_pci_match(self));
143
144 res = bus_alloc_resource_any(self, SYS_RES_IRQ, &unit, 0);
145 if (res == NULL) {
146 goto error;
147 }
148 sc->sc_irq_res = res;
149 err = bus_setup_intr(res->start, 0, ehci_interrupt, sc);
150 if (err) {
151 goto error2;
152 }
153
154 ehci_pci_take_controller(self);
155
156 hiusb_start_hcd();
157 hiusb_device2host();
158
159 err = ehci_init(sc);
160 if (!err) {
161 err = device_probe_and_attach(sc->sc_bus.bdev);
162 }
163 if (err) {
164 device_printf(self, "ehci init failed err=%d\n", err);
165 goto error;
166 }
167
168 return (0);
169
170 error:
171 device_printf(self, "ehci attach failed err=%d\n", err);
172 (void)ehci_pci_detach(self);
173 return (err);
174 error2:
175 if (sc->sc_io_res != NULL) {
176 iounmap((void *)sc->sc_io_res);
177 sc->sc_io_res = NULL;
178 }
179 error1:
180 (void)bus_teardown_intr(NUM_HAL_INTERRUPT_USB_EHCI, sc);
181 error0:
182 usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
183 device_printf(self, "ehci attach failed err=%d\n", err);
184 return (err);
185 }
186
187 static int
ehci_pci_detach(device_t self)188 ehci_pci_detach(device_t self)
189 {
190 ehci_softc_t *sc = device_get_softc(self);
191
192 /* during module unload there are lots of children leftover */
193 (void)device_delete_children(dev);
194
195 ehci_detach(sc);
196 hiusb_stop_hcd();
197
198 if (sc->sc_irq_res != NULL) {
199 (void)bus_teardown_intr(sc->sc_irq_res->start, sc);
200 sc->sc_irq_res = NULL;
201 }
202 if (sc->sc_io_res != NULL) {
203 iounmap((void *)sc->sc_io_res);
204 sc->sc_io_res = NULL;
205 sc->sc_io_tag = NULL;
206 sc->sc_io_hdl = (uintptr_t)NULL;
207 sc->sc_io_size = 0;
208 }
209 usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
210
211 return (0);
212 }
213
214 static int
ehci_pci_take_controller(device_t self)215 ehci_pci_take_controller(device_t self)
216 {
217 return (0);
218 }
219
220 static device_method_t ehci_pci_methods[] = {
221 /* Device interface */
222 DEVMETHOD(device_probe, ehci_pci_probe),
223 DEVMETHOD(device_attach, ehci_pci_attach),
224 DEVMETHOD(device_detach, ehci_pci_detach),
225 DEVMETHOD(device_suspend, bus_generic_suspend),
226 DEVMETHOD(device_resume, bus_generic_resume),
227 DEVMETHOD(device_shutdown, bus_generic_shutdown),
228 DEVMETHOD(usb_take_controller, ehci_pci_take_controller),
229
230 DEVMETHOD_END
231 };
232
233 static driver_t ehci_driver = {
234 .name = "ehci",
235 .methods = ehci_pci_methods,
236 .size = sizeof(struct ehci_softc),
237 };
238
239 static devclass_t ehci_devclass;
240
241 DRIVER_MODULE(ehci, nexus, ehci_driver, ehci_devclass, 0, 0);
242
243 int
hiehci_init(void)244 hiehci_init(void)
245 {
246 DPRINTF("hiehci_init");
247 return driver_module_handler(NULL, MOD_LOAD, &ehci_nexus_driver_mod);
248 }
249
250 void
hiehci_exit(void)251 hiehci_exit(void)
252 {
253 DPRINTF("hiehci_exit");
254 (void)driver_module_handler(NULL, MOD_UNLOAD, &ehci_nexus_driver_mod);
255 }
256