shithub: riscv

Download patch

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