[SeaBIOS] [PATCH] Improve USB reset timing

Kevin O'Connor kevin at koconnor.net
Mon Feb 15 04:09:10 CET 2010


This is a series of three patches (attached) that cleanup some USB
timings.  I've tested this on qemu (both uhci and a modified qemu with
ohci), and on the uhci controller in my epia-cn.

I don't have a real ohci controller - help with testing would be
appreciated.

-Kevin
-------------- next part --------------
commit 8bbc79c435c896ad0107725fa77c7aeb860af625
Author: Kevin O'Connor <kevin at koconnor.net>
Date:   Sun Feb 14 12:16:32 2010 -0500

    Add symbolic definitions for USB delays.

diff --git a/src/usb-ohci.c b/src/usb-ohci.c
index 2d33fa9..8ebd340 100644
--- a/src/usb-ohci.c
+++ b/src/usb-ohci.c
@@ -25,7 +25,7 @@ start_ohci(struct usb_s *cntl, struct ohci_hcca *hcca)
     // Do reset
     writel(&cntl->ohci.regs->control, OHCI_USB_RESET | oldrwc);
     readl(&cntl->ohci.regs->control); // flush writes
-    msleep(50);
+    msleep(USB_TIME_DRSTR);
 
     // Do software init (min 10us, max 2ms)
     u64 end = calc_future_tsc_usec(10);
@@ -96,7 +96,8 @@ check_ohci_ports(struct usb_s *cntl)
         // No devices connected
         goto shutdown;
 
-    msleep(60);    // XXX - should poll instead of using timer.
+    // XXX - should poll instead of using timer.
+    msleep(USB_TIME_DRSTR + USB_TIME_RSTRCY);
 
     totalcount = 0;
     for (i=0; i<ports; i++) {
diff --git a/src/usb-uhci.c b/src/usb-uhci.c
index 950ec6a..b09b17a 100644
--- a/src/usb-uhci.c
+++ b/src/usb-uhci.c
@@ -106,10 +106,10 @@ check_ports(struct usb_s *cntl)
         outw(USBPORTSC_PR, cntl->uhci.iobase + USBPORTSC1);
     if (port2 & USBPORTSC_CCS)
         outw(USBPORTSC_PR, cntl->uhci.iobase + USBPORTSC2);
-    msleep(50);
+    msleep(USB_TIME_DRSTR);
     outw(0, cntl->uhci.iobase + USBPORTSC1);
     outw(0, cntl->uhci.iobase + USBPORTSC2);
-    msleep(10);
+    msleep(USB_TIME_RSTRCY);
 
     // Configure ports
     int totalcount = 0;
diff --git a/src/usb.c b/src/usb.c
index 3d59faf..7d4520e 100644
--- a/src/usb.c
+++ b/src/usb.c
@@ -125,7 +125,7 @@ set_address(u32 endp)
     int ret = send_default_control(endp, &req, NULL);
     if (ret)
         return 0;
-    msleep(2);
+    msleep(USB_TIME_SETADDR_RECOVERY);
 
     cntl->maxaddr++;
     return mkendp(cntl, cntl->maxaddr, 0, endp2speed(endp), endp2maxsize(endp));
diff --git a/src/usb.h b/src/usb.h
index 8b9c5f1..e4d0a66 100644
--- a/src/usb.h
+++ b/src/usb.h
@@ -75,6 +75,15 @@ static inline u8 endp2maxsize(u32 endp) {
  * usb structs and flags
  ****************************************************************/
 
+// USB mandated timings (in ms)
+#define USB_TIME_SIGATT 100
+#define USB_TIME_ATTDB  100
+#define USB_TIME_DRST   10
+#define USB_TIME_DRSTR  50
+#define USB_TIME_RSTRCY 10
+
+#define USB_TIME_SETADDR_RECOVERY 2
+
 #define USB_PID_OUT                     0xe1
 #define USB_PID_IN                      0x69
 #define USB_PID_SETUP                   0x2d
-------------- next part --------------
commit 49a0aa61a581991526926a875c30870bfd7ec6e9
Author: Kevin O'Connor <kevin at koconnor.net>
Date:   Sun Feb 14 18:59:48 2010 -0500

    Don't parallelize USB OHCI root port reset.
    
    If multiple ports are reset simultaneously, they could both respond to
    the default address.  So, only reset one at a time.

diff --git a/src/usb-ohci.c b/src/usb-ohci.c
index 421c19b..4ce75a6 100644
--- a/src/usb-ohci.c
+++ b/src/usb-ohci.c
@@ -82,38 +82,49 @@ check_ohci_ports(struct usb_s *cntl)
     writel(&cntl->ohci.regs->roothub_status, RH_HS_LPSC);
     writel(&cntl->ohci.regs->roothub_b, RH_B_PPCM);
     msleep((rha >> 24) * 2);
+    // XXX - need to sleep for USB_TIME_SIGATT if just powered up?
 
     // Count and reset connected devices
     int ports = rha & RH_A_NDP;
     int totalcount = 0;
     int i;
-    for (i=0; i<ports; i++)
-        if (readl(&cntl->ohci.regs->roothub_portstatus[i]) & RH_PS_CCS) {
-            writel(&cntl->ohci.regs->roothub_portstatus[i], RH_PS_PRS);
-            totalcount++;
-        }
-    if (!totalcount)
-        // No devices connected
-        goto shutdown;
-
-    // XXX - should poll instead of using timer.
-    msleep(USB_TIME_DRSTR + USB_TIME_RSTRCY);
-
-    totalcount = 0;
     for (i=0; i<ports; i++) {
         u32 sts = readl(&cntl->ohci.regs->roothub_portstatus[i]);
-        if ((sts & (RH_PS_CCS|RH_PS_PES)) == (RH_PS_CCS|RH_PS_PES)) {
-            int count = configure_usb_device(cntl, !!(sts & RH_PS_LSDA));
-            if (! count)
-                // Shutdown port
-                writel(&cntl->ohci.regs->roothub_portstatus[i]
-                       , RH_PS_CCS|RH_PS_LSDA);
-            totalcount += count;
+        if (!(sts & RH_PS_CCS))
+            continue;
+        // XXX - need to wait for USB_TIME_ATTDB if just powered up?
+        writel(&cntl->ohci.regs->roothub_portstatus[i], RH_PS_PRS);
+        u64 end = calc_future_tsc(USB_TIME_DRSTR * 2);
+        for (;;) {
+            sts = readl(&cntl->ohci.regs->roothub_portstatus[i]);
+            if (!(sts & RH_PS_PRS))
+                // XXX - need to ensure USB_TIME_DRSTR time in reset?
+                break;
+            if (check_time(end)) {
+                // Timeout.
+                warn_timeout();
+                goto shutdown;
+            }
+            yield();
         }
+
+        if ((sts & (RH_PS_CCS|RH_PS_PES)) != (RH_PS_CCS|RH_PS_PES))
+            // Device no longer present
+            continue;
+
+        msleep(USB_TIME_RSTRCY);
+
+        // XXX - should try to parallelize configuration.
+        int count = configure_usb_device(cntl, !!(sts & RH_PS_LSDA));
+        if (! count)
+            // Shutdown port
+            writel(&cntl->ohci.regs->roothub_portstatus[i]
+                   , RH_PS_CCS|RH_PS_LSDA);
+        totalcount += count;
     }
     if (!totalcount)
+        // No devices connected
         goto shutdown;
-
     return totalcount;
 
 shutdown:
-------------- next part --------------
commit ba94a68d273fa82015fb56d4c9410047cf59c21b
Author: Kevin O'Connor <kevin at koconnor.net>
Date:   Sun Feb 14 19:05:35 2010 -0500

    Don't leave USB UHCI ports disabled for extended time during reset.
    
    Disabling the port will cause device to go into suspend - so don't do
    that during the reset sequence.

diff --git a/src/usb-uhci.c b/src/usb-uhci.c
index b7ff394..c3ff744 100644
--- a/src/usb-uhci.c
+++ b/src/usb-uhci.c
@@ -94,6 +94,7 @@ start_uhci(struct usb_s *cntl)
 static int
 check_ports(struct usb_s *cntl)
 {
+    // XXX - if just powered up, need to wait for USB_TIME_SIGATT?
     u16 port1 = inw(cntl->uhci.iobase + USBPORTSC1);
     u16 port2 = inw(cntl->uhci.iobase + USBPORTSC2);
 
@@ -101,29 +102,34 @@ check_ports(struct usb_s *cntl)
         // No devices
         return 0;
 
+    // XXX - if just powered up, need to wait for USB_TIME_ATTDB?
+
     // reset ports
     if (port1 & USBPORTSC_CCS)
         outw(USBPORTSC_PR, cntl->uhci.iobase + USBPORTSC1);
     if (port2 & USBPORTSC_CCS)
         outw(USBPORTSC_PR, cntl->uhci.iobase + USBPORTSC2);
     msleep(USB_TIME_DRSTR);
-    outw(0, cntl->uhci.iobase + USBPORTSC1);
-    outw(0, cntl->uhci.iobase + USBPORTSC2);
-    msleep(USB_TIME_RSTRCY);
 
     // Configure ports
     int totalcount = 0;
+    outw(0, cntl->uhci.iobase + USBPORTSC1);
+    udelay(6); // 64 high-speed bit times
     port1 = inw(cntl->uhci.iobase + USBPORTSC1);
     if (port1 & USBPORTSC_CCS) {
         outw(USBPORTSC_PE, cntl->uhci.iobase + USBPORTSC1);
+        msleep(USB_TIME_RSTRCY);
         int count = configure_usb_device(cntl, !!(port1 & USBPORTSC_LSDA));
         if (! count)
             outw(0, cntl->uhci.iobase + USBPORTSC1);
         totalcount += count;
     }
+    outw(0, cntl->uhci.iobase + USBPORTSC2);
+    udelay(6);
     port2 = inw(cntl->uhci.iobase + USBPORTSC2);
     if (port2 & USBPORTSC_CCS) {
         outw(USBPORTSC_PE, cntl->uhci.iobase + USBPORTSC2);
+        msleep(USB_TIME_RSTRCY);
         int count = configure_usb_device(cntl, !!(port2 & USBPORTSC_LSDA));
         if (! count)
             outw(0, cntl->uhci.iobase + USBPORTSC2);


More information about the SeaBIOS mailing list