1 /* $OpenBSD: ehci_pci.c,v 1.18 2009/07/24 03:18:58 deraadt 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/proc.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 ehci_softc_t 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 void ehci_pci_givecontroller(struct ehci_pci_softc *); 78 void ehci_pci_takecontroller(struct ehci_pci_softc *); 79 void ehci_pci_shutdown(void *); 80 81 struct cfattach ehci_pci_ca = { 82 sizeof(struct ehci_pci_softc), ehci_pci_match, ehci_pci_attach, 83 ehci_pci_detach, ehci_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 conventional 156 * 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, ehci_intr, sc, devname); 173 if (sc->sc_ih == NULL) { 174 printf(": couldn't establish interrupt"); 175 if (intrstr != NULL) 176 printf(" at %s", intrstr); 177 printf("\n"); 178 goto unmap_ret; 179 } 180 printf(": %s\n", intrstr); 181 182 switch(pci_conf_read(pc, tag, PCI_USBREV) & PCI_USBREV_MASK) { 183 case PCI_USBREV_PRE_1_0: 184 case PCI_USBREV_1_0: 185 case PCI_USBREV_1_1: 186 sc->sc.sc_bus.usbrev = USBREV_UNKNOWN; 187 printf("%s: pre-2.0 USB rev\n", devname); 188 goto unmap_ret; 189 case PCI_USBREV_2_0: 190 sc->sc.sc_bus.usbrev = USBREV_2_0; 191 break; 192 default: 193 sc->sc.sc_bus.usbrev = USBREV_UNKNOWN; 194 break; 195 } 196 197 /* Figure out vendor for root hub descriptor. */ 198 vendor = pci_findvendor(pa->pa_id); 199 sc->sc.sc_id_vendor = PCI_VENDOR(pa->pa_id); 200 if (vendor) 201 strlcpy(sc->sc.sc_vendor, vendor, sizeof(sc->sc.sc_vendor)); 202 else 203 snprintf(sc->sc.sc_vendor, sizeof(sc->sc.sc_vendor), 204 "vendor 0x%04x", PCI_VENDOR(pa->pa_id)); 205 206 /* Enable workaround for dropped interrupts as required */ 207 if (sc->sc.sc_id_vendor == PCI_VENDOR_VIATECH) 208 sc->sc.sc_flags |= EHCIF_DROPPED_INTR_WORKAROUND; 209 210 ehci_pci_takecontroller(sc); 211 r = ehci_init(&sc->sc); 212 if (r != USBD_NORMAL_COMPLETION) { 213 printf("%s: init failed, error=%d\n", devname, r); 214 goto unmap_ret; 215 } 216 217 sc->sc.sc_shutdownhook = shutdownhook_establish(ehci_pci_shutdown, sc); 218 splx(s); 219 220 /* Attach usb device. */ 221 sc->sc.sc_child = config_found((void *)sc, &sc->sc.sc_bus, 222 usbctlprint); 223 224 return; 225 226 unmap_ret: 227 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 228 splx(s); 229 } 230 231 int 232 ehci_pci_detach(struct device *self, int flags) 233 { 234 struct ehci_pci_softc *sc = (struct ehci_pci_softc *)self; 235 int rv; 236 237 rv = ehci_detach(&sc->sc, flags); 238 if (rv) 239 return (rv); 240 if (sc->sc_ih != NULL) { 241 pci_intr_disestablish(sc->sc_pc, sc->sc_ih); 242 sc->sc_ih = NULL; 243 } 244 if (sc->sc.sc_size) { 245 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 246 sc->sc.sc_size = 0; 247 } 248 return (0); 249 } 250 251 #if 0 /* not used */ 252 void 253 ehci_pci_givecontroller(struct ehci_pci_softc *sc) 254 { 255 u_int32_t cparams, eec, legsup; 256 int eecp; 257 258 cparams = EREAD4(&sc->sc, EHCI_HCCPARAMS); 259 for (eecp = EHCI_HCC_EECP(cparams); eecp != 0; 260 eecp = EHCI_EECP_NEXT(eec)) { 261 eec = pci_conf_read(sc->sc_pc, sc->sc_tag, eecp); 262 if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP) 263 continue; 264 legsup = eec; 265 pci_conf_write(sc->sc_pc, sc->sc_tag, eecp, 266 legsup & ~EHCI_LEGSUP_OSOWNED); 267 } 268 } 269 #endif 270 271 void 272 ehci_pci_takecontroller(struct ehci_pci_softc *sc) 273 { 274 u_int32_t cparams, eec, legsup; 275 int eecp, i; 276 277 cparams = EREAD4(&sc->sc, EHCI_HCCPARAMS); 278 /* Synchronise with the BIOS if it owns the controller. */ 279 for (eecp = EHCI_HCC_EECP(cparams); eecp != 0; 280 eecp = EHCI_EECP_NEXT(eec)) { 281 eec = pci_conf_read(sc->sc_pc, sc->sc_tag, eecp); 282 if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP) 283 continue; 284 legsup = eec; 285 if (legsup & EHCI_LEGSUP_BIOSOWNED) { 286 pci_conf_write(sc->sc_pc, sc->sc_tag, eecp, 287 legsup | EHCI_LEGSUP_OSOWNED); 288 DPRINTF(("%s: waiting for BIOS to give up control\n", 289 sc->sc.sc_bus.bdev.dv_xname)); 290 for (i = 0; i < 5000; i++) { 291 legsup = pci_conf_read(sc->sc_pc, sc->sc_tag, 292 eecp); 293 if ((legsup & EHCI_LEGSUP_BIOSOWNED) == 0) 294 break; 295 DELAY(1000); 296 } 297 if (legsup & EHCI_LEGSUP_BIOSOWNED) 298 printf("%s: timed out waiting for BIOS\n", 299 sc->sc.sc_bus.bdev.dv_xname); 300 } 301 } 302 } 303 304 void 305 ehci_pci_shutdown(void *v) 306 { 307 struct ehci_pci_softc *sc = (struct ehci_pci_softc *)v; 308 309 ehci_shutdown(&sc->sc); 310 #if 0 311 /* best not to do this anymore; BIOS SMM spins? */ 312 ehci_pci_givecontroller(sc); 313 #endif 314 } 315 316 int 317 ehci_sb700_match(struct pci_attach_args *pa) 318 { 319 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ATI && 320 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_SBX00_SMB && 321 (PCI_REVISION(pa->pa_class) == 0x3a || 322 PCI_REVISION(pa->pa_class) == 0x3b)) 323 return (1); 324 325 return (0); 326 } 327