1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. 5 * All rights reserved. 6 * 7 * Developed by Semihalf. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of MARVELL nor the names of contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 /* 35 * FDT attachment driver for the USB Enhanced Host Controller. 36 */ 37 38 #include "opt_bus.h" 39 40 #include <sys/stdint.h> 41 #include <sys/stddef.h> 42 #include <sys/param.h> 43 #include <sys/queue.h> 44 #include <sys/types.h> 45 #include <sys/systm.h> 46 #include <sys/kernel.h> 47 #include <sys/bus.h> 48 #include <sys/module.h> 49 #include <sys/lock.h> 50 #include <sys/mutex.h> 51 #include <sys/condvar.h> 52 #include <sys/sysctl.h> 53 #include <sys/sx.h> 54 #include <sys/unistd.h> 55 #include <sys/callout.h> 56 #include <sys/malloc.h> 57 #include <sys/priv.h> 58 59 #include <dev/ofw/ofw_bus.h> 60 #include <dev/ofw/ofw_bus_subr.h> 61 62 #include <dev/usb/usb.h> 63 #include <dev/usb/usbdi.h> 64 65 #include <dev/usb/usb_core.h> 66 #include <dev/usb/usb_busdma.h> 67 #include <dev/usb/usb_process.h> 68 #include <dev/usb/usb_util.h> 69 70 #include <dev/usb/usb_controller.h> 71 #include <dev/usb/usb_bus.h> 72 #include <dev/usb/controller/ehci.h> 73 #include <dev/usb/controller/ehcireg.h> 74 75 #if !defined(__aarch64__) 76 #include <arm/mv/mvreg.h> 77 #endif 78 #include <arm/mv/mvvar.h> 79 80 #define EHCI_VENDORID_MRVL 0x1286 81 #define EHCI_HC_DEVSTR "Marvell Integrated USB 2.0 controller" 82 83 static device_attach_t mv_ehci_attach; 84 static device_detach_t mv_ehci_detach; 85 86 static int err_intr(void *arg); 87 88 static struct resource *irq_err; 89 static void *ih_err; 90 91 /* EHCI HC regs start at this offset within USB range */ 92 #define MV_USB_HOST_OFST 0x0100 93 94 #define USB_BRIDGE_INTR_CAUSE 0x210 95 #define USB_BRIDGE_INTR_MASK 0x214 96 #define USB_BRIDGE_ERR_ADDR 0x21C 97 98 #define MV_USB_ADDR_DECODE_ERR (1 << 0) 99 #define MV_USB_HOST_UNDERFLOW (1 << 1) 100 #define MV_USB_HOST_OVERFLOW (1 << 2) 101 #define MV_USB_DEVICE_UNDERFLOW (1 << 3) 102 103 enum mv_ehci_hwtype { 104 HWTYPE_NONE = 0, 105 HWTYPE_MV_EHCI_V1, 106 HWTYPE_MV_EHCI_V2, 107 }; 108 109 static struct ofw_compat_data compat_data[] = { 110 {"mrvl,usb-ehci", HWTYPE_MV_EHCI_V1}, 111 {"marvell,orion-ehci", HWTYPE_MV_EHCI_V2}, 112 {"marvell,armada-3700-ehci", HWTYPE_MV_EHCI_V2}, 113 {NULL, HWTYPE_NONE} 114 }; 115 116 static void 117 mv_ehci_post_reset(struct ehci_softc *ehci_softc) 118 { 119 uint32_t usbmode; 120 121 /* Force HOST mode */ 122 usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM); 123 usbmode &= ~EHCI_UM_CM; 124 usbmode |= EHCI_UM_CM_HOST; 125 EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode); 126 } 127 128 static int 129 mv_ehci_probe(device_t self) 130 { 131 132 if (!ofw_bus_status_okay(self)) 133 return (ENXIO); 134 135 if (!ofw_bus_search_compatible(self, compat_data)->ocd_data) 136 return (ENXIO); 137 138 device_set_desc(self, EHCI_HC_DEVSTR); 139 140 return (BUS_PROBE_DEFAULT); 141 } 142 143 static int 144 mv_ehci_attach(device_t self) 145 { 146 ehci_softc_t *sc = device_get_softc(self); 147 enum mv_ehci_hwtype hwtype; 148 bus_space_handle_t bsh; 149 int err; 150 int rid; 151 152 /* initialise some bus fields */ 153 sc->sc_bus.parent = self; 154 sc->sc_bus.devices = sc->sc_devices; 155 sc->sc_bus.devices_max = EHCI_MAX_DEVICES; 156 sc->sc_bus.dma_bits = 32; 157 158 hwtype = ofw_bus_search_compatible(self, compat_data)->ocd_data; 159 if (hwtype == HWTYPE_NONE) { 160 device_printf(self, "Wrong HW type flag detected\n"); 161 return (ENXIO); 162 } 163 164 /* get all DMA memory */ 165 if (usb_bus_mem_alloc_all(&sc->sc_bus, 166 USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { 167 return (ENOMEM); 168 } 169 170 rid = 0; 171 sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); 172 if (!sc->sc_io_res) { 173 device_printf(self, "Could not map memory\n"); 174 goto error; 175 } 176 sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); 177 bsh = rman_get_bushandle(sc->sc_io_res); 178 sc->sc_io_size = rman_get_size(sc->sc_io_res) - MV_USB_HOST_OFST; 179 180 /* 181 * Marvell EHCI host controller registers start at certain offset 182 * within the whole USB registers range, so create a subregion for the 183 * host mode configuration purposes. 184 */ 185 186 if (bus_space_subregion(sc->sc_io_tag, bsh, MV_USB_HOST_OFST, 187 sc->sc_io_size, &sc->sc_io_hdl) != 0) 188 panic("%s: unable to subregion USB host registers", 189 device_get_name(self)); 190 191 rid = 0; 192 if (hwtype == HWTYPE_MV_EHCI_V1) { 193 irq_err = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, 194 RF_SHAREABLE | RF_ACTIVE); 195 if (irq_err == NULL) { 196 device_printf(self, "Could not allocate error irq\n"); 197 mv_ehci_detach(self); 198 return (ENXIO); 199 } 200 rid = 1; 201 } 202 203 /* 204 * Notice: Marvell EHCI controller has TWO interrupt lines, so make 205 * sure to use the correct rid for the main one (controller interrupt) 206 * -- refer to DTS for the right resource number to use here. 207 */ 208 sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, 209 RF_SHAREABLE | RF_ACTIVE); 210 if (sc->sc_irq_res == NULL) { 211 device_printf(self, "Could not allocate irq\n"); 212 goto error; 213 } 214 215 sc->sc_bus.bdev = device_add_child(self, "usbus", DEVICE_UNIT_ANY); 216 if (!sc->sc_bus.bdev) { 217 device_printf(self, "Could not add USB device\n"); 218 goto error; 219 } 220 device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 221 device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); 222 223 sprintf(sc->sc_vendor, "Marvell"); 224 225 if (hwtype == HWTYPE_MV_EHCI_V1) { 226 err = bus_setup_intr(self, irq_err, INTR_TYPE_BIO, 227 err_intr, NULL, sc, &ih_err); 228 if (err) { 229 device_printf(self, "Could not setup error irq, %d\n", err); 230 ih_err = NULL; 231 goto error; 232 } 233 } 234 235 EWRITE4(sc, USB_BRIDGE_INTR_MASK, MV_USB_ADDR_DECODE_ERR | 236 MV_USB_HOST_UNDERFLOW | MV_USB_HOST_OVERFLOW | 237 MV_USB_DEVICE_UNDERFLOW); 238 239 err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, 240 NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); 241 if (err) { 242 device_printf(self, "Could not setup irq, %d\n", err); 243 sc->sc_intr_hdl = NULL; 244 goto error; 245 } 246 247 /* 248 * Workaround for Marvell integrated EHCI controller: reset of 249 * the EHCI core clears the USBMODE register, which sets the core in 250 * an undefined state (neither host nor agent), so it needs to be set 251 * again for proper operation. 252 * 253 * Refer to errata document MV-S500832-00D.pdf (p. 5.24 GL USB-2) for 254 * details. 255 */ 256 sc->sc_vendor_post_reset = mv_ehci_post_reset; 257 if (bootverbose) 258 device_printf(self, "5.24 GL USB-2 workaround enabled\n"); 259 260 /* XXX all MV chips need it? */ 261 sc->sc_flags |= EHCI_SCFLG_TT | EHCI_SCFLG_NORESTERM; 262 sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc; 263 err = ehci_init(sc); 264 if (!err) { 265 err = device_probe_and_attach(sc->sc_bus.bdev); 266 } 267 if (err) { 268 device_printf(self, "USB init failed err=%d\n", err); 269 goto error; 270 } 271 return (0); 272 273 error: 274 mv_ehci_detach(self); 275 return (ENXIO); 276 } 277 278 static int 279 mv_ehci_detach(device_t self) 280 { 281 ehci_softc_t *sc = device_get_softc(self); 282 int err; 283 284 /* during module unload there are lots of children leftover */ 285 err = bus_generic_detach(self); 286 if (err != 0) 287 return (err); 288 289 /* 290 * disable interrupts that might have been switched on in mv_ehci_attach 291 */ 292 if (sc->sc_io_res) { 293 EWRITE4(sc, USB_BRIDGE_INTR_MASK, 0); 294 } 295 if (sc->sc_irq_res && sc->sc_intr_hdl) { 296 /* 297 * only call ehci_detach() after ehci_init() 298 */ 299 ehci_detach(sc); 300 301 err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); 302 303 if (err) 304 /* XXX or should we panic? */ 305 device_printf(self, "Could not tear down irq, %d\n", 306 err); 307 sc->sc_intr_hdl = NULL; 308 } 309 if (irq_err && ih_err) { 310 err = bus_teardown_intr(self, irq_err, ih_err); 311 312 if (err) 313 device_printf(self, "Could not tear down irq, %d\n", 314 err); 315 ih_err = NULL; 316 } 317 if (irq_err) { 318 bus_release_resource(self, SYS_RES_IRQ, 0, irq_err); 319 irq_err = NULL; 320 } 321 if (sc->sc_irq_res) { 322 bus_release_resource(self, SYS_RES_IRQ, 1, sc->sc_irq_res); 323 sc->sc_irq_res = NULL; 324 } 325 if (sc->sc_io_res) { 326 bus_release_resource(self, SYS_RES_MEMORY, 0, 327 sc->sc_io_res); 328 sc->sc_io_res = NULL; 329 } 330 usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); 331 332 return (0); 333 } 334 335 static int 336 err_intr(void *arg) 337 { 338 ehci_softc_t *sc = arg; 339 unsigned cause; 340 341 cause = EREAD4(sc, USB_BRIDGE_INTR_CAUSE); 342 if (cause) { 343 printf("USB error: "); 344 if (cause & MV_USB_ADDR_DECODE_ERR) { 345 uint32_t addr; 346 347 addr = EREAD4(sc, USB_BRIDGE_ERR_ADDR); 348 printf("address decoding error (addr=%#x)\n", addr); 349 } 350 if (cause & MV_USB_HOST_UNDERFLOW) 351 printf("host underflow\n"); 352 if (cause & MV_USB_HOST_OVERFLOW) 353 printf("host overflow\n"); 354 if (cause & MV_USB_DEVICE_UNDERFLOW) 355 printf("device underflow\n"); 356 if (cause & ~(MV_USB_ADDR_DECODE_ERR | MV_USB_HOST_UNDERFLOW | 357 MV_USB_HOST_OVERFLOW | MV_USB_DEVICE_UNDERFLOW)) 358 printf("unknown cause (cause=%#x)\n", cause); 359 360 EWRITE4(sc, USB_BRIDGE_INTR_CAUSE, 0); 361 } 362 return (FILTER_HANDLED); 363 } 364 365 static device_method_t ehci_methods[] = { 366 /* Device interface */ 367 DEVMETHOD(device_probe, mv_ehci_probe), 368 DEVMETHOD(device_attach, mv_ehci_attach), 369 DEVMETHOD(device_detach, mv_ehci_detach), 370 DEVMETHOD(device_suspend, bus_generic_suspend), 371 DEVMETHOD(device_resume, bus_generic_resume), 372 DEVMETHOD(device_shutdown, bus_generic_shutdown), 373 374 DEVMETHOD_END 375 }; 376 377 static driver_t ehci_driver = { 378 "ehci", 379 ehci_methods, 380 sizeof(ehci_softc_t), 381 }; 382 383 DRIVER_MODULE(ehci_mv, simplebus, ehci_driver, 0, 0); 384 MODULE_DEPEND(ehci_mv, usb, 1, 1, 1); 385