]> xenbits.xensource.com Git - seabios.git/commitdiff
xhci: Check for device disconnects during USB2 reset polling
authorKevin O'Connor <kevin@koconnor.net>
Tue, 10 Nov 2015 13:50:52 +0000 (08:50 -0500)
committerGerd Hoffmann <kraxel@redhat.com>
Fri, 15 Jan 2016 09:46:34 +0000 (10:46 +0100)
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>
(cherry picked from commit aa34e4e52fb65abb4ef8539660f05b4d52fb1f6f)

src/hw/usb-xhci.c

index 173dff1e56b8268460bf2274d617ef8efa63d1fe..945b4626b491c4e0c7e828a6ef59db4e9952c20d 100644 (file)
@@ -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: