aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2015-11-10 08:50:52 -0500
committerKevin O'Connor <kevin@koconnor.net>2015-11-19 08:48:34 -0500
commitaa34e4e52fb65abb4ef8539660f05b4d52fb1f6f (patch)
treeeefe809c4508fd198792d6561e7843ecc2ba5763
parent45b594ab1d0d05903482e818e7c6b2c81b850e54 (diff)
downloadseabios-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.c24
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: