1/* $OpenBSD: xhci_pci.c,v 1.13 2024/05/24 06:02:58 jsg Exp $ */ 2 3/* 4 * Copyright (c) 2001, 2002 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 (lennart@augustsson.net). 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/rwlock.h> 35#include <sys/device.h> 36 37#include <machine/bus.h> 38 39#include <dev/pci/pcidevs.h> 40#include <dev/pci/pcivar.h> 41 42#include <dev/usb/usb.h> 43#include <dev/usb/usbdi.h> 44#include <dev/usb/usbdivar.h> 45 46#include <dev/usb/xhcireg.h> 47#include <dev/usb/xhcivar.h> 48 49#ifdef XHCI_DEBUG 50#define DPRINTF(x) if (xhcidebug) printf x 51extern int xhcidebug; 52#else 53#define DPRINTF(x) 54#endif 55 56struct xhci_pci_softc { 57 struct xhci_softc sc; 58 pci_chipset_tag_t sc_pc; 59 pcitag_t sc_tag; 60 pcireg_t sc_id; 61 void *sc_ih; /* interrupt vectoring */ 62}; 63 64int xhci_pci_match(struct device *, void *, void *); 65void xhci_pci_attach(struct device *, struct device *, void *); 66int xhci_pci_detach(struct device *, int); 67int xhci_pci_activate(struct device *, int); 68void xhci_pci_takecontroller(struct xhci_pci_softc *, int); 69 70const struct cfattach xhci_pci_ca = { 71 sizeof(struct xhci_pci_softc), xhci_pci_match, xhci_pci_attach, 72 xhci_pci_detach, xhci_pci_activate 73}; 74 75int 76xhci_pci_match(struct device *parent, void *match, void *aux) 77{ 78 struct pci_attach_args *pa = (struct pci_attach_args *) aux; 79 80 if (PCI_CLASS(pa->pa_class) == PCI_CLASS_SERIALBUS && 81 PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_SERIALBUS_USB && 82 PCI_INTERFACE(pa->pa_class) == PCI_INTERFACE_XHCI) 83 return (1); 84 85 return (0); 86} 87 88static int 89xhci_pci_port_route(struct xhci_pci_softc *psc) 90{ 91 pcireg_t val; 92 93 /* 94 * Check USB3 Port Routing Mask register that indicates the ports 95 * can be changed from OS, and turn on by USB3 Port SS Enable register. 96 */ 97 val = pci_conf_read(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_USB3PRM); 98 DPRINTF(("%s: USB3PRM / USB3.0 configurable ports: 0x%08x\n", 99 psc->sc.sc_bus.bdev.dv_xname, val)); 100 101 pci_conf_write(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_USB3_PSSEN, val); 102 val = pci_conf_read(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_USB3_PSSEN); 103 DPRINTF(("%s: USB3_PSSEN / Enabled USB3.0 ports under xHCI: 0x%08x\n", 104 psc->sc.sc_bus.bdev.dv_xname, val)); 105 106 /* 107 * Check USB2 Port Routing Mask register that indicates the USB2.0 108 * ports to be controlled by xHCI HC, and switch them to xHCI HC. 109 */ 110 val = pci_conf_read(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_XUSB2PRM); 111 DPRINTF(("%s: XUSB2PRM / USB2.0 ports can switch from EHCI to xHCI:" 112 "0x%08x\n", psc->sc.sc_bus.bdev.dv_xname, val)); 113 114 pci_conf_write(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_XUSB2PR, val); 115 val = pci_conf_read(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_XUSB2PR); 116 DPRINTF(("%s: XUSB2PR / USB2.0 ports under xHCI: 0x%08x\n", 117 psc->sc.sc_bus.bdev.dv_xname, val)); 118 119 return (0); 120} 121 122 123void 124xhci_pci_attach(struct device *parent, struct device *self, void *aux) 125{ 126 struct xhci_pci_softc *psc = (struct xhci_pci_softc *)self; 127 struct pci_attach_args *pa = (struct pci_attach_args *)aux; 128 const char *intrstr; 129 const char *vendor; 130 pci_intr_handle_t ih; 131 pcireg_t reg; 132 int error; 133 134 reg = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_CBMEM); 135 if (pci_mapreg_map(pa, PCI_CBMEM, reg, 0, &psc->sc.iot, &psc->sc.ioh, 136 NULL, &psc->sc.sc_size, 0)) { 137 printf(": can't map mem space\n"); 138 return; 139 } 140 141 psc->sc_pc = pa->pa_pc; 142 psc->sc_tag = pa->pa_tag; 143 psc->sc_id = pa->pa_id; 144 psc->sc.sc_bus.dmatag = pa->pa_dmat; 145 146 /* Handle quirks */ 147 switch (PCI_VENDOR(pa->pa_id)) { 148 case PCI_VENDOR_FRESCO: 149 /* FL1000 / FL1400 claim MSI support but do not support MSI */ 150 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_FRESCO_FL1000 || 151 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_FRESCO_FL1400) 152 pa->pa_flags &= ~PCI_FLAGS_MSI_ENABLED; 153 break; 154 } 155 156 /* Map and establish the interrupt. */ 157 if (pci_intr_map_msix(pa, 0, &ih) != 0 && 158 pci_intr_map_msi(pa, &ih) != 0 && pci_intr_map(pa, &ih) != 0) { 159 printf(": couldn't map interrupt\n"); 160 goto unmap_ret; 161 } 162 intrstr = pci_intr_string(pa->pa_pc, ih); 163 164 psc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_USB | IPL_MPSAFE, 165 xhci_intr, psc, psc->sc.sc_bus.bdev.dv_xname); 166 if (psc->sc_ih == NULL) { 167 printf(": couldn't establish interrupt"); 168 if (intrstr != NULL) 169 printf(" at %s", intrstr); 170 printf("\n"); 171 goto unmap_ret; 172 } 173 printf(": %s", intrstr); 174 175 /* Figure out vendor for root hub descriptor. */ 176 vendor = pci_findvendor(pa->pa_id); 177 psc->sc.sc_id_vendor = PCI_VENDOR(pa->pa_id); 178 if (vendor) 179 strlcpy(psc->sc.sc_vendor, vendor, sizeof(psc->sc.sc_vendor)); 180 else 181 snprintf(psc->sc.sc_vendor, sizeof(psc->sc.sc_vendor), 182 "vendor 0x%04x", PCI_VENDOR(pa->pa_id)); 183 184 xhci_pci_takecontroller(psc, 0); 185 186 if ((error = xhci_init(&psc->sc)) != 0) { 187 printf("%s: init failed, error=%d\n", 188 psc->sc.sc_bus.bdev.dv_xname, error); 189 goto disestablish_ret; 190 } 191 192 if (PCI_VENDOR(psc->sc_id) == PCI_VENDOR_INTEL) 193 xhci_pci_port_route(psc); 194 195 /* Attach usb device. */ 196 config_found(self, &psc->sc.sc_bus, usbctlprint); 197 198 /* Now that the stack is ready, config' the HC and enable interrupts. */ 199 xhci_config(&psc->sc); 200 201 return; 202 203disestablish_ret: 204 pci_intr_disestablish(psc->sc_pc, psc->sc_ih); 205unmap_ret: 206 bus_space_unmap(psc->sc.iot, psc->sc.ioh, psc->sc.sc_size); 207} 208 209int 210xhci_pci_detach(struct device *self, int flags) 211{ 212 struct xhci_pci_softc *psc = (struct xhci_pci_softc *)self; 213 int rv; 214 215 rv = xhci_detach(self, flags); 216 if (rv) 217 return (rv); 218 if (psc->sc_ih != NULL) { 219 pci_intr_disestablish(psc->sc_pc, psc->sc_ih); 220 psc->sc_ih = NULL; 221 } 222 if (psc->sc.sc_size) { 223 bus_space_unmap(psc->sc.iot, psc->sc.ioh, psc->sc.sc_size); 224 psc->sc.sc_size = 0; 225 } 226 return (0); 227} 228 229int 230xhci_pci_activate(struct device *self, int act) 231{ 232 struct xhci_pci_softc *psc = (struct xhci_pci_softc *)self; 233 234 switch (act) { 235 case DVACT_RESUME: 236 if (PCI_VENDOR(psc->sc_id) == PCI_VENDOR_INTEL) 237 xhci_pci_port_route(psc); 238 break; 239 default: 240 break; 241 } 242 243 return (xhci_activate(self, act)); 244} 245 246 247void 248xhci_pci_takecontroller(struct xhci_pci_softc *psc, int silent) 249{ 250 uint32_t cparams, xecp, eec; 251 uint8_t bios_sem; 252 int i; 253 254 cparams = XREAD4(&psc->sc, XHCI_HCCPARAMS); 255 if (cparams == 0xffffffff) 256 return; 257 258 eec = -1; 259 260 /* Synchronise with the BIOS if it owns the controller. */ 261 for (xecp = XHCI_HCC_XECP(cparams) << 2; 262 xecp != 0 && XHCI_XECP_NEXT(eec); 263 xecp += XHCI_XECP_NEXT(eec) << 2) { 264 eec = XREAD4(&psc->sc, xecp); 265 if (eec == 0xffffffff) 266 return; 267 if (XHCI_XECP_ID(eec) != XHCI_ID_USB_LEGACY) 268 continue; 269 bios_sem = XREAD1(&psc->sc, xecp + XHCI_XECP_BIOS_SEM); 270 if (bios_sem) { 271 XWRITE1(&psc->sc, xecp + XHCI_XECP_OS_SEM, 1); 272 DPRINTF(("%s: waiting for BIOS to give up control\n", 273 psc->sc.sc_bus.bdev.dv_xname)); 274 for (i = 0; i < 5000; i++) { 275 bios_sem = XREAD1(&psc->sc, xecp + 276 XHCI_XECP_BIOS_SEM); 277 if (bios_sem == 0) 278 break; 279 DELAY(1000); 280 } 281 if (silent == 0 && bios_sem) 282 printf("%s: timed out waiting for BIOS\n", 283 psc->sc.sc_bus.bdev.dv_xname); 284 } 285 } 286} 287