xhci_pci.c revision 278278
1226026Sdelphij/*- 2226026Sdelphij * Copyright (c) 2010 Hans Petter Selasky. All rights reserved. 3226026Sdelphij * 4226026Sdelphij * Redistribution and use in source and binary forms, with or without 5226026Sdelphij * modification, are permitted provided that the following conditions 6226026Sdelphij * are met: 7226026Sdelphij * 1. Redistributions of source code must retain the above copyright 8226026Sdelphij * notice, this list of conditions and the following disclaimer. 9226026Sdelphij * 2. Redistributions in binary form must reproduce the above copyright 10226026Sdelphij * notice, this list of conditions and the following disclaimer in the 11226026Sdelphij * documentation and/or other materials provided with the distribution. 12226026Sdelphij * 13226026Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14226026Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15226026Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16226026Sdelphij * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17226026Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18226026Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19226026Sdelphij * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20226026Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21226026Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22226026Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23226026Sdelphij * SUCH DAMAGE. 24226026Sdelphij */ 25226026Sdelphij 26226026Sdelphij#include <sys/cdefs.h> 27226026Sdelphij__FBSDID("$FreeBSD: stable/10/sys/dev/usb/controller/xhci_pci.c 278278 2015-02-05 20:03:02Z hselasky $"); 28226026Sdelphij 29226026Sdelphij#include <sys/stdint.h> 30226026Sdelphij#include <sys/stddef.h> 31226026Sdelphij#include <sys/param.h> 32226026Sdelphij#include <sys/queue.h> 33226026Sdelphij#include <sys/types.h> 34226026Sdelphij#include <sys/systm.h> 35226026Sdelphij#include <sys/kernel.h> 36226026Sdelphij#include <sys/bus.h> 37226026Sdelphij#include <sys/module.h> 38226026Sdelphij#include <sys/lock.h> 39226026Sdelphij#include <sys/mutex.h> 40226026Sdelphij#include <sys/condvar.h> 41226026Sdelphij#include <sys/sysctl.h> 42226026Sdelphij#include <sys/sx.h> 43226026Sdelphij#include <sys/unistd.h> 44226026Sdelphij#include <sys/callout.h> 45226026Sdelphij#include <sys/malloc.h> 46226026Sdelphij#include <sys/priv.h> 47226026Sdelphij 48226026Sdelphij#include <dev/usb/usb.h> 49226026Sdelphij#include <dev/usb/usbdi.h> 50226026Sdelphij 51226026Sdelphij#include <dev/usb/usb_core.h> 52226026Sdelphij#include <dev/usb/usb_busdma.h> 53226026Sdelphij#include <dev/usb/usb_process.h> 54226026Sdelphij#include <dev/usb/usb_util.h> 55226026Sdelphij 56226026Sdelphij#include <dev/usb/usb_controller.h> 57226026Sdelphij#include <dev/usb/usb_bus.h> 58226026Sdelphij#include <dev/usb/usb_pci.h> 59226026Sdelphij#include <dev/usb/controller/xhci.h> 60226026Sdelphij#include <dev/usb/controller/xhcireg.h> 61226026Sdelphij#include "usb_if.h" 62226026Sdelphij 63226026Sdelphijstatic device_probe_t xhci_pci_probe; 64226026Sdelphijstatic device_attach_t xhci_pci_attach; 65226026Sdelphijstatic device_detach_t xhci_pci_detach; 66226026Sdelphijstatic usb_take_controller_t xhci_pci_take_controller; 67226026Sdelphij 68226026Sdelphijstatic device_method_t xhci_device_methods[] = { 69226026Sdelphij /* device interface */ 70226026Sdelphij DEVMETHOD(device_probe, xhci_pci_probe), 71226026Sdelphij DEVMETHOD(device_attach, xhci_pci_attach), 72226026Sdelphij DEVMETHOD(device_detach, xhci_pci_detach), 73226026Sdelphij DEVMETHOD(device_suspend, bus_generic_suspend), 74226026Sdelphij DEVMETHOD(device_resume, bus_generic_resume), 75226026Sdelphij DEVMETHOD(device_shutdown, bus_generic_shutdown), 76226026Sdelphij DEVMETHOD(usb_take_controller, xhci_pci_take_controller), 77226026Sdelphij 78226026Sdelphij DEVMETHOD_END 79226026Sdelphij}; 80226026Sdelphij 81226026Sdelphijstatic driver_t xhci_driver = { 82226026Sdelphij .name = "xhci", 83226026Sdelphij .methods = xhci_device_methods, 84226026Sdelphij .size = sizeof(struct xhci_softc), 85226026Sdelphij}; 86226026Sdelphij 87226026Sdelphijstatic devclass_t xhci_devclass; 88226026Sdelphij 89226026SdelphijDRIVER_MODULE(xhci, pci, xhci_driver, xhci_devclass, 0, 0); 90226026SdelphijMODULE_DEPEND(xhci, usb, 1, 1, 1); 91226026Sdelphij 92226026Sdelphij 93226026Sdelphijstatic const char * 94226026Sdelphijxhci_pci_match(device_t self) 95226026Sdelphij{ 96226026Sdelphij uint32_t device_id = pci_get_devid(self); 97226026Sdelphij 98226026Sdelphij switch (device_id) { 99226026Sdelphij case 0x01941033: 100226026Sdelphij return ("NEC uPD720200 USB 3.0 controller"); 101226026Sdelphij 102226026Sdelphij case 0x10421b21: 103226026Sdelphij return ("ASMedia ASM1042 USB 3.0 controller"); 104226026Sdelphij 105226026Sdelphij case 0x0f358086: 106226026Sdelphij return ("Intel Intel BayTrail USB 3.0 controller"); 107226026Sdelphij case 0x9c318086: 108226026Sdelphij case 0x1e318086: 109226026Sdelphij return ("Intel Panther Point USB 3.0 controller"); 110226026Sdelphij case 0x8c318086: 111226026Sdelphij return ("Intel Lynx Point USB 3.0 controller"); 112226026Sdelphij case 0x8cb18086: 113226026Sdelphij return ("Intel Wildcat Point USB 3.0 controller"); 114226026Sdelphij 115226026Sdelphij default: 116226026Sdelphij break; 117226026Sdelphij } 118226026Sdelphij 119226026Sdelphij if ((pci_get_class(self) == PCIC_SERIALBUS) 120226026Sdelphij && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) 121226026Sdelphij && (pci_get_progif(self) == PCIP_SERIALBUS_USB_XHCI)) { 122226026Sdelphij return ("XHCI (generic) USB 3.0 controller"); 123226026Sdelphij } 124226026Sdelphij return (NULL); /* dunno */ 125226026Sdelphij} 126226026Sdelphij 127226026Sdelphijstatic int 128226026Sdelphijxhci_pci_probe(device_t self) 129226026Sdelphij{ 130226026Sdelphij const char *desc = xhci_pci_match(self); 131226026Sdelphij 132226026Sdelphij if (desc) { 133226026Sdelphij device_set_desc(self, desc); 134226026Sdelphij return (0); 135226026Sdelphij } else { 136226026Sdelphij return (ENXIO); 137226026Sdelphij } 138226026Sdelphij} 139226026Sdelphij 140226026Sdelphijstatic int xhci_use_msi = 1; 141226026SdelphijTUNABLE_INT("hw.usb.xhci.msi", &xhci_use_msi); 142226026Sdelphij 143226026Sdelphijstatic void 144226026Sdelphijxhci_interrupt_poll(void *_sc) 145226026Sdelphij{ 146226026Sdelphij struct xhci_softc *sc = _sc; 147226026Sdelphij USB_BUS_UNLOCK(&sc->sc_bus); 148226026Sdelphij xhci_interrupt(sc); 149226026Sdelphij USB_BUS_LOCK(&sc->sc_bus); 150226026Sdelphij usb_callout_reset(&sc->sc_callout, 1, (void *)&xhci_interrupt_poll, sc); 151226026Sdelphij} 152226026Sdelphij 153226026Sdelphijstatic int 154226026Sdelphijxhci_pci_port_route(device_t self, uint32_t set, uint32_t clear) 155226026Sdelphij{ 156226026Sdelphij uint32_t temp; 157226026Sdelphij uint32_t usb3_mask; 158226026Sdelphij uint32_t usb2_mask; 159226026Sdelphij 160226026Sdelphij temp = pci_read_config(self, PCI_XHCI_INTEL_USB3_PSSEN, 4) | 161226026Sdelphij pci_read_config(self, PCI_XHCI_INTEL_XUSB2PR, 4); 162226026Sdelphij 163226026Sdelphij temp |= set; 164226026Sdelphij temp &= ~clear; 165226026Sdelphij 166226026Sdelphij /* Don't set bits which the hardware doesn't support */ 167226026Sdelphij usb3_mask = pci_read_config(self, PCI_XHCI_INTEL_USB3PRM, 4); 168226026Sdelphij usb2_mask = pci_read_config(self, PCI_XHCI_INTEL_USB2PRM, 4); 169226026Sdelphij 170226026Sdelphij pci_write_config(self, PCI_XHCI_INTEL_USB3_PSSEN, temp & usb3_mask, 4); 171226026Sdelphij pci_write_config(self, PCI_XHCI_INTEL_XUSB2PR, temp & usb2_mask, 4); 172226026Sdelphij 173226026Sdelphij device_printf(self, "Port routing mask set to 0x%08x\n", temp); 174226026Sdelphij 175226026Sdelphij return (0); 176226026Sdelphij} 177226026Sdelphij 178226026Sdelphijstatic int 179226026Sdelphijxhci_pci_attach(device_t self) 180226026Sdelphij{ 181226026Sdelphij struct xhci_softc *sc = device_get_softc(self); 182226026Sdelphij int count, err, rid; 183226026Sdelphij 184226026Sdelphij rid = PCI_XHCI_CBMEM; 185226026Sdelphij sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, 186226026Sdelphij RF_ACTIVE); 187226026Sdelphij if (!sc->sc_io_res) { 188226026Sdelphij device_printf(self, "Could not map memory\n"); 189226026Sdelphij return (ENOMEM); 190226026Sdelphij } 191226026Sdelphij sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); 192226026Sdelphij sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); 193226026Sdelphij sc->sc_io_size = rman_get_size(sc->sc_io_res); 194226026Sdelphij 195226026Sdelphij if (xhci_init(sc, self)) { 196226026Sdelphij device_printf(self, "Could not initialize softc\n"); 197226026Sdelphij bus_release_resource(self, SYS_RES_MEMORY, PCI_XHCI_CBMEM, 198226026Sdelphij sc->sc_io_res); 199226026Sdelphij return (ENXIO); 200226026Sdelphij } 201226026Sdelphij 202226026Sdelphij pci_enable_busmaster(self); 203226026Sdelphij 204226026Sdelphij usb_callout_init_mtx(&sc->sc_callout, &sc->sc_bus.bus_mtx, 0); 205226026Sdelphij 206226026Sdelphij rid = 0; 207226026Sdelphij if (xhci_use_msi) { 208226026Sdelphij count = 1; 209226026Sdelphij if (pci_alloc_msi(self, &count) == 0) { 210226026Sdelphij if (bootverbose) 211226026Sdelphij device_printf(self, "MSI enabled\n"); 212226026Sdelphij rid = 1; 213226026Sdelphij } 214226026Sdelphij } 215226026Sdelphij sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, 216226026Sdelphij RF_ACTIVE | (rid != 0 ? 0 : RF_SHAREABLE)); 217226026Sdelphij if (sc->sc_irq_res == NULL) { 218226026Sdelphij pci_release_msi(self); 219226026Sdelphij device_printf(self, "Could not allocate IRQ\n"); 220226026Sdelphij /* goto error; FALLTHROUGH - use polling */ 221226026Sdelphij } 222226026Sdelphij sc->sc_bus.bdev = device_add_child(self, "usbus", -1); 223226026Sdelphij if (sc->sc_bus.bdev == NULL) { 224226026Sdelphij device_printf(self, "Could not add USB device\n"); 225226026Sdelphij goto error; 226226026Sdelphij } 227226026Sdelphij device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); 228226026Sdelphij 229226026Sdelphij sprintf(sc->sc_vendor, "0x%04x", pci_get_vendor(self)); 230226026Sdelphij 231226026Sdelphij if (sc->sc_irq_res != NULL) { 232226026Sdelphij err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, 233226026Sdelphij NULL, (driver_intr_t *)xhci_interrupt, sc, &sc->sc_intr_hdl); 234226026Sdelphij if (err != 0) { 235226026Sdelphij bus_release_resource(self, SYS_RES_IRQ, 236226026Sdelphij rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); 237226026Sdelphij sc->sc_irq_res = NULL; 238226026Sdelphij pci_release_msi(self); 239226026Sdelphij device_printf(self, "Could not setup IRQ, err=%d\n", err); 240226026Sdelphij sc->sc_intr_hdl = NULL; 241226026Sdelphij } 242226026Sdelphij } 243226026Sdelphij if (sc->sc_irq_res == NULL || sc->sc_intr_hdl == NULL) { 244226026Sdelphij if (xhci_use_polling() != 0) { 245226026Sdelphij device_printf(self, "Interrupt polling at %dHz\n", hz); 246226026Sdelphij USB_BUS_LOCK(&sc->sc_bus); 247226026Sdelphij xhci_interrupt_poll(sc); 248226026Sdelphij USB_BUS_UNLOCK(&sc->sc_bus); 249226026Sdelphij } else 250226026Sdelphij goto error; 251226026Sdelphij } 252226026Sdelphij 253226026Sdelphij /* On Intel chipsets reroute ports from EHCI to XHCI controller. */ 254226026Sdelphij switch (pci_get_devid(self)) { 255226026Sdelphij case 0x0f358086: /* BayTrail */ 256226026Sdelphij case 0x9c318086: /* Panther Point */ 257226026Sdelphij case 0x1e318086: /* Panther Point */ 258226026Sdelphij case 0x8c318086: /* Lynx Point */ 259226026Sdelphij case 0x8cb18086: /* Wildcat Point */ 260226026Sdelphij sc->sc_port_route = &xhci_pci_port_route; 261226026Sdelphij sc->sc_imod_default = XHCI_IMOD_DEFAULT_LP; 262226026Sdelphij break; 263226026Sdelphij default: 264226026Sdelphij break; 265226026Sdelphij } 266226026Sdelphij 267226026Sdelphij xhci_pci_take_controller(self); 268226026Sdelphij 269226026Sdelphij err = xhci_halt_controller(sc); 270226026Sdelphij 271226026Sdelphij if (err == 0) 272226026Sdelphij err = xhci_start_controller(sc); 273226026Sdelphij 274226026Sdelphij if (err == 0) 275226026Sdelphij err = device_probe_and_attach(sc->sc_bus.bdev); 276226026Sdelphij 277226026Sdelphij if (err) { 278226026Sdelphij device_printf(self, "XHCI halt/start/probe failed err=%d\n", err); 279226026Sdelphij goto error; 280226026Sdelphij } 281226026Sdelphij return (0); 282226026Sdelphij 283226026Sdelphijerror: 284226026Sdelphij xhci_pci_detach(self); 285226026Sdelphij return (ENXIO); 286226026Sdelphij} 287226026Sdelphij 288226026Sdelphijstatic int 289226026Sdelphijxhci_pci_detach(device_t self) 290226026Sdelphij{ 291226026Sdelphij struct xhci_softc *sc = device_get_softc(self); 292226026Sdelphij device_t bdev; 293226026Sdelphij 294226026Sdelphij if (sc->sc_bus.bdev != NULL) { 295226026Sdelphij bdev = sc->sc_bus.bdev; 296226026Sdelphij device_detach(bdev); 297226026Sdelphij device_delete_child(self, bdev); 298226026Sdelphij } 299226026Sdelphij /* during module unload there are lots of children leftover */ 300226026Sdelphij device_delete_children(self); 301226026Sdelphij 302226026Sdelphij usb_callout_drain(&sc->sc_callout); 303226026Sdelphij xhci_halt_controller(sc); 304226026Sdelphij 305226026Sdelphij pci_disable_busmaster(self); 306226026Sdelphij 307226026Sdelphij if (sc->sc_irq_res && sc->sc_intr_hdl) { 308226026Sdelphij bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); 309226026Sdelphij sc->sc_intr_hdl = NULL; 310226026Sdelphij } 311226026Sdelphij if (sc->sc_irq_res) { 312226026Sdelphij bus_release_resource(self, SYS_RES_IRQ, 313226026Sdelphij rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); 314226026Sdelphij sc->sc_irq_res = NULL; 315226026Sdelphij pci_release_msi(self); 316226026Sdelphij } 317226026Sdelphij if (sc->sc_io_res) { 318226026Sdelphij bus_release_resource(self, SYS_RES_MEMORY, PCI_XHCI_CBMEM, 319226026Sdelphij sc->sc_io_res); 320226026Sdelphij sc->sc_io_res = NULL; 321226026Sdelphij } 322226026Sdelphij 323226026Sdelphij xhci_uninit(sc); 324226026Sdelphij 325226026Sdelphij return (0); 326226026Sdelphij} 327226026Sdelphij 328226026Sdelphijstatic int 329226026Sdelphijxhci_pci_take_controller(device_t self) 330226026Sdelphij{ 331226026Sdelphij struct xhci_softc *sc = device_get_softc(self); 332226026Sdelphij uint32_t cparams; 333226026Sdelphij uint32_t eecp; 334226026Sdelphij uint32_t eec; 335226026Sdelphij uint16_t to; 336226026Sdelphij uint8_t bios_sem; 337226026Sdelphij 338226026Sdelphij cparams = XREAD4(sc, capa, XHCI_HCSPARAMS0); 339226026Sdelphij 340226026Sdelphij eec = -1; 341226026Sdelphij 342226026Sdelphij /* Synchronise with the BIOS if it owns the controller. */ 343226026Sdelphij for (eecp = XHCI_HCS0_XECP(cparams) << 2; eecp != 0 && XHCI_XECP_NEXT(eec); 344226026Sdelphij eecp += XHCI_XECP_NEXT(eec) << 2) { 345226026Sdelphij eec = XREAD4(sc, capa, eecp); 346226026Sdelphij 347226026Sdelphij if (XHCI_XECP_ID(eec) != XHCI_ID_USB_LEGACY) 348226026Sdelphij continue; 349226026Sdelphij bios_sem = XREAD1(sc, capa, eecp + 350226026Sdelphij XHCI_XECP_BIOS_SEM); 351226026Sdelphij if (bios_sem == 0) 352226026Sdelphij continue; 353226026Sdelphij device_printf(sc->sc_bus.bdev, "waiting for BIOS " 354226026Sdelphij "to give up control\n"); 355226026Sdelphij XWRITE1(sc, capa, eecp + 356226026Sdelphij XHCI_XECP_OS_SEM, 1); 357226026Sdelphij to = 500; 358226026Sdelphij while (1) { 359226026Sdelphij bios_sem = XREAD1(sc, capa, eecp + 360226026Sdelphij XHCI_XECP_BIOS_SEM); 361226026Sdelphij if (bios_sem == 0) 362226026Sdelphij break; 363226026Sdelphij 364226026Sdelphij if (--to == 0) { 365226026Sdelphij device_printf(sc->sc_bus.bdev, 366226026Sdelphij "timed out waiting for BIOS\n"); 367226026Sdelphij break; 368226026Sdelphij } 369226026Sdelphij usb_pause_mtx(NULL, hz / 100); /* wait 10ms */ 370226026Sdelphij } 371226026Sdelphij } 372226026Sdelphij return (0); 373226026Sdelphij} 374226026Sdelphij