From b3de9664c7b7d4878d3dcd5a3c62a8e0155d5e89 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 8 May 2015 15:33:18 +0100 Subject: [ehci] Poll child companion controllers after disowning port Signed-off-by: Michael Brown --- src/drivers/usb/ehci.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/drivers/usb/ehci.h | 6 ++++++ 2 files changed, 59 insertions(+) diff --git a/src/drivers/usb/ehci.c b/src/drivers/usb/ehci.c index d64024da..e847c6b7 100644 --- a/src/drivers/usb/ehci.c +++ b/src/drivers/usb/ehci.c @@ -292,6 +292,51 @@ static void ehci_legacy_release ( struct ehci_device *ehci, DBGC ( ehci, "EHCI %p released ownership to BIOS\n", ehci ); } +/****************************************************************************** + * + * Companion controllers + * + ****************************************************************************** + */ + +/** + * Poll child companion controllers + * + * @v ehci EHCI device + */ +static void ehci_poll_companions ( struct ehci_device *ehci ) { + struct usb_bus *bus; + struct device_description *desc; + + /* Poll any USB buses belonging to child companion controllers */ + for_each_usb_bus ( bus ) { + + /* Get underlying devices description */ + desc = &bus->dev->desc; + + /* Skip buses that are not PCI devices */ + if ( desc->bus_type != BUS_TYPE_PCI ) + continue; + + /* Skip buses that are not part of the same PCI device */ + if ( PCI_FIRST_FUNC ( desc->location ) != + PCI_FIRST_FUNC ( ehci->bus->dev->desc.location ) ) + continue; + + /* Skip buses that are not UHCI or OHCI PCI devices */ + if ( ( desc->class != PCI_CLASS ( PCI_CLASS_SERIAL, + PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_UHCI ))&& + ( desc->class != PCI_CLASS ( PCI_CLASS_SERIAL, + PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_OHCI ) )) + continue; + + /* Poll child companion controller bus */ + usb_poll ( bus ); + } +} + /****************************************************************************** * * Run / stop / reset @@ -1460,9 +1505,17 @@ static int ehci_root_enable ( struct usb_hub *hub, struct usb_port *port ) { return -ETIMEDOUT; disown: + /* Disown port */ portsc &= ~EHCI_PORTSC_CHANGE; portsc |= EHCI_PORTSC_OWNER; writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); + + /* Delay to allow child companion controllers to settle */ + mdelay ( EHCI_DISOWN_DELAY_MS ); + + /* Poll child companion controllers */ + ehci_poll_companions ( ehci ); + return -ENODEV; } diff --git a/src/drivers/usb/ehci.h b/src/drivers/usb/ehci.h index 7ad1e649..d8814ec7 100644 --- a/src/drivers/usb/ehci.h +++ b/src/drivers/usb/ehci.h @@ -424,6 +424,12 @@ ehci_ring_remaining ( struct ehci_ring *ring ) { */ #define EHCI_PORT_POWER_DELAY_MS 20 +/** Time to delay after releasing ownership of a port + * + * This is a policy decision. + */ +#define EHCI_DISOWN_DELAY_MS 100 + /** Maximum time to wait for BIOS to release ownership * * This is a policy decision. -- cgit