/* * This file is part of the libpayload project. * * Copyright (C) 2008-2010 coresystems GmbH * * 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ //#define USB_DEBUG #include <libpayload-config.h> #include <usb/usb.h> #include "uhci.h" #include "ohci.h" #include "ehci.h" #include "xhci.h" #include "dwc2.h" #include <usb/usbdisk.h> #if IS_ENABLED(CONFIG_LP_USB_PCI) /** * Initializes USB controller attached to PCI * * @param bus PCI bus number * @param dev PCI device id at bus * @param func function id of the controller */ static int usb_controller_initialize(int bus, int dev, int func) { u32 class; u32 devclass; u32 prog_if; pcidev_t pci_device; u32 pciid; pci_device = PCI_DEV (bus, dev, func); class = pci_read_config32(pci_device, 8); pciid = pci_read_config32(pci_device, 0); devclass = class >> 16; prog_if = (class >> 8) & 0xff; /* enable busmaster */ if (devclass == 0xc03) { u32 pci_command; pci_command = pci_read_config32(pci_device, PCI_COMMAND); pci_command |= PCI_COMMAND_MASTER; pci_write_config32(pci_device, PCI_COMMAND, pci_command); usb_debug("%02x:%02x.%x %04x:%04x.%d ", bus, dev, func, pciid >> 16, pciid & 0xFFFF, func); switch (prog_if) { case 0x00: #if IS_ENABLED(CONFIG_LP_USB_UHCI) usb_debug("UHCI controller\n"); uhci_pci_init (pci_device); #else usb_debug("UHCI controller (not supported)\n"); #endif break; case 0x10: #if IS_ENABLED(CONFIG_LP_USB_OHCI) usb_debug("OHCI controller\n"); ohci_pci_init(pci_device); #else usb_debug("OHCI controller (not supported)\n"); #endif break; case 0x20: #if IS_ENABLED(CONFIG_LP_USB_EHCI) usb_debug("EHCI controller\n"); ehci_pci_init(pci_device); #else usb_debug("EHCI controller (not supported)\n"); #endif break; case 0x30: #if IS_ENABLED(CONFIG_LP_USB_XHCI) usb_debug("xHCI controller\n"); xhci_pci_init(pci_device); #else usb_debug("xHCI controller (not supported)\n"); #endif break; default: usb_debug("unknown controller %x not supported\n", prog_if); break; } } return 0; } static void usb_scan_pci_bus(int bus) { int dev, func; for (dev = 0; dev < 32; dev++) { u8 header_type; pcidev_t pci_device = PCI_DEV(bus, dev, 0); /* Check if there's a device here at all. */ if (pci_read_config32(pci_device, REG_VENDOR_ID) == 0xffffffff) continue; /* * EHCI is defined by standards to be at a higher function * than the USB1 controllers. We don't want to init USB1 + * devices just to "steal" those for USB2, so make sure USB2 * comes first by scanning multifunction devices from 7 to 0. */ /* Check for a multifunction device. */ header_type = pci_read_config8(pci_device, REG_HEADER_TYPE); if (header_type & HEADER_TYPE_MULTIFUNCTION) func = 7; else func = 0; for (; func >= 0; func--) { pci_device = PCI_DEV(bus, dev, func); header_type = pci_read_config8(pci_device, REG_HEADER_TYPE); /* If this is a bridge, scan the other side. */ if ((header_type & ~HEADER_TYPE_MULTIFUNCTION) == HEADER_TYPE_BRIDGE) { /* Verify that the bridge is enabled */ if ((pci_read_config16(pci_device, REG_COMMAND) & 3) != 0) usb_scan_pci_bus(pci_read_config8( pci_device, REG_SECONDARY_BUS)); } else usb_controller_initialize(bus, dev, func); } } } #endif /** * Initialize all USB controllers attached to PCI. */ int usb_initialize(void) { #if IS_ENABLED(CONFIG_LP_USB_PCI) usb_scan_pci_bus(0); #endif return 0; } hci_t *usb_add_mmio_hc(hc_type type, void *bar) { switch (type) { #if IS_ENABLED(CONFIG_LP_USB_OHCI) case OHCI: return ohci_init((unsigned long)bar); #endif #if IS_ENABLED(CONFIG_LP_USB_EHCI) case EHCI: return ehci_init((unsigned long)bar); #endif #if IS_ENABLED(CONFIG_LP_USB_DWC2) case DWC2: return dwc2_init(bar); #endif #if IS_ENABLED(CONFIG_LP_USB_XHCI) case XHCI: return xhci_init((unsigned long)bar); #endif default: usb_debug("HC type %d (at %p) is not supported!\n", type, bar); return NULL; } }