shithub: riscv

Download patch

ref: c74538a67e57ab9e37826104210d1c1f64711a04
parent: aaf6d7c5586d442c0bc10ed50558b984faeb51aa
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Fri Jul 28 21:02:23 EDT 2017

usbxhci: implement controller handoff, ignore usb3.0 ports, honor pollival for isochronous endpoints

--- a/sys/src/9/pc/usbxhci.c
+++ b/sys/src/9/pc/usbxhci.c
@@ -147,6 +147,7 @@
 typedef struct Ring Ring;
 typedef struct Slot Slot;
 typedef struct Epio Epio;
+typedef struct Port Port;
 
 struct Wait
 {
@@ -193,6 +194,14 @@
 	Ring	epr[32];
 };
 
+struct Port
+{
+	char	spec[4];
+	int	proto;
+
+	u32int	*reg;
+};
+
 struct Ctlr
 {
 	Pcidev	*pcidev;
@@ -200,7 +209,6 @@
 	u32int	*mmio;
 
 	u32int	*opr;	/* operational registers */
-	u32int	*prt;	/* port register set */
 	u32int	*rts;	/* runtime registers */
 	u32int	*dba;	/* doorbell array */
 
@@ -213,10 +221,11 @@
 	Ring	er[1];		/* event ring segment */
 	Ring	cr[1];		/* command ring segment */
 
-	u32int	mfwrap;
+	u32int	µframe;
 
 	QLock	slotlock;
 	Slot	**slot;		/* slots by slot id */
+	Port	*port;
 	
 	u32int	hccparams;
 
@@ -239,10 +248,12 @@
 	Block	*b;
 
 	/* iso */
-	int	nleft;
 	u32int	frame;
+	u32int	period;
 	u32int	incr;
 	u32int	tdsz;
+
+	int	nleft;
 };
 
 static char Ebadlen[] = "bad usb request length";
@@ -262,15 +273,14 @@
 }
 
 static u32int
-mfindex(Ctlr *ctlr)
+µframe(Ctlr *ctlr)
 {
-	u32int lo, hi;
-
+	u32int µ;
 	do {
-		hi = ctlr->mfwrap;
-		lo = ctlr->rts[MFINDEX];
-	} while(hi != ctlr->mfwrap);
-	return (lo & (1<<14)-1) | hi<<14;
+		µ = (ctlr->rts[MFINDEX] & (1<<14)-1) |
+			(ctlr->µframe & ~((1<<14)-1));
+	} while((int)(µ - ctlr->µframe) < 0);
+	return µ;
 }
 
 static void
@@ -302,25 +312,67 @@
 	return r;
 }
 
+static u32int*
+xecp(Ctlr *ctlr, uchar id, u32int *p)
+{
+	u32int x;
+
+	if(p != nil) {
+		x = *p;
+		goto Next;
+	}
+	x = ctlr->hccparams>>16;
+	if(x == 0)
+		return nil;
+	p = ctlr->mmio + x;
+	while(((x = *p) & 255) != id){
+	Next:
+		x >>= 8, x &= 255;
+		if(x == 0)
+			return nil;
+		p += x;
+	}
+	return p;
+}
+
 static void
+handoff(Ctlr *ctlr)
+{
+	u32int *r;
+	int i;
+
+	if((r = xecp(ctlr, 1, nil)) == nil)
+		return;
+	r[0] |= 1<<24;		/* request ownership */
+	for(i = 0; (r[0] & (1<<16)) != 0 && i<100; i++)
+		tsleep(&up->sleep, return0, nil, 10);
+	r[1] = 0;		/* disable SMI interrupts */
+	r[0] &= ~(1<<16);	/* in case of timeout */
+}
+
+static void
 init(Hci *hp)
 {
 	Ctlr *ctlr;
+	Port *pp;
+	u32int *x;
 	uchar *p;
-	int i;
+	int i, j;
 
 	ctlr = hp->aux;
 
 	ctlr->opr = &ctlr->mmio[(ctlr->mmio[CAPLENGTH]&0xFF)/4];
-	ctlr->prt = &ctlr->opr[0x400/4];
 	ctlr->dba = &ctlr->mmio[ctlr->mmio[DBOFF]/4];
 	ctlr->rts = &ctlr->mmio[ctlr->mmio[RTSOFF]/4];
 
-	while(ctlr->opr[USBSTS] & CNR)
+	ctlr->hccparams = ctlr->mmio[HCCPARAMS];
+	handoff(ctlr);
+
+	for(i=0; (ctlr->opr[USBSTS] & CNR) != 0 && i<100; i++)
 		tsleep(&up->sleep, return0, nil, 10);
 
 	ctlr->opr[USBCMD] = HCRST;
-	while((ctlr->opr[USBSTS] & (CNR|HCH)) != HCH)
+	for(i=0; (ctlr->opr[USBSTS] & (CNR|HCH)) != HCH && i<100; i++)
 		tsleep(&up->sleep, return0, nil, 10);
 
 	pcisetbme(ctlr->pcidev);
@@ -327,13 +379,11 @@
 	pcisetpms(ctlr->pcidev, 0);
 	intrenable(ctlr->pcidev->intl, hp->interrupt, hp, ctlr->pcidev->tbdf, hp->type);
 
-	ctlr->hccparams = ctlr->mmio[HCCPARAMS];
 	ctlr->csz = (ctlr->hccparams & CSZ) != 0;
 	if(ctlr->hccparams & AC64)
 		ctlr->setrptr = setrptr64;
 	else
 		ctlr->setrptr = setrptr32;
-
 	ctlr->pagesize = ctlr->opr[PAGESIZE]<<12;
 
 	ctlr->nscratch = (ctlr->mmio[HCSPARAMS2] >> 27) & 0x1F;
@@ -341,6 +391,24 @@
 	ctlr->nslots = (ctlr->mmio[HCSPARAMS1] >> 0) & 0xFF;
 	hp->nports = (ctlr->mmio[HCSPARAMS1] >> 24) & 0xFF;
 
+	ctlr->port = malloc(hp->nports * sizeof(Port));
+	for(i=0; i<hp->nports; i++)
+		ctlr->port[i].reg = &ctlr->opr[0x400/4 + i*4];
+
+	x = nil;
+	while((x = xecp(ctlr, 2, x)) != nil){
+		i = x[2]&255;
+		j = (x[2]>>8)&255;
+		while(j--){
+			if(i < 1 || i > hp->nports)
+				break;
+			pp = &ctlr->port[i-1];
+			pp->proto = x[0]>>16;
+			memmove(pp->spec, &x[1], 4);
+			i++;
+		}
+	}
+
 	ctlr->slot = malloc((1+ctlr->nslots)*sizeof(ctlr->slot[0]));
 	ctlr->dcba = mallocalign((1+ctlr->nslots)*8, 64, 0, ctlr->pagesize);
 	if(ctlr->nscratch != 0){
@@ -391,10 +459,9 @@
 		irs[IMOD] = 0;
 	}	
 
-	ctlr->mfwrap = 0;
-
+	ctlr->µframe = 0;
 	ctlr->opr[USBCMD] = RUNSTOP|INTE|HSEE|EWE;
-	while(ctlr->opr[USBSTS] & (CNR|HCH))
+	for(i=0; (ctlr->opr[USBSTS] & (CNR|HCH)) != 0 && i<100; i++)
 		tsleep(&up->sleep, return0, nil, 10);
 }
 
@@ -580,6 +647,9 @@
 	Slot *slot;
 	u32int *irs, *td, x;
 
+	if(ring->base == nil)
+		return;
+
 	irs = &ctlr->rts[IR0];
 	x = irs[IMAN];
 	if(x & 1) irs[IMAN] = x & 3;
@@ -603,7 +673,8 @@
 			completering(&slot->epr[(td[3]>>16)-1&31], td);
 			break;
 		case ER_MFINDEXWRAP:
-			ctlr->mfwrap++;
+			ctlr->µframe = (ctlr->rts[MFINDEX] & (1<<14)-1) | 
+				(ctlr->µframe+(1<<14) & ~((1<<14)-1));
 			break;
 		case ER_HCE:
 			iprint("xhci: host controller error: %ux %ux %ux %ux\n",
@@ -787,10 +858,9 @@
 	if(io->ring == nil)
 		return;
 	io->frame = 0;
-	io->incr = (ep->hz<<8)/1000;
+	io->period = ep->pollival<<3*(ep->dev->speed == Fullspeed);
+	io->incr = (ep->hz*io->period<<8)/8000;
 	io->tdsz = (io->incr+255>>8)*ep->samplesz;
-	if(io->tdsz > ep->maxpkt*ep->ntds)
-		error(Egreg);
 	io->b = allocb((io->ring->mask+1)*io->tdsz);
 }
 
@@ -977,36 +1047,6 @@
 	poperror();
 }
 
-static char*
-unstall(Ring *r)
-{
-	u64int qp;
-	char *err;
-
-	switch(r->ctx[0]&7){
-	case 0:	/* disabled */
-	case 1:	/* running */
-	case 3:	/* stopped */
-		break;
-	case 2:	/* halted */
-	case 4:	/* error */
-		ilock(r);
-		r->rp = r->wp;
-		qp = PADDR(&r->base[4*(r->wp & r->mask)]) | ((~r->wp>>r->shift) & 1);
-		iunlock(r);
-
-		err = ctlrcmd(r->slot->ctlr, CR_RESETEP | (r->id<<16) | (r->slot->id<<24), 0, 0, nil);
-		ctlrcmd(r->slot->ctlr, CR_SETTRDQP | (r->id<<16) | (r->slot->id<<24), 0, qp, nil);
-		if(err != nil)
-			return err;
-	}
-
-	if(r->wp - r->rp >= r->mask)
-		return "Ring Full";
-
-	return nil;
-}
-
 static long
 isoread(Ep *, uchar *, long)
 {
@@ -1020,7 +1060,7 @@
 	uchar *s, *d;
 	Ctlr *ctlr;
 	Epio *io;
-	u32int x;
+	u32int i, µ;
 	long m;
 
 	s = p;
@@ -1030,21 +1070,20 @@
 		qunlock(io);
 		nexterror();
 	}
-
+	µ = io->period;
 	ctlr = ep->hp->aux;
-	for(x = io->frame;; x++){
+	for(i = io->frame;; i++){
 		for(;;){
 			m = (int)(io->ring->wp - io->ring->rp);
 			if(m <= 0)
-				x = 10 + mfindex(ctlr)/8;
-			if(m < io->ring->mask-1)
+				i = (80 + µframe(ctlr))/µ;
+			if(m < io->ring->mask)
 				break;
-			coherence();
 			*io->ring->doorbell = io->ring->id;
 			tsleep(&up->sleep, return0, nil, 5);
 		}
-		m = ((io->incr + (x*io->incr&255))>>8)*ep->samplesz;
-		d = io->b->rp + (x&io->ring->mask)*io->tdsz;
+		m = ((io->incr + (i*io->incr&255))>>8)*ep->samplesz;
+		d = io->b->rp + (i&io->ring->mask)*io->tdsz;
 		m -= io->nleft, d += io->nleft;
 		if(n < m){
 			memmove(d, p, n);
@@ -1056,28 +1095,56 @@
 		p += m, n -= m;
 		m += io->nleft, d -= io->nleft;
 		io->nleft = 0;
-
+		coherence();
 		ilock(io->ring);
-		queuetd(io->ring, TR_ISOCH | (x & 0x7ff)<<20 | 1<<5, m, PADDR(d), nil);
+		queuetd(io->ring, TR_ISOCH | (i*µ/8 & 0x7ff)<<20 | 1<<5, m, PADDR(d), nil);
 		iunlock(io->ring);
 	}
-	io->frame = x;
-	coherence();
-	*io->ring->doorbell = io->ring->id;
-	qunlock(io);
-	poperror();
-
-	for(;;){
-		int d = (int)(x - mfindex(ctlr)/8);
+	io->frame = i;
+	while(io->ring->rp != io->ring->wp){
+		int d = (int)(i*µ - µframe(ctlr))/8;
 		d -= ep->sampledelay*1000 / ep->hz;
 		if(d < 5)
 			break;
+		*io->ring->doorbell = io->ring->id;
 		tsleep(&up->sleep, return0, nil, d);
 	}
+	qunlock(io);
+	poperror();
 
 	return p - s;
 }
 
+static char*
+unstall(Ring *r)
+{
+	u64int qp;
+	char *err;
+
+	switch(r->ctx[0]&7){
+	case 0:	/* disabled */
+	case 1:	/* running */
+	case 3:	/* stopped */
+		break;
+	case 2:	/* halted */
+	case 4:	/* error */
+		ilock(r);
+		r->rp = r->wp;
+		qp = PADDR(&r->base[4*(r->wp & r->mask)]) | ((~r->wp>>r->shift) & 1);
+		iunlock(r);
+
+		err = ctlrcmd(r->slot->ctlr, CR_RESETEP | (r->id<<16) | (r->slot->id<<24), 0, 0, nil);
+		ctlrcmd(r->slot->ctlr, CR_SETTRDQP | (r->id<<16) | (r->slot->id<<24), 0, qp, nil);
+		if(err != nil)
+			return err;
+	}
+
+	if(r->wp - r->rp >= r->mask)
+		return "Ring Full";
+
+	return nil;
+}
+
 static long
 epread(Ep *ep, void *va, long n)
 {
@@ -1269,9 +1336,13 @@
 portstatus(Hci *hp, int port)
 {
 	Ctlr *ctlr = hp->aux;
-	u32int psc = ctlr->prt[PORTSC + (port-1)*4];
+	Port *pp = &ctlr->port[port-1];
+	u32int psc = pp->reg[PORTSC];
 	int ps = 0;
 
+	if(memcmp(pp->spec, "USB", 3) != 0 || pp->proto > 0x0200)
+		return 0;
+
 	if(psc & CCS)	ps |= HPpresent;
 	if(psc & PED)	ps |= HPenable;
 	if(psc & OCA)	ps |= HPovercurrent;
@@ -1287,8 +1358,7 @@
 		case 3:
 			ps |= HPhigh;
 			break;
-		case 4:
-			/* super speed */
+		case 4:	/* super speed */
 			break;
 		}
 	}
@@ -1310,10 +1380,13 @@
 portreset(Hci *hp, int port, int on)
 {
 	Ctlr *ctlr = hp->aux;
-	u32int *r = &ctlr->prt[PORTSC + (port-1)*4];
+	Port *pp = &ctlr->port[port-1];
 
+	if(memcmp(pp->spec, "USB", 3) != 0 || pp->proto > 0x0200)
+		return 0;
+
 	if(on){
-		*r |= PR;
+		pp->reg[PORTSC] |= PR;
 		tsleep(&up->sleep, return0, nil, 200);
 	}