1/*- 2 * SPDX-License-Identifier: BSD-2-Clause 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/* 35 * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller. 36 * 37 * The EHCI 1.0 spec can be found at 38 * http://developer.intel.com/technology/usb/download/ehci-r10.pdf 39 * and the USB 2.0 spec at 40 * http://www.usb.org/developers/docs/usb_20.zip 41 */ 42 43/* The low level controller code for EHCI has been split into 44 * PCI probes and EHCI specific code. This was done to facilitate the 45 * sharing of code between *BSD's 46 */ 47 48#include "implementation/global_implementation.h" 49#include "controller/ehci.h" 50#include "controller/ehcireg.h" 51 52static device_probe_t ehci_pci_probe; 53static device_attach_t ehci_pci_attach; 54static device_detach_t ehci_pci_detach; 55static usb_take_controller_t ehci_pci_take_controller; 56 57static void 58hiehci_post_reset(struct ehci_softc *sc) 59{ 60 uint32_t usb_mode; 61 62 /* Force HOST mode */ 63 usb_mode = EOREAD4(sc, EHCI_USBMODE_NOLPM); 64 usb_mode &= ~EHCI_UM_CM; 65 usb_mode |= EHCI_UM_CM_HOST; 66 EOWRITE4(sc, EHCI_USBMODE_NOLPM, usb_mode); 67} 68 69static const char * 70ehci_pci_match(device_t self) 71{ 72 return ("EHCI (generic) USB 2.0 controller"); 73} 74 75static int 76ehci_pci_probe(device_t self) 77{ 78 const char *desc = ehci_pci_match(self); 79 80 if (desc) { 81 device_set_desc(self, desc); 82 return (BUS_PROBE_DEFAULT); 83 } else { 84 return (ENXIO); 85 } 86} 87 88static int 89ehci_pci_attach(device_t self) 90{ 91 struct resource *res; 92 ehci_softc_t *sc = device_get_softc(self); 93 int err = ENXIO; 94 95 /* initialise some bus fields */ 96 sc->sc_bus.parent = self; 97 sc->sc_bus.devices = sc->sc_devices; 98 sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 99 sc->sc_bus.dma_bits = 32; 100 sc->sc_bus.dma_parent_tag[0].dma_bits = 32; 101 102 /* get all DMA memory */ 103 if (usb_bus_mem_alloc_all(&sc->sc_bus, 104 USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) { 105 err = ENOMEM; 106 goto error0; 107 } 108 109 res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &unit, 0); 110 if (res == NULL) { 111 goto error; 112 } 113 114 sc->sc_io_res = ioremap(res->start, res->count); 115 if (!sc->sc_io_res) { 116 goto error1; 117 } 118 119 sc->sc_io_tag = (void *)sc->sc_io_res; 120 sc->sc_io_hdl = (bus_space_handle_t)sc->sc_io_res; 121 sc->sc_io_size = res->count; 122 123 sc->sc_flags |= EHCI_SCFLG_DONTRESET; 124 125 /* Setup callbacks. */ 126 sc->sc_vendor_post_reset = hiehci_post_reset; 127 sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc; 128 129 sc->sc_bus.bdev = device_add_child(self, "usbus", -1); 130 if (!sc->sc_bus.bdev) { 131 device_printf(self, "Could not add USB device\n"); 132 goto error1; 133 } 134 device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 135 136 /* 137 * ehci_pci_match will never return NULL if ehci_pci_probe 138 * succeeded 139 */ 140 device_set_desc(sc->sc_bus.bdev, ehci_pci_match(self)); 141 142 res = bus_alloc_resource_any(self, SYS_RES_IRQ, &unit, 0); 143 if (res == NULL) { 144 goto error; 145 } 146 sc->sc_irq_res = res; 147 err = bus_setup_intr(res->start, 0, ehci_interrupt, sc); 148 if (err) { 149 goto error2; 150 } 151 152 ehci_pci_take_controller(self); 153 154 hiusb_start_hcd(); 155 hiusb_device2host(); 156 157 err = ehci_init(sc); 158 if (!err) { 159 err = device_probe_and_attach(sc->sc_bus.bdev); 160 } 161 if (err) { 162 device_printf(self, "ehci init failed err=%d\n", err); 163 goto error; 164 } 165 166 return (0); 167 168error: 169 device_printf(self, "ehci attach failed err=%d\n", err); 170 (void)ehci_pci_detach(self); 171 return (err); 172error2: 173 if (sc->sc_io_res != NULL) { 174 iounmap((void *)sc->sc_io_res); 175 sc->sc_io_res = NULL; 176 } 177error1: 178 (void)bus_teardown_intr(NUM_HAL_INTERRUPT_USB_EHCI, sc); 179error0: 180 usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); 181 device_printf(self, "ehci attach failed err=%d\n", err); 182 return (err); 183} 184 185static int 186ehci_pci_detach(device_t self) 187{ 188 ehci_softc_t *sc = device_get_softc(self); 189 190 /* during module unload there are lots of children leftover */ 191 (void)device_delete_children(dev); 192 193 ehci_detach(sc); 194 hiusb_stop_hcd(); 195 196 if (sc->sc_irq_res != NULL) { 197 (void)bus_teardown_intr(sc->sc_irq_res->start, sc); 198 sc->sc_irq_res = NULL; 199 } 200 if (sc->sc_io_res != NULL) { 201 iounmap((void *)sc->sc_io_res); 202 sc->sc_io_res = NULL; 203 sc->sc_io_tag = NULL; 204 sc->sc_io_hdl = (uintptr_t)NULL; 205 sc->sc_io_size = 0; 206 } 207 usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); 208 209 return (0); 210} 211 212static int 213ehci_pci_take_controller(device_t self) 214{ 215 return (0); 216} 217 218static device_method_t ehci_pci_methods[] = { 219 /* Device interface */ 220 DEVMETHOD(device_probe, ehci_pci_probe), 221 DEVMETHOD(device_attach, ehci_pci_attach), 222 DEVMETHOD(device_detach, ehci_pci_detach), 223 DEVMETHOD(device_suspend, bus_generic_suspend), 224 DEVMETHOD(device_resume, bus_generic_resume), 225 DEVMETHOD(device_shutdown, bus_generic_shutdown), 226 DEVMETHOD(usb_take_controller, ehci_pci_take_controller), 227 228 DEVMETHOD_END 229}; 230 231static driver_t ehci_driver = { 232 .name = "ehci", 233 .methods = ehci_pci_methods, 234 .size = sizeof(struct ehci_softc), 235}; 236 237static devclass_t ehci_devclass; 238 239DRIVER_MODULE(ehci, nexus, ehci_driver, ehci_devclass, 0, 0); 240 241int 242hiehci_init(void) 243{ 244 DPRINTF("hiehci_init"); 245 return driver_module_handler(NULL, MOD_LOAD, &ehci_nexus_driver_mod); 246} 247 248void 249hiehci_exit(void) 250{ 251 DPRINTF("hiehci_exit"); 252 (void)driver_module_handler(NULL, MOD_UNLOAD, &ehci_nexus_driver_mod); 253} 254