From f0facb2ed88a2d9768340c9577aede526df3c09f Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Wed, 7 Mar 2012 23:50:58 +0100 Subject: [PATCH] usbehci: import changes from sources (portreset, port handover handling), set interrupt threshold to 1 uframe --- sys/src/9/pc/usbehcipc.c | 20 +++++++------ sys/src/9/port/usbehci.c | 63 +++++++++++++++++++++++++--------------- 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/sys/src/9/pc/usbehcipc.c b/sys/src/9/pc/usbehcipc.c index c67901bed..b1394c039 100644 --- a/sys/src/9/pc/usbehcipc.c +++ b/sys/src/9/pc/usbehcipc.c @@ -15,6 +15,7 @@ #include "usbehci.h" static Ctlr* ctlrs[Nhcis]; +static int maxehci = Nhcis; /* Isn't this cap list search in a helper function? */ static void @@ -89,9 +90,7 @@ ehcireset(Ctlr *ctlr) if(i == 100) print("ehci %#p controller reset timed out\n", ctlr->capio); } - - /* requesting more interrupts per µframe may miss interrupts */ - opio->cmd |= Citc8; /* 1 intr. per ms */ + opio->cmd |= Citc1; /* 1 intr. per µframe */ coherence(); switch(opio->cmd & Cflsmask){ case Cfls1024: @@ -202,11 +201,10 @@ scanpci(void) * currently, if we enable a second ehci controller, * we'll wedge solid after iunlock in init for the second one. */ - if (i > 0) { -// iprint("usbehci: ignoring controllers after the first, " -// "at %#p\n", io); -// ctlrs[i] = nil; - iprint("usbehci: multiple controllers present\n"); + if (i >= maxehci) { + iprint("usbehci: ignoring controllers after first %d, " + "at %#p\n", maxehci, io); + ctlrs[i] = nil; } } } @@ -215,12 +213,16 @@ static int reset(Hci *hp) { int i; + char *s; Ctlr *ctlr; Ecapio *capio; Pcidev *p; static Lock resetlck; - if(getconf("*nousbehci")) + s = getconf("*maxehci"); + if (s != nil && s[0] >= '0' && s[0] <= '9') + maxehci = atoi(s); + if(maxehci == 0 || getconf("*nousbehci")) return -1; ilock(&resetlck); scanpci(); diff --git a/sys/src/9/port/usbehci.c b/sys/src/9/port/usbehci.c index 1f0b77488..ef1c1ca71 100644 --- a/sys/src/9/port/usbehci.c +++ b/sys/src/9/port/usbehci.c @@ -514,7 +514,10 @@ static Qh* qhlinkqh(Qh *qh, Qh *next) { qh->next = next; - qh->link = PADDR(next)|Lqh; + if(next == nil) + qh->link = Lterm; + else + qh->link = PADDR(next)|Lqh; coherence(); return qh; } @@ -1662,7 +1665,7 @@ portlend(Ctlr *ctlr, int port, char *ss) static int portreset(Hci *hp, int port, int on) { - ulong s; + ulong *portscp; Eopio *opio; Ctlr *ctlr; int i; @@ -1678,31 +1681,35 @@ portreset(Hci *hp, int port, int on) qunlock(&ctlr->portlck); nexterror(); } - s = opio->portsc[port-1]; - dprint("ehci %#p port %d reset; sts %#lux\n", ctlr->capio, port, s); + portscp = &opio->portsc[port-1]; + dprint("ehci %#p port %d reset; sts %#lux\n", ctlr->capio, port, *portscp); ilock(ctlr); - s &= ~(Psenable|Psreset); - opio->portsc[port-1] = s | Psreset; /* initiate reset */ + /* Shalted must be zero, else Psreset will stay set */ + if (opio->sts & Shalted) + iprint("ehci %#p: halted yet trying to reset port\n", + ctlr->capio); + *portscp = (*portscp & ~Psenable) | Psreset; /* initiate reset */ coherence(); - for(i = 0; i < 50; i++){ /* was 10 */ + /* + * usb 2 spec: reset must finish within 20 ms. + * linux says spec says it can take 50 ms. for hubs. + */ + for(i = 0; *portscp & Psreset && i < 10; i++) delay(10); - if((opio->portsc[port-1] & Psreset) == 0) - break; - } - if (opio->portsc[port-1] & Psreset) - iprint("ehci %#p: port %d didn't reset after %d ms; sts %#lux\n", - ctlr->capio, port, i * 10, opio->portsc[port-1]); - opio->portsc[port-1] &= ~Psreset; /* force appearance of reset done */ + if (*portscp & Psreset) + iprint("ehci %#p: port %d didn't reset within %d ms; sts %#lux\n", + ctlr->capio, port, i * 10, *portscp); + *portscp &= ~Psreset; /* force appearance of reset done */ coherence(); + delay(10); /* ehci spec: enable within 2 ms. */ - delay(10); - if((opio->portsc[port-1] & Psenable) == 0) + if((*portscp & Psenable) == 0) portlend(ctlr, port, "full"); iunlock(ctlr); dprint("ehci %#p after port %d reset; sts %#lux\n", - ctlr->capio, port, opio->portsc[port-1]); + ctlr->capio, port, *portscp); qunlock(&ctlr->portlck); poperror(); return 0; @@ -2407,15 +2414,18 @@ epio(Ep *ep, Qio *io, void *a, long count, int mustlock) ntds++; /* * Use td tok, not io tok, because of setup packets. - * Also, if the Td was stalled or active (previous Td - * was a short packet), we must save the toggle as it is. + * Also, we must save the next toggle value from the + * last completed Td (in case of a short packet, or + * fewer than the requested number of packets in the + * Td being transferred). */ - if(td->csw & (Tdhalt|Tdactive)){ - if(saved++ == 0) { + if(td->csw & (Tdhalt|Tdactive)) + saved++; + else{ + if(!saved){ io->toggle = td->csw & Tddata1; coherence(); } - }else{ tot += td->ndata; if(c != nil && (td->csw & Tdtok) == Tdtokin && td->ndata > 0){ memmove(c, td->data, td->ndata); @@ -3199,6 +3209,7 @@ init(Hci *hp) { Ctlr *ctlr; Eopio *opio; + static int ctlrno; int i; hp->highspeed = 1; @@ -3218,7 +3229,12 @@ init(Hci *hp) opio->cmd |= Case; coherence(); ehcirun(ctlr, 1); - opio->config = Callmine; /* reclaim all ports */ + /* + * route all ports by default to only one ehci (the first). + * it's not obvious how multiple ehcis could work and on some + * machines, setting Callmine on all ehcis makes the machine seize up. + */ + opio->config = (ctlrno == 0 ? Callmine : 0); coherence(); for (i = 0; i < hp->nports; i++) @@ -3226,6 +3242,7 @@ init(Hci *hp) iunlock(ctlr); if(ehcidebug > 1) dump(hp); + ctlrno++; } void