Hi Nils,
I put together a patch to modify the way the OHCI controller does pipe free'ing. This is a preliminary patch (similar batch free'ing should be done for the other controllers as well). However, it might help with the USB issues you've been having.
-Kevin
commit fa43976172295d3bad9209374bd68469c10ec918 Author: Kevin O'Connor kevin@koconnor.net Date: Mon Feb 20 12:54:49 2012 -0500
Batch free USB pipes on OHCI controllers.
Instead of unregistering each control "endpoint descriptor" after it is used, keep them around for later users. Free all unused descriptors in one batch at the end of initialization. This should slightly optimize boot time, and it requires less overall interaction with the controller.
This also makes the descriptor free code more compliant with the spec. The descriptor lists will only be modified after the list processing has been disabled on the controller.
Signed-off-by: Kevin O'Connor kevin@koconnor.net
diff --git a/src/usb-ohci.c b/src/usb-ohci.c index 9107db2..7a437ad 100644 --- a/src/usb-ohci.c +++ b/src/usb-ohci.c @@ -19,6 +19,14 @@ struct usb_ohci_s { struct ohci_regs *regs; };
+struct ohci_pipe { + struct ohci_ed ed; + struct usb_pipe pipe; + void *data; + int count; + struct ohci_td *tds; +}; +
/**************************************************************** * Root hub @@ -109,6 +117,56 @@ check_ohci_ports(struct usb_ohci_s *cntl) * Setup ****************************************************************/
+// Wait for next USB frame to start - for ensuring safe memory release. +static void +ohci_waittick(struct usb_ohci_s *cntl) +{ + barrier(); + struct ohci_hcca *hcca = (void*)cntl->regs->hcca; + u32 startframe = hcca->frame_no; + u64 end = calc_future_tsc(1000 * 5); + for (;;) { + if (hcca->frame_no != startframe) + break; + if (check_tsc(end)) { + warn_timeout(); + return; + } + yield(); + } +} + +static void +ohci_free_pipes(struct usb_ohci_s *cntl) +{ + dprintf(7, "ohci_free_pipes %p\n", cntl); + + u32 creg = readl(&cntl->regs->control); + if (creg & (OHCI_CTRL_CLE|OHCI_CTRL_BLE)) { + writel(&cntl->regs->control, creg & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE)); + ohci_waittick(cntl); + } + + u32 *pos = &cntl->regs->ed_controlhead; + for (;;) { + struct ohci_ed *next = (void*)*pos; + if (!next) + break; + struct ohci_pipe *pipe = container_of(next, struct ohci_pipe, ed); + if (pipe->pipe.cntl != &cntl->usb) { + *pos = next->hwNextED; + free(pipe); + } else { + pos = &next->hwNextED; + } + } + + writel(&cntl->regs->ed_controlcurrent, 0); + writel(&cntl->regs->ed_bulkcurrent, 0); + writel(&cntl->regs->control, creg); + cntl->usb.freelist = NULL; +} + static int start_ohci(struct usb_ohci_s *cntl, struct ohci_hcca *hcca) { @@ -192,6 +250,7 @@ configure_ohci(void *data)
int count = check_ohci_ports(cntl); free_pipe(cntl->usb.defaultpipe); + ohci_free_pipes(cntl); if (! count) goto err; return; @@ -260,74 +319,13 @@ wait_ed(struct ohci_ed *ed) } }
-// Wait for next USB frame to start - for ensuring safe memory release. -static void -ohci_waittick(struct usb_ohci_s *cntl) -{ - barrier(); - struct ohci_hcca *hcca = (void*)cntl->regs->hcca; - u32 startframe = hcca->frame_no; - u64 end = calc_future_tsc(1000 * 5); - for (;;) { - if (hcca->frame_no != startframe) - break; - if (check_tsc(end)) { - warn_timeout(); - return; - } - yield(); - } -} - -static void -signal_freelist(struct usb_ohci_s *cntl) -{ - u32 v = readl(&cntl->regs->control); - if (v & OHCI_CTRL_CLE) { - writel(&cntl->regs->control, v & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE)); - ohci_waittick(cntl); - writel(&cntl->regs->ed_controlcurrent, 0); - writel(&cntl->regs->ed_bulkcurrent, 0); - writel(&cntl->regs->control, v); - } else { - ohci_waittick(cntl); - } -} - -struct ohci_pipe { - struct ohci_ed ed; - struct usb_pipe pipe; - void *data; - int count; - struct ohci_td *tds; -}; - void -ohci_free_pipe(struct usb_pipe *p) +ohci_free_pipe(struct usb_pipe *pipe) { - if (! CONFIG_USB_OHCI) - return; - dprintf(7, "ohci_free_pipe %p\n", p); - struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe); - struct usb_ohci_s *cntl = container_of( - pipe->pipe.cntl, struct usb_ohci_s, usb); - - u32 *pos = &cntl->regs->ed_controlhead; - for (;;) { - struct ohci_ed *next = (void*)*pos; - if (!next) { - // Not found?! Exit without freeing. - warn_internalerror(); - return; - } - if (next == &pipe->ed) { - *pos = next->hwNextED; - signal_freelist(cntl); - free(pipe); - return; - } - pos = &next->hwNextED; - } + // Add to controller's free list. + struct usb_s *cntl = pipe->cntl; + pipe->freenext = cntl->freelist; + cntl->freelist = pipe; }
struct usb_pipe * @@ -339,7 +337,17 @@ ohci_alloc_control_pipe(struct usb_pipe *dummy) dummy->cntl, struct usb_ohci_s, usb); dprintf(7, "ohci_alloc_control_pipe %p\n", &cntl->usb);
- // Allocate a queue head. + if (cntl->usb.freelist) { + // Use previously allocated queue head. + struct ohci_pipe *pipe = container_of(cntl->usb.freelist + , struct ohci_pipe, pipe); + cntl->usb.freelist = pipe->pipe.freenext; + + memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe)); + return &pipe->pipe; + } + + // Allocate a new queue head. struct ohci_pipe *pipe = malloc_tmphigh(sizeof(*pipe)); if (!pipe) { warn_noalloc(); diff --git a/src/usb.h b/src/usb.h index 8b2af40..cc32eb7 100644 --- a/src/usb.h +++ b/src/usb.h @@ -6,7 +6,10 @@
// Information on a USB end point. struct usb_pipe { - struct usb_s *cntl; + union { + struct usb_s *cntl; + struct usb_pipe *freenext; + }; u64 path; u8 type; u8 ep; @@ -20,6 +23,7 @@ struct usb_pipe { // Common information for usb controllers. struct usb_s { struct usb_pipe *defaultpipe; + struct usb_pipe *freelist; struct mutex_s resetlock; struct pci_device *pci; int busid;
Op maandag 20-02-2012 om 13:17 uur [tijdzone -0500], schreef Kevin O'Connor:
Hi Nils,
I put together a patch to modify the way the OHCI controller does pipe free'ing. This is a preliminary patch (similar batch free'ing should be done for the other controllers as well). However, it might help with the USB issues you've been having.
-Kevin
commit fa43976172295d3bad9209374bd68469c10ec918 Author: Kevin O'Connor kevin@koconnor.net Date: Mon Feb 20 12:54:49 2012 -0500
Batch free USB pipes on OHCI controllers. Instead of unregistering each control "endpoint descriptor" after it is used, keep them around for later users. Free all unused descriptors in one batch at the end of initialization. This should slightly optimize boot time, and it requires less overall interaction with the controller. This also makes the descriptor free code more compliant with the spec. The descriptor lists will only be modified after the list processing has been disabled on the controller. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
diff --git a/src/usb-ohci.c b/src/usb-ohci.c index 9107db2..7a437ad 100644 --- a/src/usb-ohci.c +++ b/src/usb-ohci.c @@ -19,6 +19,14 @@ struct usb_ohci_s { struct ohci_regs *regs; };
+struct ohci_pipe {
- struct ohci_ed ed;
- struct usb_pipe pipe;
- void *data;
- int count;
- struct ohci_td *tds;
+};
/****************************************************************
- Root hub
@@ -109,6 +117,56 @@ check_ohci_ports(struct usb_ohci_s *cntl)
- Setup
****************************************************************/
+// Wait for next USB frame to start - for ensuring safe memory release. +static void +ohci_waittick(struct usb_ohci_s *cntl) +{
- barrier();
- struct ohci_hcca *hcca = (void*)cntl->regs->hcca;
- u32 startframe = hcca->frame_no;
- u64 end = calc_future_tsc(1000 * 5);
- for (;;) {
if (hcca->frame_no != startframe)
break;
if (check_tsc(end)) {
warn_timeout();
return;
}
yield();
- }
+}
+static void +ohci_free_pipes(struct usb_ohci_s *cntl) +{
- dprintf(7, "ohci_free_pipes %p\n", cntl);
- u32 creg = readl(&cntl->regs->control);
- if (creg & (OHCI_CTRL_CLE|OHCI_CTRL_BLE)) {
writel(&cntl->regs->control, creg & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE));
ohci_waittick(cntl);
- }
- u32 *pos = &cntl->regs->ed_controlhead;
- for (;;) {
struct ohci_ed *next = (void*)*pos;
if (!next)
break;
struct ohci_pipe *pipe = container_of(next, struct ohci_pipe, ed);
if (pipe->pipe.cntl != &cntl->usb) {
*pos = next->hwNextED;
free(pipe);
} else {
pos = &next->hwNextED;
}
- }
- writel(&cntl->regs->ed_controlcurrent, 0);
- writel(&cntl->regs->ed_bulkcurrent, 0);
- writel(&cntl->regs->control, creg);
- cntl->usb.freelist = NULL;
+}
static int start_ohci(struct usb_ohci_s *cntl, struct ohci_hcca *hcca) { @@ -192,6 +250,7 @@ configure_ohci(void *data)
int count = check_ohci_ports(cntl); free_pipe(cntl->usb.defaultpipe);
- ohci_free_pipes(cntl); if (! count) goto err; return;
@@ -260,74 +319,13 @@ wait_ed(struct ohci_ed *ed) } }
-// Wait for next USB frame to start - for ensuring safe memory release. -static void -ohci_waittick(struct usb_ohci_s *cntl) -{
- barrier();
- struct ohci_hcca *hcca = (void*)cntl->regs->hcca;
- u32 startframe = hcca->frame_no;
- u64 end = calc_future_tsc(1000 * 5);
- for (;;) {
if (hcca->frame_no != startframe)
break;
if (check_tsc(end)) {
warn_timeout();
return;
}
yield();
- }
-}
-static void -signal_freelist(struct usb_ohci_s *cntl) -{
- u32 v = readl(&cntl->regs->control);
- if (v & OHCI_CTRL_CLE) {
writel(&cntl->regs->control, v & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE));
ohci_waittick(cntl);
writel(&cntl->regs->ed_controlcurrent, 0);
writel(&cntl->regs->ed_bulkcurrent, 0);
writel(&cntl->regs->control, v);
- } else {
ohci_waittick(cntl);
- }
-}
-struct ohci_pipe {
- struct ohci_ed ed;
- struct usb_pipe pipe;
- void *data;
- int count;
- struct ohci_td *tds;
-};
void -ohci_free_pipe(struct usb_pipe *p) +ohci_free_pipe(struct usb_pipe *pipe) {
- if (! CONFIG_USB_OHCI)
return;
- dprintf(7, "ohci_free_pipe %p\n", p);
- struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe);
- struct usb_ohci_s *cntl = container_of(
pipe->pipe.cntl, struct usb_ohci_s, usb);
- u32 *pos = &cntl->regs->ed_controlhead;
- for (;;) {
struct ohci_ed *next = (void*)*pos;
if (!next) {
// Not found?! Exit without freeing.
warn_internalerror();
return;
}
if (next == &pipe->ed) {
*pos = next->hwNextED;
signal_freelist(cntl);
free(pipe);
return;
}
pos = &next->hwNextED;
- }
- // Add to controller's free list.
- struct usb_s *cntl = pipe->cntl;
- pipe->freenext = cntl->freelist;
- cntl->freelist = pipe;
}
struct usb_pipe * @@ -339,7 +337,17 @@ ohci_alloc_control_pipe(struct usb_pipe *dummy) dummy->cntl, struct usb_ohci_s, usb); dprintf(7, "ohci_alloc_control_pipe %p\n", &cntl->usb);
- // Allocate a queue head.
- if (cntl->usb.freelist) {
// Use previously allocated queue head.
struct ohci_pipe *pipe = container_of(cntl->usb.freelist
, struct ohci_pipe, pipe);
cntl->usb.freelist = pipe->pipe.freenext;
memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
return &pipe->pipe;
- }
- // Allocate a new queue head. struct ohci_pipe *pipe = malloc_tmphigh(sizeof(*pipe)); if (!pipe) { warn_noalloc();
diff --git a/src/usb.h b/src/usb.h index 8b2af40..cc32eb7 100644 --- a/src/usb.h +++ b/src/usb.h @@ -6,7 +6,10 @@
// Information on a USB end point. struct usb_pipe {
- struct usb_s *cntl;
- union {
struct usb_s *cntl;
struct usb_pipe *freenext;
- }; u64 path; u8 type; u8 ep;
@@ -20,6 +23,7 @@ struct usb_pipe { // Common information for usb controllers. struct usb_s { struct usb_pipe *defaultpipe;
- struct usb_pipe *freelist; struct mutex_s resetlock; struct pci_device *pci; int busid;
Thanks for the patch. I tested this patch and the keyboard gets initialized now. The keyboard doesn't seem to react on keystroke yet tough and i still get a timeout error.(another bug?)
Thanks, Nils.
On Tue, Feb 21, 2012 at 09:43:12PM +0100, Nils wrote:
Op maandag 20-02-2012 om 13:17 uur [tijdzone -0500], schreef Kevin O'Connor:
I put together a patch to modify the way the OHCI controller does pipe free'ing. This is a preliminary patch (similar batch free'ing should be done for the other controllers as well). However, it might help with the USB issues you've been having.
Thanks for the patch. I tested this patch and the keyboard gets initialized now. The keyboard doesn't seem to react on keystroke yet tough and i still get a timeout error.(another bug?)
Well, that's not good. I was assuming that the earlier problems were due to a sloppy free, but it looks like something else is confusing the controller. The previous and current errors are probably related. It's odd that control messages seem to be handled properly while the frame_no and interrupt lists aren't. To fix this, more debugging is needed to see what's going wrong at what point. You can start with the patch below.
-Kevin
--- a/src/usb-ohci.c +++ b/src/usb-ohci.c @@ -123,6 +123,8 @@ ohci_waittick(struct usb_ohci_s *cntl) { barrier(); struct ohci_hcca *hcca = (void*)cntl->regs->hcca; + dprintf(1, "ohci wait: c=%x h=%p f=%d\n" + , cntl->regs->control, hcca, hcca->frame_no); u32 startframe = hcca->frame_no; u64 end = calc_future_tsc(1000 * 5); for (;;) { @@ -214,6 +216,9 @@ start_ohci(struct usb_ohci_s *cntl, struct ohci_hcca *hcca) | OHCI_USB_OPER | oldrwc)); readl(&cntl->regs->control); // flush writes
+ dprintf(1, "ohci init: c=%x h=%p h=%x f=%d\n" + , cntl->regs->control, hcca, cntl->regs->hcca, hcca->frame_no); + return 0; }
Op dinsdag 21-02-2012 om 19:31 uur [tijdzone -0500], schreef Kevin O'Connor:
On Tue, Feb 21, 2012 at 09:43:12PM +0100, Nils wrote:
Op maandag 20-02-2012 om 13:17 uur [tijdzone -0500], schreef Kevin O'Connor:
I put together a patch to modify the way the OHCI controller does pipe free'ing. This is a preliminary patch (similar batch free'ing should be done for the other controllers as well). However, it might help with the USB issues you've been having.
Thanks for the patch. I tested this patch and the keyboard gets initialized now. The keyboard doesn't seem to react on keystroke yet tough and i still get a timeout error.(another bug?)
Well, that's not good. I was assuming that the earlier problems were due to a sloppy free, but it looks like something else is confusing the controller. The previous and current errors are probably related. It's odd that control messages seem to be handled properly while the frame_no and interrupt lists aren't. To fix this, more debugging is needed to see what's going wrong at what point. You can start with the patch below.
See attachment for the corresponding log.
Thanks, Nils.
On Wed, Feb 22, 2012 at 06:59:38PM +0100, Nils wrote:
Op dinsdag 21-02-2012 om 19:31 uur [tijdzone -0500], schreef Kevin O'Connor:
Well, that's not good. I was assuming that the earlier problems were due to a sloppy free, but it looks like something else is confusing the controller. The previous and current errors are probably related. It's odd that control messages seem to be handled properly while the frame_no and interrupt lists aren't. To fix this, more debugging is needed to see what's going wrong at what point. You can start with the patch below.
See attachment for the corresponding log.
[...]
OHCI init on dev 00:0f.4 (regs=0xfe00e000) pmm_malloc zone=0x1f7bfec4 handle=ffffffff size=256 align=100 ret=0x1f7cff00 (detail=0x1f7b7000) pmm_malloc zone=0x1f7bfec4 handle=ffffffff size=16 align=10 ret=0x1f7cfef0 (detail=0x1f7b6fd0) ohci init: c=97 h=0x1f7cff00 h=1f7cff00 f=0
[...]
ohci wait: c=87 h=0x1f7cff00 f=236 WARNING - Timeout at ohci_waittick:134!
That's odd. It looks like the controller was running for 236ms and then freezes. The only thing I can think of to track this down is to sprinkle dprintf() statements through the code until we find what caused the controller to stop.
As an example of more debugging, see the patch below. I think you'll likely need to keep adding statements until finding the place where the counter stops.
Another thing to check is to see if the Linux code has any quirks for this particular controller.
-Kevin
--- a/src/usb-ohci.c +++ b/src/usb-ohci.c @@ -123,6 +123,8 @@ ohci_waittick(struct usb_ohci_s *cntl) { barrier(); struct ohci_hcca *hcca = (void*)cntl->regs->hcca; + dprintf(1, "ohci wait: c=%x h=%p f=%d s=%x\n" + , cntl->regs->control, hcca, hcca->frame_no, cntl->regs->intrstatus); u32 startframe = hcca->frame_no; u64 end = calc_future_tsc(1000 * 5); for (;;) { @@ -130,6 +132,8 @@ ohci_waittick(struct usb_ohci_s *cntl) break; if (check_tsc(end)) { warn_timeout(); + dprintf(1, "ohci to: c=%x h=%p f=%d s=%x\n" + , cntl->regs->control, hcca, hcca->frame_no, cntl->regs->intrstatus); return; } yield(); @@ -141,11 +145,16 @@ ohci_free_pipes(struct usb_ohci_s *cntl) { dprintf(7, "ohci_free_pipes %p\n", cntl);
+ struct ohci_hcca *hcca = (void*)cntl->regs->hcca; + dprintf(1, "ohci fp: c=%x h=%p f=%d s=%x\n" + , cntl->regs->control, hcca, hcca->frame_no, cntl->regs->intrstatus); u32 creg = readl(&cntl->regs->control); if (creg & (OHCI_CTRL_CLE|OHCI_CTRL_BLE)) { writel(&cntl->regs->control, creg & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE)); ohci_waittick(cntl); } + dprintf(1, "ohci fp2: c=%x h=%p f=%d s=%x\n" + , cntl->regs->control, hcca, hcca->frame_no, cntl->regs->intrstatus);
u32 *pos = &cntl->regs->ed_controlhead; for (;;) { @@ -214,6 +223,9 @@ start_ohci(struct usb_ohci_s *cntl, struct ohci_hcca *hcca) | OHCI_USB_OPER | oldrwc)); readl(&cntl->regs->control); // flush writes
+ dprintf(1, "ohci init: c=%x h=%p f=%d s=%x\n" + , cntl->regs->control, hcca, hcca->frame_no, cntl->regs->intrstatus); + return 0; }
On Wed, Feb 22, 2012 at 08:48:33PM -0500, Kevin O'Connor wrote:
That's odd. It looks like the controller was running for 236ms and then freezes. The only thing I can think of to track this down is to sprinkle dprintf() statements through the code until we find what caused the controller to stop.
Another thing you could try would be to use the script in tools/readserial.py on the host machine that reads from the serial port of your target machine. That script can provide timestamps on debug messages which may help correlate the frame count time (236ms) to debugging messages at that time. Use the "-n" option on the script - something like:
./tools/readserial.py -n /dev/ttyUSB0 115200
-Kevin
Op woensdag 22-02-2012 om 20:48 uur [tijdzone -0500], schreef Kevin O'Connor:
[...]
OHCI init on dev 00:0f.4 (regs=0xfe00e000) pmm_malloc zone=0x1f7bfec4 handle=ffffffff size=256 align=100 ret=0x1f7cff00 (detail=0x1f7b7000) pmm_malloc zone=0x1f7bfec4 handle=ffffffff size=16 align=10 ret=0x1f7cfef0 (detail=0x1f7b6fd0) ohci init: c=97 h=0x1f7cff00 h=1f7cff00 f=0
[...]
ohci wait: c=87 h=0x1f7cff00 f=236 WARNING - Timeout at ohci_waittick:134!
That's odd. It looks like the controller was running for 236ms and then freezes. The only thing I can think of to track this down is to sprinkle dprintf() statements through the code until we find what caused the controller to stop.
As an example of more debugging, see the patch below. I think you'll likely need to keep adding statements until finding the place where the counter stops.
Another thing to check is to see if the Linux code has any quirks for this particular controller.
I ran your debugging example with timestamps enabled. (attached for reference) I'l try to study/test this some more in the next day's.
Thanks for your help!
Nils.