diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2015-11-10 08:50:52 -0500 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2015-11-19 08:48:34 -0500 |
commit | aa34e4e52fb65abb4ef8539660f05b4d52fb1f6f (patch) | |
tree | eefe809c4508fd198792d6561e7843ecc2ba5763 | |
parent | 45b594ab1d0d05903482e818e7c6b2c81b850e54 (diff) | |
download | seabios-aa34e4e52fb65abb4ef8539660f05b4d52fb1f6f.zip seabios-aa34e4e52fb65abb4ef8539660f05b4d52fb1f6f.tar.gz seabios-aa34e4e52fb65abb4ef8539660f05b4d52fb1f6f.tar.bz2 |
xhci: Check for device disconnects during USB2 reset polling
Some XHCI controllers register super-speed devices on high-speed ports
and then disconnect them when the super-speed detection completes.
Make sure to recognize these disconnect events during the reset
process.
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
-rw-r--r-- | src/hw/usb-xhci.c | 24 |
1 files changed, 19 insertions, 5 deletions
diff --git a/src/hw/usb-xhci.c b/src/hw/usb-xhci.c index 173dff1..945b462 100644 --- a/src/hw/usb-xhci.c +++ b/src/hw/usb-xhci.c @@ -351,6 +351,9 @@ xhci_hub_reset(struct usbhub_s *hub, u32 port) struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb); u32 portsc = readl(&xhci->pr[port].portsc); int rc; + if (!(portsc & XHCI_PORTSC_CCS)) + // Device no longer connected?! + return -1; switch (xhci_get_field(portsc, XHCI_PORTSC_PLS)) { case PLS_U0: @@ -360,11 +363,22 @@ xhci_hub_reset(struct usbhub_s *hub, u32 port) case PLS_POLLING: // A USB2 port - perform device reset and wait for completion xhci_print_port_state(3, __func__, port, portsc); - portsc |= XHCI_PORTSC_PR; - writel(&xhci->pr[port].portsc, portsc); - if (wait_bit(&xhci->pr[port].portsc, XHCI_PORTSC_PED, XHCI_PORTSC_PED, 100) != 0) - return -1; - portsc = readl(&xhci->pr[port].portsc); + writel(&xhci->pr[port].portsc, portsc | XHCI_PORTSC_PR); + u32 end = timer_calc(100); + for (;;) { + portsc = readl(&xhci->pr[port].portsc); + if (!(portsc & XHCI_PORTSC_CCS)) + // Device disconnected during reset + return -1; + if (portsc & XHCI_PORTSC_PED) + // Reset complete + break; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)]; break; default: |