1 /* $OpenBSD: ehci_pci.c,v 1.31 2019/05/02 20:28:46 kettenis Exp $ */ 2 /* $NetBSD: ehci_pci.c,v 1.15 2004/04/23 21:13:06 itojun Exp $ */ 3 4 /* 5 * Copyright (c) 2001, 2002 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Lennart Augustsson (lennart@augustsson.net). 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/param.h> 34 #include <sys/systm.h> 35 #include <sys/kernel.h> 36 #include <sys/rwlock.h> 37 #include <sys/device.h> 38 #include <sys/timeout.h> 39 #include <sys/queue.h> 40 41 #include <machine/bus.h> 42 43 #include <dev/pci/pcidevs.h> 44 #include <dev/pci/pcivar.h> 45 46 #include <dev/usb/usb.h> 47 #include <dev/usb/usbdi.h> 48 #include <dev/usb/usbdivar.h> 49 #include <dev/usb/usb_mem.h> 50 51 #include <dev/usb/ehcireg.h> 52 #include <dev/usb/ehcivar.h> 53 54 #ifdef EHCI_DEBUG 55 #define DPRINTF(x) if (ehcidebug) printf x 56 extern int ehcidebug; 57 #else 58 #define DPRINTF(x) 59 #endif 60 61 struct ehci_pci_softc { 62 struct ehci_softc sc; 63 pci_chipset_tag_t sc_pc; 64 pcitag_t sc_tag; 65 void *sc_ih; /* interrupt vectoring */ 66 }; 67 68 int ehci_sb700_match(struct pci_attach_args *pa); 69 70 #define EHCI_SBx00_WORKAROUND_REG 0x50 71 #define EHCI_SBx00_WORKAROUND_ENABLE (1 << 3) 72 #define EHCI_VT6202_WORKAROUND_REG 0x48 73 74 int ehci_pci_match(struct device *, void *, void *); 75 void ehci_pci_attach(struct device *, struct device *, void *); 76 int ehci_pci_detach(struct device *, int); 77 int ehci_pci_activate(struct device *, int); 78 #if 0 79 void ehci_pci_givecontroller(struct ehci_pci_softc *); 80 #endif 81 void ehci_pci_takecontroller(struct ehci_pci_softc *, int); 82 83 struct cfattach ehci_pci_ca = { 84 sizeof(struct ehci_pci_softc), ehci_pci_match, ehci_pci_attach, 85 ehci_pci_detach, ehci_pci_activate 86 }; 87 88 int 89 ehci_pci_match(struct device *parent, void *match, void *aux) 90 { 91 struct pci_attach_args *pa = (struct pci_attach_args *) aux; 92 93 if (PCI_CLASS(pa->pa_class) == PCI_CLASS_SERIALBUS && 94 PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_SERIALBUS_USB && 95 PCI_INTERFACE(pa->pa_class) == PCI_INTERFACE_EHCI) 96 return (1); 97 98 return (0); 99 } 100 101 void 102 ehci_pci_attach(struct device *parent, struct device *self, void *aux) 103 { 104 struct ehci_pci_softc *sc = (struct ehci_pci_softc *)self; 105 struct pci_attach_args *pa = (struct pci_attach_args *)aux; 106 pci_chipset_tag_t pc = pa->pa_pc; 107 pcitag_t tag = pa->pa_tag; 108 char const *intrstr; 109 pci_intr_handle_t ih; 110 const char *vendor; 111 char *devname = sc->sc.sc_bus.bdev.dv_xname; 112 usbd_status r; 113 int s; 114 115 /* Map I/O registers */ 116 if (pci_mapreg_map(pa, PCI_CBMEM, PCI_MAPREG_TYPE_MEM, 0, 117 &sc->sc.iot, &sc->sc.ioh, NULL, &sc->sc.sc_size, 0)) { 118 printf(": can't map mem space\n"); 119 return; 120 } 121 122 sc->sc_pc = pc; 123 sc->sc_tag = tag; 124 sc->sc.sc_bus.dmatag = pa->pa_dmat; 125 126 /* Disable interrupts, so we don't get any spurious ones. */ 127 s = splhardusb(); 128 sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH); 129 DPRINTF(("%s: offs=%d\n", devname, sc->sc.sc_offs)); 130 EOWRITE2(&sc->sc, EHCI_USBINTR, 0); 131 132 /* Handle quirks */ 133 switch (PCI_VENDOR(pa->pa_id)) { 134 case PCI_VENDOR_ATI: 135 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_SB600_EHCI || 136 (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_SB700_EHCI && 137 pci_find_device(NULL, ehci_sb700_match))) { 138 pcireg_t value; 139 140 /* apply the ATI SB600/SB700 workaround */ 141 value = pci_conf_read(sc->sc_pc, sc->sc_tag, 142 EHCI_SBx00_WORKAROUND_REG); 143 pci_conf_write(sc->sc_pc, sc->sc_tag, 144 EHCI_SBx00_WORKAROUND_REG, value | 145 EHCI_SBx00_WORKAROUND_ENABLE); 146 } 147 break; 148 149 case PCI_VENDOR_VIATECH: 150 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_VIATECH_VT6202 && 151 (PCI_REVISION(pa->pa_class) & 0xf0) == 0x60) { 152 pcireg_t value; 153 154 /* 155 * The VT6202 defaults to a 1 usec EHCI sleep time 156 * which hogs the PCI bus *badly*. Setting bit 5 of 157 * the register makes that sleep time use the 158 * conventional 10 usec. 159 */ 160 value = pci_conf_read(sc->sc_pc, sc->sc_tag, 161 EHCI_VT6202_WORKAROUND_REG); 162 pci_conf_write(sc->sc_pc, sc->sc_tag, 163 EHCI_VT6202_WORKAROUND_REG, value | 0x20000000); 164 } 165 break; 166 } 167 168 /* Map and establish the interrupt. */ 169 if (pci_intr_map(pa, &ih)) { 170 printf(": couldn't map interrupt\n"); 171 goto unmap_ret; 172 } 173 intrstr = pci_intr_string(pc, ih); 174 sc->sc_ih = pci_intr_establish(pc, ih, IPL_USB | IPL_MPSAFE, 175 ehci_intr, sc, devname); 176 if (sc->sc_ih == NULL) { 177 printf(": couldn't establish interrupt"); 178 if (intrstr != NULL) 179 printf(" at %s", intrstr); 180 printf("\n"); 181 goto unmap_ret; 182 } 183 printf(": %s\n", intrstr); 184 185 switch(pci_conf_read(pc, tag, PCI_USBREV) & PCI_USBREV_MASK) { 186 case PCI_USBREV_PRE_1_0: 187 case PCI_USBREV_1_0: 188 case PCI_USBREV_1_1: 189 sc->sc.sc_bus.usbrev = USBREV_UNKNOWN; 190 printf("%s: pre-2.0 USB rev\n", devname); 191 goto disestablish_ret; 192 case PCI_USBREV_2_0: 193 sc->sc.sc_bus.usbrev = USBREV_2_0; 194 break; 195 default: 196 sc->sc.sc_bus.usbrev = USBREV_UNKNOWN; 197 break; 198 } 199 200 /* Figure out vendor for root hub descriptor. */ 201 vendor = pci_findvendor(pa->pa_id); 202 sc->sc.sc_id_vendor = PCI_VENDOR(pa->pa_id); 203 if (vendor) 204 strlcpy(sc->sc.sc_vendor, vendor, sizeof(sc->sc.sc_vendor)); 205 else 206 snprintf(sc->sc.sc_vendor, sizeof(sc->sc.sc_vendor), 207 "vendor 0x%04x", PCI_VENDOR(pa->pa_id)); 208 209 /* Enable workaround for dropped interrupts as required */ 210 switch (sc->sc.sc_id_vendor) { 211 case PCI_VENDOR_ATI: 212 case PCI_VENDOR_VIATECH: 213 sc->sc.sc_flags |= EHCIF_DROPPED_INTR_WORKAROUND; 214 break; 215 default: 216 break; 217 } 218 219 ehci_pci_takecontroller(sc, 0); 220 r = ehci_init(&sc->sc); 221 if (r != USBD_NORMAL_COMPLETION) { 222 printf("%s: init failed, error=%d\n", devname, r); 223 goto disestablish_ret; 224 } 225 226 /* Attach usb device. */ 227 config_found(self, &sc->sc.sc_bus, usbctlprint); 228 splx(s); 229 return; 230 231 disestablish_ret: 232 pci_intr_disestablish(sc->sc_pc, sc->sc_ih); 233 unmap_ret: 234 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 235 sc->sc.sc_size = 0; 236 splx(s); 237 } 238 239 int 240 ehci_pci_activate(struct device *self, int act) 241 { 242 struct ehci_pci_softc *sc = (struct ehci_pci_softc *)self; 243 int rv; 244 245 if (sc->sc.sc_size == 0) 246 return 0; 247 248 switch (act) { 249 case DVACT_RESUME: 250 ehci_pci_takecontroller(sc, 1); 251 break; 252 } 253 254 rv = ehci_activate(self, act); 255 256 #if 0 257 switch (act) { 258 case DVACT_POWERDOWN: 259 ehci_pci_givecontroller(sc); 260 break; 261 } 262 #endif 263 return (rv); 264 } 265 266 int 267 ehci_pci_detach(struct device *self, int flags) 268 { 269 struct ehci_pci_softc *sc = (struct ehci_pci_softc *)self; 270 int rv; 271 272 rv = ehci_detach(self, flags); 273 if (rv) 274 return (rv); 275 if (sc->sc_ih != NULL) { 276 pci_intr_disestablish(sc->sc_pc, sc->sc_ih); 277 sc->sc_ih = NULL; 278 } 279 if (sc->sc.sc_size) { 280 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 281 sc->sc.sc_size = 0; 282 } 283 return (0); 284 } 285 286 #if 0 287 void 288 ehci_pci_givecontroller(struct ehci_pci_softc *sc) 289 { 290 u_int32_t cparams, eec, legsup; 291 int eecp; 292 293 cparams = EREAD4(&sc->sc, EHCI_HCCPARAMS); 294 for (eecp = EHCI_HCC_EECP(cparams); eecp != 0; 295 eecp = EHCI_EECP_NEXT(eec)) { 296 eec = pci_conf_read(sc->sc_pc, sc->sc_tag, eecp); 297 if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP) 298 continue; 299 legsup = eec; 300 pci_conf_write(sc->sc_pc, sc->sc_tag, eecp, 301 legsup & ~EHCI_LEGSUP_OSOWNED); 302 } 303 } 304 #endif 305 306 void 307 ehci_pci_takecontroller(struct ehci_pci_softc *sc, int silent) 308 { 309 u_int32_t cparams, eec, legsup; 310 int eecp, i; 311 312 cparams = EREAD4(&sc->sc, EHCI_HCCPARAMS); 313 /* Synchronise with the BIOS if it owns the controller. */ 314 for (eecp = EHCI_HCC_EECP(cparams); eecp != 0; 315 eecp = EHCI_EECP_NEXT(eec)) { 316 eec = pci_conf_read(sc->sc_pc, sc->sc_tag, eecp); 317 if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP) 318 continue; 319 legsup = eec; 320 if (legsup & EHCI_LEGSUP_BIOSOWNED) { 321 pci_conf_write(sc->sc_pc, sc->sc_tag, eecp, 322 legsup | EHCI_LEGSUP_OSOWNED); 323 DPRINTF(("%s: waiting for BIOS to give up control\n", 324 sc->sc.sc_bus.bdev.dv_xname)); 325 for (i = 0; i < 5000; i++) { 326 legsup = pci_conf_read(sc->sc_pc, sc->sc_tag, 327 eecp); 328 if ((legsup & EHCI_LEGSUP_BIOSOWNED) == 0) 329 break; 330 DELAY(1000); 331 } 332 if (silent == 0 && (legsup & EHCI_LEGSUP_BIOSOWNED)) 333 printf("%s: timed out waiting for BIOS\n", 334 sc->sc.sc_bus.bdev.dv_xname); 335 } 336 } 337 } 338 339 int 340 ehci_sb700_match(struct pci_attach_args *pa) 341 { 342 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ATI && 343 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_SBX00_SMB && 344 (PCI_REVISION(pa->pa_class) == 0x3a || 345 PCI_REVISION(pa->pa_class) == 0x3b)) 346 return (1); 347 348 return (0); 349 } 350