Index: sys/arch/x86/include/pci_machdep_common.h
===================================================================
RCS file: /cvsroot/src/sys/arch/x86/include/pci_machdep_common.h,v
retrieving revision 1.3
diff -u -r1.3 pci_machdep_common.h
--- sys/arch/x86/include/pci_machdep_common.h   28 Apr 2010 21:27:14 -0000      1.3
+++ sys/arch/x86/include/pci_machdep_common.h   27 Oct 2010 14:56:51 -0000
@@ -135,4 +135,6 @@
void pci_mmio_range_infer(pci_chipset_tag_t, int, int, bus_addr_t *,
    bus_size_t *);

+void pci_usb_fixup(pci_chipset_tag_t);
+
#endif /* _X86_PCI_MACHDEP_COMMON_H_ */
Index: sys/arch/i386/i386/machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/i386/machdep.c,v
retrieving revision 1.696
diff -u -r1.696 machdep.c
--- sys/arch/i386/i386/machdep.c        24 Oct 2010 07:53:04 -0000      1.696
+++ sys/arch/i386/i386/machdep.c        27 Oct 2010 14:56:51 -0000
@@ -1414,6 +1414,10 @@
        */
       initgdt(NULL);
#endif /* XEN */
+
+#if NPCI > 0
+       pci_usb_fixup(NULL);
+#endif
       consinit();     /* XXX SHOULD NOT BE DONE HERE */

#ifdef DEBUG_MEMLOAD
Index: sys/arch/amd64/amd64/machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/amd64/machdep.c,v
retrieving revision 1.154
diff -u -r1.154 machdep.c
--- sys/arch/amd64/amd64/machdep.c      24 Oct 2010 07:53:05 -0000      1.154
+++ sys/arch/amd64/amd64/machdep.c      27 Oct 2010 14:56:51 -0000
@@ -1280,6 +1280,9 @@
       x86_bus_space_init();
#endif

+#if NPCI > 0
+       pci_usb_fixup(NULL);
+#endif
       consinit();     /* XXX SHOULD NOT BE DONE HERE */

       /*
Index: sys/arch/x86/pci/pci_usb.c
===================================================================
RCS file: sys/arch/x86/pci/pci_usb.c
diff -N sys/arch/x86/pci/pci_usb.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ sys/arch/x86/pci/pci_usb.c  27 Oct 2010 14:56:51 -0000
@@ -0,0 +1,242 @@
+/* $NetBSD$ */
+
+/*-
+ * Copyright (c) 2010 Jared D. McNeill <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: platform.c,v 1.9 2010/09/06 15:54:27 jmcneill Exp $");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/ehcireg.h>
+#include <dev/usb/uhcireg.h>
+#include <dev/usb/ohcireg.h>
+
+static void
+pci_usb_attach_args(pci_chipset_tag_t pc, pcitag_t tag,
+    struct pci_attach_args *pa)
+{
+       int bus, device, function;
+       pcireg_t csr;
+
+       pci_decompose_tag(pc, tag, &bus, &device, &function);
+       csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
+
+       pa->pa_iot = x86_bus_space_io;
+       pa->pa_memt = x86_bus_space_mem;
+       pa->pa_dmat = &pci_bus_dma_tag;
+#ifdef _LP64
+       pa->pa_dmat64 = &pci_bus_dma64_tag;
+#else
+       pa->pa_dmat64 = NULL;
+#endif
+       pa->pa_pc = pc;
+       pa->pa_bus = bus;
+       pa->pa_device = device;
+       pa->pa_function = function;
+       pa->pa_tag = tag;
+       pa->pa_id = pci_conf_read(pc, tag, PCI_ID_REG);
+       pa->pa_class = pci_conf_read(pc, tag, PCI_CLASS_REG);
+       pa->pa_flags = 0;
+       if (csr & PCI_COMMAND_IO_ENABLE)
+               pa->pa_flags |= PCI_FLAGS_IO_ENABLED;
+       if (csr & PCI_COMMAND_MEM_ENABLE)
+               pa->pa_flags |= PCI_FLAGS_MEM_ENABLED;
+}
+
+static void
+pci_usb_fixup_ehci(pci_chipset_tag_t pc, pcitag_t tag)
+{
+       struct pci_attach_args pa;
+       bus_space_tag_t iot;
+       bus_space_handle_t ioh;
+       bus_size_t size;
+       uint32_t cparams, addr, cap;
+       pcireg_t legsup;
+       int maxcap = 10;
+       int ms;
+
+       pci_usb_attach_args(pc, tag, &pa);
+
+       if (pci_mapreg_map(&pa, PCI_CBMEM, PCI_MAPREG_TYPE_MEM, 0,
+           &iot, &ioh, NULL, &size))
+               return;
+
+       cparams = bus_space_read_4(iot, ioh, EHCI_HCCPARAMS);
+       addr = EHCI_HCC_EECP(cparams);
+       while (addr != 0) {
+               cap = pci_conf_read(pc, tag, addr);
+               if (EHCI_CAP_GET_ID(cap) != EHCI_CAP_ID_LEGACY)
+                       goto next;
+               legsup = pci_conf_read(pc, tag, addr + PCI_EHCI_USBLEGSUP);
+               /* Ask BIOS to give up ownership */
+               pci_conf_write(pc, tag, addr + PCI_EHCI_USBLEGSUP,
+                   legsup | EHCI_LEG_HC_OS_OWNED);
+               if (legsup & EHCI_LEG_HC_BIOS_OWNED) {
+                       for (ms = 0; ms < 1000; ms++) {
+                               legsup = pci_conf_read(pc, tag,
+                                   addr + PCI_EHCI_USBLEGSUP);
+                               if (!(legsup & EHCI_LEG_HC_BIOS_OWNED))
+                                       break;
+                               delay(1000);
+                       }
+                       if (ms == 1000)
+                               pci_conf_write(pc, tag,
+                                   addr + PCI_EHCI_USBLEGSUP, 0);
+               }
+
+               /* Disable SMI */
+               pci_conf_write(pc, tag, addr + PCI_EHCI_USBLEGCTLSTS,
+                   EHCI_LEG_EXT_SMI_BAR | EHCI_LEG_EXT_SMI_PCICMD |
+                   EHCI_LEG_EXT_SMI_OS_CHANGE);
+
+next:
+               if (--maxcap < 0)
+                       break;
+               addr = EHCI_CAP_GET_NEXT(cap);
+       }
+
+       bus_space_unmap(iot, ioh, size);
+}
+
+static void
+pci_usb_fixup_uhci(pci_chipset_tag_t pc, pcitag_t tag)
+{
+       struct pci_attach_args pa;
+       bus_space_tag_t iot;
+       bus_space_handle_t ioh;
+       bus_size_t size;
+
+       pci_usb_attach_args(pc, tag, &pa);
+
+       if (pci_mapreg_map(&pa, PCI_CBMEM, PCI_MAPREG_TYPE_MEM, 0,
+           &iot, &ioh, NULL, &size))
+               return;
+
+       pci_conf_write(pc, tag, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN);
+       bus_space_write_2(iot, ioh, UHCI_INTR, 0);
+       bus_space_write_2(iot, ioh, UHCI_STS,
+           bus_space_read_2(iot, ioh, UHCI_STS));
+
+       bus_space_unmap(iot, ioh, size);
+}
+
+static void
+pci_usb_fixup_ohci(pci_chipset_tag_t pc, pcitag_t tag)
+{
+       struct pci_attach_args pa;
+       bus_space_tag_t iot;
+       bus_space_handle_t ioh;
+       bus_size_t size;
+       uint32_t ctl, rwc, s;
+       int i;
+
+       pci_usb_attach_args(pc, tag, &pa);
+
+       if (pci_mapreg_map(&pa, PCI_CBMEM, PCI_MAPREG_TYPE_MEM, 0,
+           &iot, &ioh, NULL, &size))
+               return;
+
+       ctl = bus_space_read_4(iot, ioh, OHCI_CONTROL);
+       if (ctl & OHCI_IR) {
+               rwc = ctl & OHCI_RWC;
+
+               s = bus_space_read_4(iot, ioh, OHCI_COMMAND_STATUS);
+               bus_space_write_4(iot, ioh, OHCI_COMMAND_STATUS,
+                   s | OHCI_OCR);
+               for (i = 0; i < 100 && (ctl & OHCI_IR); i++) {
+                       delay(2000);
+                       ctl = bus_space_read_4(iot, ioh, OHCI_CONTROL);
+               }
+               bus_space_write_4(iot, ioh, OHCI_INTERRUPT_DISABLE,
+                   OHCI_MIE);
+               if ((ctl & OHCI_IR) == 0) {
+                       bus_space_write_4(iot, ioh, OHCI_CONTROL,
+                           OHCI_HCFS_RESET | rwc);
+                       delay(100000);
+               }
+       }
+
+       bus_space_unmap(iot, ioh, size);
+}
+
+static void
+pci_usb_fixup_cb(pci_chipset_tag_t pc, pcitag_t tag, void *priv)
+{
+       void (*fixup)(pci_chipset_tag_t, pcitag_t) = NULL;
+       int bus, device, function;
+       pcireg_t class, id;
+       const char *type;
+
+       class = pci_conf_read(pc, tag, PCI_CLASS_REG);
+       id = pci_conf_read(pc, tag, PCI_ID_REG);
+
+       if (PCI_CLASS(class) != PCI_CLASS_SERIALBUS ||
+           PCI_SUBCLASS(class) != PCI_SUBCLASS_SERIALBUS_USB)
+               return;
+
+       switch (PCI_INTERFACE(class)) {
+       case PCI_INTERFACE_EHCI:
+               type = "EHCI";
+               fixup = pci_usb_fixup_ehci;
+               break;
+       case PCI_INTERFACE_UHCI:
+               type = "UHCI";
+               fixup = pci_usb_fixup_uhci;
+               break;
+       case PCI_INTERFACE_OHCI:
+               type = "OHCI";
+               fixup = pci_usb_fixup_ohci;
+               break;
+       }
+
+       if (fixup) {
+               pci_decompose_tag(pc, tag, &bus, &device, &function);
+               aprint_debug("PCI%03d:%02d:%d %04x:%04x %s fixup\n",
+                   bus, device, function,
+                   PCI_VENDOR(id), PCI_PRODUCT(id), type);
+               fixup(pc, tag);
+       } else {
+               aprint_debug("PCI%03d:%02d:%d %04x:%04x "
+                   "unknown USB interface 0x%x\n",
+                   bus, device, function,
+                   PCI_VENDOR(id), PCI_PRODUCT(id), PCI_INTERFACE(class));
+       }
+}
+
+void
+pci_usb_fixup(pci_chipset_tag_t pc)
+{
+       int maxbus = 0; /* XXX USB controller is likely on bus 0 */
+
+       pci_device_foreach(pc, maxbus, pci_usb_fixup_cb, NULL);
+}