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>
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 @@
     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 @@
     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: