shithub: riscv

Download patch

ref: 6e65596827f7ee292221697ff5248c9bc9520851
parent: 215b67ff3d630c56418026dda7ae68f23111c4a5
author: cinap_lenrek <[email protected]>
date: Sun Jul 30 23:22:23 EDT 2017

xhci: experimental usb3 support

--- a/sys/src/9/pc/usbohci.c
+++ b/sys/src/9/pc/usbohci.c
@@ -949,7 +949,7 @@
 	case Tctl:
 		cio = ep->aux;
 		s = seprintio(s, e, cio, "c");
-		s = seprint(s, e, "\trepl %d ndata %d\n", ep->rhrepl, cio->ndata);
+		s = seprint(s, e, "\trepl %llux ndata %d\n", ep->rhrepl, cio->ndata);
 		break;
 	case Tbulk:
 	case Tintr:
--- a/sys/src/9/pc/usbxhci.c
+++ b/sys/src/9/pc/usbxhci.c
@@ -387,8 +387,10 @@
 	ctlr->nscratch = (ctlr->mmio[HCSPARAMS2] >> 27) & 0x1F;
 	ctlr->nintrs = (ctlr->mmio[HCSPARAMS1] >> 8) & 0x7FF;
 	ctlr->nslots = (ctlr->mmio[HCSPARAMS1] >> 0) & 0xFF;
-	hp->nports = (ctlr->mmio[HCSPARAMS1] >> 24) & 0xFF;
 
+	hp->highspeed = 1;
+	hp->superspeed = 0;
+	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];
@@ -403,6 +405,8 @@
 			pp = &ctlr->port[i-1];
 			pp->proto = x[0]>>16;
 			memmove(pp->spec, &x[1], 4);
+			if(memcmp(pp->spec, "USB ", 4) == 0 && pp->proto >= 0x0300)
+				hp->superspeed |= 1<<(i-1);
 			i++;
 		}
 	}
@@ -1151,6 +1155,9 @@
 	char *err;
 	Wait ws[1];
 
+	if(ep->dev->isroot)
+		error(Egreg);
+
 	p = va;
 	if(ep->ttype == Tctl){
 		io = (Epio*)ep->aux + OREAD;
@@ -1212,6 +1219,9 @@
 	uchar *p;
 	char *err;
 
+	if(ep->dev->isroot)
+		error(Egreg);
+
 	p = va;
 	if(ep->ttype == Tctl){
 		int dir, len;
@@ -1335,41 +1345,46 @@
 {
 	Ctlr *ctlr = hp->aux;
 	Port *pp = &ctlr->port[port-1];
-	u32int psc = pp->reg[PORTSC];
-	int ps = 0;
+	u32int psc, ps = 0;
 
-	if(memcmp(pp->spec, "USB", 3) != 0 || pp->proto > 0x0200)
-		return 0;
-
+	psc = pp->reg[PORTSC];
 	if(psc & CCS)	ps |= HPpresent;
 	if(psc & PED)	ps |= HPenable;
 	if(psc & OCA)	ps |= HPovercurrent;
 	if(psc & PR)	ps |= HPreset;
-	else {
-		switch((psc>>10)&0xF){
-		case 1:
-			/* full speed */
-			break;
-		case 2:
-			ps |= HPslow;
-			break;
-		case 3:
-			ps |= HPhigh;
-			break;
-		case 4:	/* super speed */
-			break;
+
+	if((hp->superspeed & (1<<(port-1))) != 0){
+		ps |= psc & (PLS|PP);
+		if(psc & CSC)	ps |= 1<<0+16;
+		if(psc & OCC)	ps |= 1<<3+16;
+		if(psc & PRC)	ps |= 1<<4+16;
+		if(psc & WRC)	ps |= 1<<5+16;
+		if(psc & PLC)	ps |= 1<<6+16;
+		if(psc & CEC)	ps |= 1<<7+16;
+	} else {
+		if((ps & HPreset) == 0){
+			switch((psc>>10)&15){
+			case 1:
+				/* full speed */
+				break;
+			case 2:
+				ps |= HPslow;
+				break;
+			case 3:
+				ps |= HPhigh;
+				break;
+			}
 		}
+		if(psc & PP)	ps |= HPpower;
+		if(psc & CSC)	ps |= HPstatuschg;
+		if(psc & PRC)	ps |= HPchange;
 	}
-	if(psc & PP)	ps |= HPpower;
 
-	if(psc & CSC)	ps |= HPstatuschg;
-	if(psc & PRC)	ps |= HPchange;
-
 	return ps;
 }
 	
 static int
-portenable(Hci *, int, int)
+portenable(Hci *, int port, int on)
 {
 	return 0;
 }
@@ -1379,9 +1394,6 @@
 {
 	Ctlr *ctlr = hp->aux;
 	Port *pp = &ctlr->port[port-1];
-
-	if(memcmp(pp->spec, "USB", 3) != 0 || pp->proto > 0x0200)
-		return 0;
 
 	if(on){
 		pp->reg[PORTSC] |= PR;
--- a/sys/src/9/port/devusb.c
+++ b/sys/src/9/port/devusb.c
@@ -73,7 +73,7 @@
 
 	/* Ep. ctls */
 	CMnew = 0,		/* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
-	CMnewdev,		/* newdev full|low|high portnb (allocate new devices) */
+	CMnewdev,		/* newdev full|low|high|super portnb (allocate new devices) */
 	CMhub,			/* hub (set the device as a hub) */
 	CMspeed,		/* speed full|low|high|no */
 	CMmaxpkt,		/* maxpkt size */
@@ -296,6 +296,7 @@
 	s = seprint(s, se, " hz %ld", ep->hz);
 	s = seprint(s, se, " hub %d", ep->dev->hub);
 	s = seprint(s, se, " port %d", ep->dev->port);
+	s = seprint(s, se, " rootport %d", ep->dev->rootport);
 	s = seprint(s, se, " addr %d", ep->dev->addr);
 	if(ep->inuse)
 		s = seprint(s, se, " busy");
@@ -474,11 +475,8 @@
 	d->isroot = isroot;
 	d->rootport = 0;
 	d->routestr = 0;
-	d->depth = 0;
-	if(hp->highspeed != 0)
-		d->speed = Highspeed;
-	else
-		d->speed = Fullspeed;
+	d->depth = -1;
+	d->speed = Fullspeed;
 	d->state = Dconfig;		/* address not yet set */
 	ep->dev = d;
 	ep->ep0 = ep;			/* no ref counted here */
@@ -746,6 +744,17 @@
 		print("usbreset: bug: Nhcis (%d) too small\n", Nhcis);
 }
 
+static int
+numbits(uint n)
+{
+	int c = 0;
+	while(n != 0){
+		c++;
+		n = (n-1) & n;
+	}
+	return c;
+}
+
 static void
 usbinit(void)
 {
@@ -758,13 +767,31 @@
 	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
 		hp = hcis[ctlrno];
 		if(hp != nil){
+			int n;
+
 			if(hp->init != nil)
 				hp->init(hp);
-			d = newdev(hp, 1, 1);		/* new root hub */
-			d->dev->state = Denabled;	/* although addr == 0 */
-			d->maxpkt = 64;
-			snprint(info, sizeof(info), "ports %d", hp->nports);
-			kstrdup(&d->info, info);
+
+			hp->superspeed &= (1<<hp->nports)-1;
+			n = hp->nports - numbits(hp->superspeed);
+			if(n > 0){
+				d = newdev(hp, 1, 1);		/* new LS/FS/HS root hub */
+				d->maxpkt = 64;
+				if(hp->highspeed != 0)
+					d->dev->speed = Highspeed;
+				d->dev->state = Denabled;	/* although addr == 0 */
+				snprint(info, sizeof(info), "roothub ports %d", n);
+				kstrdup(&d->info, info);
+			}
+			n = numbits(hp->superspeed);
+			if(n > 0){
+				d = newdev(hp, 1, 1);		/* new SS root hub */
+				d->maxpkt = 512;
+				d->dev->speed = Superspeed;
+				d->dev->state = Denabled;	/* although addr == 0 */
+				snprint(info, sizeof(info), "roothub ports %d", n);
+				kstrdup(&d->info, info);
+			}
 		}
 	}
 }
@@ -805,6 +832,7 @@
 	l = 0;
 	bs = 10UL * maxpkt;
 	switch(speed){
+	case Superspeed:
 	case Highspeed:
 		l = 55*8*2 + 2 * (3 + bs) + Hostns;
 		break;
@@ -985,20 +1013,57 @@
 static long
 rhubread(Ep *ep, void *a, long n)
 {
-	char *b;
+	uchar b[8];
 
-	if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2)
+	if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2 || ep->rhrepl == -1)
 		return -1;
-	if(ep->rhrepl < 0)
-		return -1;
 
-	b = a;
-	memset(b, 0, n);
-	PUT2(b, ep->rhrepl);
+	b[0] = ep->rhrepl;
+	b[1] = ep->rhrepl>>8;
+	b[2] = ep->rhrepl>>16;
+	b[3] = ep->rhrepl>>24;
+	b[4] = ep->rhrepl>>32;
+	b[5] = ep->rhrepl>>40;
+	b[6] = ep->rhrepl>>48;
+	b[7] = ep->rhrepl>>56;
+
 	ep->rhrepl = -1;
+
+	if(n > sizeof(b))
+		n = sizeof(b);
+	memmove(a, b, n);
+
 	return n;
 }
 
+static int
+rootport(Ep *ep, int port)
+{
+	Hci *hp;
+	Udev *hub;
+	uint mask;
+	int rootport;
+
+	hp = ep->hp;
+	hub = ep->dev;
+	if(!hub->isroot)
+		return hub->rootport;
+
+	mask = hp->superspeed;
+	if(hub->speed != Superspeed)
+		mask = (1<<hp->nports)-1 & ~mask;
+
+	for(rootport = 1; mask != 0; rootport++){
+		if(mask & 1){
+			if(--port == 0)
+				return rootport;
+		}
+		mask >>= 1;
+	}
+
+	return 0;
+}
+
 static long
 rhubwrite(Ep *ep, void *a, long n)
 {
@@ -1019,8 +1084,8 @@
 	hp = ep->hp;
 	cmd = s[Rreq];
 	feature = GET2(s+Rvalue);
-	port = GET2(s+Rindex);
-	if(port < 1 || port > hp->nports)
+	port = rootport(ep, GET2(s+Rindex));
+	if(port == 0)
 		error("bad hub port number");
 	switch(feature){
 	case Rportenable:
@@ -1089,16 +1154,15 @@
 {
 	long spp, max;	/* samples per packet */
 
-	if(ep->dev->speed == Highspeed || ep->dev->speed == Superspeed)
-		spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
-	else
+	if(ep->dev->speed == Fullspeed)
 		spp = (ep->hz * ep->pollival + 999) / 1000;
+	else
+		spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
 	ep->maxpkt = spp * ep->samplesz;
 	deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
 		" ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
 		ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
 		spp, ep->maxpkt);
-
 	switch(ep->dev->speed){
 	case Fullspeed:
 		max = 1024;
@@ -1170,16 +1234,20 @@
 			error("not a hub setup endpoint");
 		l = name2speed(cb->f[1]);
 		if(l == Nospeed)
-			error("speed must be full|low|high");
+			error("speed must be full|low|high|super");
+		if(l != d->speed && (l == Superspeed || d->speed == Superspeed))
+			error("wrong speed for superspeed hub/device");
 		nep = newdev(ep->hp, 0, 0);
 		nep->dev->speed = l;
-		if(nep->dev->speed != Lowspeed)
+		if(l == Superspeed)
+			nep->maxpkt = 512;
+		else if(l != Lowspeed)
 			nep->maxpkt = 64;	/* assume full speed */
 		nep->dev->hub = d->addr;
 		nep->dev->port = atoi(cb->f[2]);
 		nep->dev->depth = d->depth+1;
-		nep->dev->rootport = d->depth == 0 ? nep->dev->port : d->rootport;
-		nep->dev->routestr = d->routestr | ((nep->dev->port&15) << 4*d->depth) >> 4;
+		nep->dev->rootport = rootport(ep, nep->dev->port);
+		nep->dev->routestr = d->routestr | (((nep->dev->port&15) << 4*nep->dev->depth) >> 4);
 		/* next read request will read
 		 * the name for the new endpoint
 		 */
@@ -1195,7 +1263,9 @@
 		l = name2speed(cb->f[1]);
 		deprint("usb epctl %s %d\n", cb->f[0], l);
 		if(l == Nospeed)
-			error("speed must be full|low|high");
+			error("speed must be full|low|high|super");
+		if(l != d->speed && (l == Superspeed || d->speed == Superspeed))
+			error("cannot change speed on superspeed device");
 		qlock(ep->ep0);
 		d->speed = l;
 		qunlock(ep->ep0);
@@ -1223,13 +1293,13 @@
 			error("not an intr or iso endpoint");
 		l = strtoul(cb->f[1], nil, 0);
 		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(ep->dev->speed == Highspeed || ep->dev->speed == Superspeed){
+		if(ep->dev->speed == Fullspeed || ep->dev->speed == Lowspeed){
+			if(l < 1 || l > 255)
+				error("pollival not in [1:255]");
+		} else {
 			if(l < 1 || l > 16)
 				error("pollival power not in [1:16]");
 			l = 1 << l-1;
-		} else {
-			if(l < 1 || l > 255)
-				error("pollival not in [1:255]");
 		}
 		qlock(ep);
 		ep->pollival = l;
--- a/sys/src/9/port/usb.h
+++ b/sys/src/9/port/usb.h
@@ -128,7 +128,8 @@
 	int	tbdf;				/* type+busno+devno+funcno */
 	int	ctlrno;				/* controller number */
 	int	nports;				/* number of ports in hub */
-	int	highspeed;
+	int	highspeed;			/* supports highspeed devices */
+	uint	superspeed;			/* bitmap of superspeed ports */
 	Hciimpl;					/* HCI driver  */
 };
 
@@ -164,7 +165,7 @@
 	int	ttype;		/* tranfer type */
 	ulong	load;		/* in µs, for a transfer of maxpkt bytes */
 	void*	aux;		/* for controller specific info */
-	int	rhrepl;		/* fake root hub replies */
+	u64int	rhrepl;		/* fake root hub replies */
 	int	toggle[2];	/* saved toggles (while ep is not in use) */
 	long	pollival;	/* poll interval ([µ]frames; intr/iso) */
 	long	hz;		/* poll frequency (iso) */
@@ -184,7 +185,7 @@
 	int	state;		/* state for the device */
 	int	ishub;		/* hubs can allocate devices */
 	int	isroot;		/* is a root hub */
-	int	speed;		/* Full/Low/High/No -speed */
+	int	speed;		/* Full/Low/High/Super/No -speed */
 	int	hub;		/* device address for the parent hub */
 	int	port;		/* port number in the parent hub */
 	int	addr;		/* device address */
--- a/sys/src/9/port/usbehci.c
+++ b/sys/src/9/port/usbehci.c
@@ -1803,7 +1803,7 @@
 	case Tctl:
 		cio = ep->aux;
 		s = seprintio(s, e, cio, "c");
-		s = seprint(s, e, "\trepl %d ndata %d\n", ep->rhrepl, cio->ndata);
+		s = seprint(s, e, "\trepl %llux ndata %d\n", ep->rhrepl, cio->ndata);
 		break;
 	case Tbulk:
 	case Tintr: