1 /* $OpenBSD: xhci_pci.c,v 1.1 2014/03/08 14:34:12 mpi 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/kernel.h> 35 #include <sys/rwlock.h> 36 #include <sys/device.h> 37 #include <sys/timeout.h> 38 #include <sys/queue.h> 39 40 #include <machine/bus.h> 41 42 #include <dev/pci/pcidevs.h> 43 #include <dev/pci/pcivar.h> 44 45 #include <dev/usb/usb.h> 46 #include <dev/usb/usbdi.h> 47 #include <dev/usb/usbdivar.h> 48 #include <dev/usb/usb_mem.h> 49 50 #include <dev/usb/xhcireg.h> 51 #include <dev/usb/xhcivar.h> 52 53 #ifdef XHCI_DEBUG 54 #define DPRINTF(x) if (xhcidebug) printf x 55 extern int xhcidebug; 56 #else 57 #define DPRINTF(x) 58 #endif 59 60 struct xhci_pci_softc { 61 struct xhci_softc sc; 62 pci_chipset_tag_t sc_pc; 63 pcitag_t sc_tag; 64 void *sc_ih; /* interrupt vectoring */ 65 }; 66 67 int xhci_pci_match(struct device *, void *, void *); 68 void xhci_pci_attach(struct device *, struct device *, void *); 69 int xhci_pci_detach(struct device *, int); 70 void xhci_pci_takecontroller(struct xhci_pci_softc *, int); 71 72 struct cfattach xhci_pci_ca = { 73 sizeof(struct xhci_pci_softc), xhci_pci_match, xhci_pci_attach, 74 xhci_pci_detach, xhci_activate 75 }; 76 77 int 78 xhci_pci_match(struct device *parent, void *match, void *aux) 79 { 80 struct pci_attach_args *pa = (struct pci_attach_args *) aux; 81 82 if (PCI_CLASS(pa->pa_class) == PCI_CLASS_SERIALBUS && 83 PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_SERIALBUS_USB && 84 PCI_INTERFACE(pa->pa_class) == PCI_INTERFACE_XHCI) 85 return (1); 86 87 return (0); 88 } 89 90 void 91 xhci_pci_attach(struct device *parent, struct device *self, void *aux) 92 { 93 struct xhci_pci_softc *psc = (struct xhci_pci_softc *)self; 94 struct pci_attach_args *pa = (struct pci_attach_args *)aux; 95 const char *intrstr; 96 const char *vendor; 97 pci_intr_handle_t ih; 98 pcireg_t reg; 99 int error; 100 101 reg = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_CBMEM); 102 if (pci_mapreg_map(pa, PCI_CBMEM, reg, 0, &psc->sc.iot, &psc->sc.ioh, 103 NULL, &psc->sc.sc_size, 0)) { 104 printf(": can't map mem space\n"); 105 return; 106 } 107 108 psc->sc_pc = pa->pa_pc; 109 psc->sc_tag = pa->pa_tag; 110 psc->sc.sc_bus.dmatag = pa->pa_dmat; 111 112 /* Map and establish the interrupt. */ 113 if (pci_intr_map(pa, &ih) != 0) { 114 printf(": couldn't map interrupt\n"); 115 goto unmap_ret; 116 } 117 intrstr = pci_intr_string(pa->pa_pc, ih); 118 119 psc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_USB, 120 xhci_intr, psc, psc->sc.sc_bus.bdev.dv_xname); 121 if (psc->sc_ih == NULL) { 122 printf(": couldn't establish interrupt"); 123 if (intrstr != NULL) 124 printf(" at %s", intrstr); 125 printf("\n"); 126 goto unmap_ret; 127 } 128 printf(": %s\n", intrstr); 129 130 /* Figure out vendor for root hub descriptor. */ 131 vendor = pci_findvendor(pa->pa_id); 132 psc->sc.sc_id_vendor = PCI_VENDOR(pa->pa_id); 133 if (vendor) 134 strlcpy(psc->sc.sc_vendor, vendor, sizeof(psc->sc.sc_vendor)); 135 else 136 snprintf(psc->sc.sc_vendor, sizeof(psc->sc.sc_vendor), 137 "vendor 0x%04x", PCI_VENDOR(pa->pa_id)); 138 139 xhci_pci_takecontroller(psc, 0); 140 141 if (xhci_init(&psc->sc)) { 142 printf("%s: init failed, error=%d\n", 143 psc->sc.sc_bus.bdev.dv_xname, error); 144 goto disestablish_ret; 145 } 146 147 /* Attach usb device. */ 148 psc->sc.sc_child = config_found(self, &psc->sc.sc_bus, usbctlprint); 149 150 return; 151 152 disestablish_ret: 153 pci_intr_disestablish(psc->sc_pc, psc->sc_ih); 154 unmap_ret: 155 bus_space_unmap(psc->sc.iot, psc->sc.ioh, psc->sc.sc_size); 156 } 157 158 int 159 xhci_pci_detach(struct device *self, int flags) 160 { 161 struct xhci_pci_softc *psc = (struct xhci_pci_softc *)self; 162 int rv; 163 164 rv = xhci_detach(&psc->sc, flags); 165 if (rv) 166 return (rv); 167 if (psc->sc_ih != NULL) { 168 pci_intr_disestablish(psc->sc_pc, psc->sc_ih); 169 psc->sc_ih = NULL; 170 } 171 if (psc->sc.sc_size) { 172 bus_space_unmap(psc->sc.iot, psc->sc.ioh, psc->sc.sc_size); 173 psc->sc.sc_size = 0; 174 } 175 return (0); 176 } 177 178 void 179 xhci_pci_takecontroller(struct xhci_pci_softc *psc, int silent) 180 { 181 uint32_t cparams, xecp, eec; 182 uint8_t bios_sem; 183 int i; 184 185 cparams = XREAD4(&psc->sc, XHCI_HCCPARAMS); 186 eec = -1; 187 188 /* Synchronise with the BIOS if it owns the controller. */ 189 for (xecp = XHCI_HCC_XECP(cparams) << 2; xecp != 0; 190 xecp = XHCI_XECP_NEXT(eec) << 2) { 191 eec = XREAD4(&psc->sc, xecp); 192 if (XHCI_XECP_ID(eec) != XHCI_ID_USB_LEGACY) 193 continue; 194 bios_sem = XREAD1(&psc->sc, xecp + XHCI_XECP_BIOS_SEM); 195 if (bios_sem) { 196 XWRITE1(&psc->sc, xecp + XHCI_XECP_OS_SEM, 1); 197 DPRINTF(("%s: waiting for BIOS to give up control\n", 198 psc->sc.sc_bus.bdev.dv_xname)); 199 for (i = 0; i < 5000; i++) { 200 bios_sem = XREAD1(&psc->sc, xecp + 201 XHCI_XECP_BIOS_SEM); 202 if (bios_sem == 0) 203 break; 204 DELAY(1000); 205 } 206 if (silent == 0 && bios_sem) 207 printf("%s: timed out waiting for BIOS\n", 208 psc->sc.sc_bus.bdev.dv_xname); 209 } 210 } 211 } 212