blob: bb46558146834f11fd8e057f5c3f1ec376e8a396 [file] [log] [blame]
/*
* 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 <usb/usb.h>
#include "drivers/bus/pci/pci.h"
#include "libpayload/drivers/usb/dwc2.h"
#include "libpayload/drivers/usb/ehci.h"
#include "libpayload/drivers/usb/ohci.h"
#include "libpayload/drivers/usb/uhci.h"
#include "libpayload/drivers/usb/xhci.h"
/* Initializes USB controller attached to PCI. */
static int usb_controller_initialize(pcidev_t dev)
{
uint32_t class = pci_read_config32(dev, 8);
uint32_t pciid = pci_read_config32(dev, 0);
uint32_t devclass = class >> 16;
uint32_t prog_if = (class >> 8) & 0xff;
// Enable busmaster.
if (devclass == 0xc03) {
uint32_t command;
command = pci_read_config32(dev, PciConfCommand);
command |= PciConfCommandBm;
pci_write_config32(dev, PciConfCommand, command);
usb_debug("%02x:%02x.%x %04x:%04x.%d ", PCI_BUS(dev),
PCI_SLOT(dev), PCI_FUNC(dev),
pciid >> 16, pciid & 0xFFFF, PCI_FUNC(dev));
switch (prog_if) {
case 0x00:
if (CONFIG_USB_UHCI) {
usb_debug("UHCI controller\n");
uhci_pci_init(dev);
} else {
usb_debug("UHCI controller (not supported)\n");
}
break;
case 0x10:
if (CONFIG_USB_OHCI) {
usb_debug("OHCI controller\n");
ohci_pci_init(dev);
} else {
usb_debug("OHCI controller (not supported)\n");
}
break;
case 0x20:
if (CONFIG_USB_EHCI) {
usb_debug("EHCI controller\n");
ehci_pci_init(dev);
} else {
usb_debug("EHCI controller (not supported)\n");
}
break;
case 0x30:
if (CONFIG_USB_XHCI) {
usb_debug("xHCI controller\n");
xhci_pci_init(dev);
} else {
usb_debug("xHCI controller (not supported)\n");
}
break;
default:
usb_debug("unknown controller %x not supported\n",
prog_if);
break;
}
}
return 0;
}
static void usb_scan_pci_bus_dev(pcidev_t dev);
static void usb_scan_pci_bus(int bus)
{
for (int dev = 0; dev < 32; dev++) {
pcidev_t pci_dev = PCI_DEV(bus, dev, 0);
/* Check if there's a device here at all. */
if (pci_read_config32(pci_dev, PciConfVendorId) == 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. */
uint8_t type = pci_read_config8(pci_dev, PciConfHeaderType);
int multifunction =
(type & PciConfHeaderTypeMultifunction) != 0;
for (int func = multifunction ? 7 : 0; func >= 0; func--)
usb_scan_pci_bus_dev(PCI_DEV(bus, dev, func));
}
}
static void usb_scan_pci_bus_dev(pcidev_t dev)
{
uint8_t hdr_type = pci_read_config8(dev, PciConfHeaderType);
uint8_t type = hdr_type & ~PciConfHeaderTypeMultifunction;
// If this is a bridge, scan the other side.
if (type == PciConfHeaderTypeBridge) {
// Verify that the bridge is enabled.
uint16_t command = pci_read_config16(dev, PciConfCommand);
if ((command & 3) != 0) {
uint8_t secondary =
pci_read_config8(dev, PciConfSecondaryBus);
usb_scan_pci_bus(secondary);
}
} else {
usb_controller_initialize(dev);
}
}
/* Initialize all USB controllers attached to PCI. */
int usb_initialize(void)
{
if (CONFIG_USB_PCI)
usb_scan_pci_bus(0);
return 0;
}
UsbDevHc *usb_add_mmio_hc(UsbHcType type, void *bar)
{
if (CONFIG_USB_OHCI && type == UsbOhci)
return ohci_init((unsigned long)bar);
if (CONFIG_USB_EHCI && type == UsbEhci)
return ehci_init((unsigned long)bar);
if (CONFIG_USB_DWC2 && type == UsbDwc2)
return dwc2_init(bar);
if (CONFIG_USB_XHCI && type == UsbXhci)
return xhci_init((unsigned long)bar);
usb_debug("HC type %d (at %p) is not supported!\n", type, bar);
return NULL;
}