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 + * 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 +__KERNEL_RCSID(0, "$NetBSD: platform.c,v 1.9 2010/09/06 15:54:27 jmcneill Exp $"); + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +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); +}