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);
+}