ref: f0facb2ed88a2d9768340c9577aede526df3c09f
parent: c4535df0c431cfbfdd635d53570477d3199f1519
author: cinap_lenrek <[email protected]>
date: Wed Mar 7 18:50:58 EST 2012
usbehci: import changes from sources (portreset, port handover handling), set interrupt threshold to 1 uframe
--- 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 @@
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 @@
* 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 @@
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();
--- a/sys/src/9/port/usbehci.c
+++ b/sys/src/9/port/usbehci.c
@@ -514,7 +514,10 @@
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 @@
static int
portreset(Hci *hp, int port, int on)
{
- ulong s;
+ ulong *portscp;
Eopio *opio;
Ctlr *ctlr;
int i;
@@ -1678,31 +1681,35 @@
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 @@
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 @@
{
Ctlr *ctlr;
Eopio *opio;
+ static int ctlrno;
int i;
hp->highspeed = 1;
@@ -3218,7 +3229,12 @@
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 @@
iunlock(ctlr);
if(ehcidebug > 1)
dump(hp);
+ ctlrno++;
}
void