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