[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