shithub: riscv

Download patch

ref: e0474599ddbeafb7f2734fead8cbba62c466990a
parent: 7d1b9e39f7b1dac4c2ed6e3771585548ccd9e2d4
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Aug 6 21:59:54 EDT 2017

usbxhci: implement recovery from host controller errors

--- a/sys/src/9/pc/usbxhci.c
+++ b/sys/src/9/pc/usbxhci.c
@@ -249,6 +249,7 @@
 
 	void	(*setrptr)(u32int*, u64int);
 
+	Rendez	recover;	
 	void	*active;
 	uintptr	base;
 };
@@ -271,6 +272,7 @@
 
 static char Ebadlen[] = "bad usb request length";
 static char Enotconfig[] = "usb endpoint not configured";
+static char Erecover[] = "xhci controller needs reset";
 
 static void
 setrptr32(u32int *reg, u64int pa)
@@ -407,6 +409,26 @@
 }
 
 static void
+release(Ctlr *ctlr)
+{
+	int i;
+
+	freering(ctlr->cr);
+	for(i=0; i<nelem(ctlr->er); i++){
+		freering(&ctlr->er[i]);
+		free(ctlr->erst[i]);
+		ctlr->erst[i] = nil;
+	}
+	free(ctlr->port), ctlr->port = nil;
+	free(ctlr->slot), ctlr->slot = nil;
+	free(ctlr->dcba), ctlr->dcba = nil;
+	free(ctlr->sba), ctlr->sba = nil;
+	free(ctlr->sbp), ctlr->sbp = nil;
+}
+
+static void recover(void *arg);
+
+static void
 init(Hci *hp)
 {
 	Ctlr *ctlr;
@@ -437,17 +459,7 @@
 
 	if(waserror()){
 		shutdown(hp);
-		freering(ctlr->cr);
-		for(i=0; i<nelem(ctlr->er); i++){
-			freering(&ctlr->er[i]);
-			free(ctlr->erst[i]);
-			ctlr->erst[i] = nil;
-		}
-		free(ctlr->port), ctlr->port = nil;
-		free(ctlr->slot), ctlr->slot = nil;
-		free(ctlr->dcba), ctlr->dcba = nil;
-		free(ctlr->sba), ctlr->sba = nil;
-		free(ctlr->sbp), ctlr->sbp = nil;
+		release(ctlr);
 		nexterror();
 	}
 
@@ -544,9 +556,83 @@
 	ctlr->opr[USBCMD] = RUNSTOP|INTE|HSEE|EWE;
 	for(i=0; (ctlr->opr[USBSTS] & (CNR|HCH)) != 0 && i<100; i++)
 		tsleep(&up->sleep, return0, nil, 10);
+
+	kproc("xhcirecover", recover, hp);
 }
 
+static int
+needrecover(void *arg)
+{
+	Ctlr *ctlr = arg;
+	return 	ctlr->er->stopped || 
+		(ctlr->opr[USBSTS] & (HCH|HCE|HSE)) != 0;
+}
+
 static void
+recover(void *arg)
+{
+	Hci *hp = arg;
+	Ctlr *ctlr = hp->aux;
+
+	while(waserror())
+		;
+	while(!needrecover(ctlr))
+		tsleep(&ctlr->recover, needrecover, ctlr, 1000);
+
+	shutdown(hp);
+
+	/*
+	 * flush all transactions and wait until all devices have
+	 * been detached by usbd.
+	 */
+	for(;;){
+		int i, j, active;
+
+		ilock(ctlr->cr);
+		ctlr->cr->stopped = 1;
+		flushring(ctlr->cr);
+		iunlock(ctlr->cr);
+
+		active = 0;
+		qlock(&ctlr->slotlock);
+		for(i=1; i<=ctlr->nslots; i++){
+			Slot *slot = ctlr->slot[i];
+			if(slot == nil)
+				continue;
+			active++;
+			for(j=0; j < slot->nep; j++){
+				Ring *ring = &slot->epr[j];
+				if(ring->base == nil)
+					continue;
+				ilock(ring);
+				ring->stopped = 1;
+				flushring(ring);
+				iunlock(ring);
+			}
+		}
+		qunlock(&ctlr->slotlock);
+		if(active == 0)
+			break;
+
+		tsleep(&up->sleep, return0, nil, 100);
+	}
+
+	qlock(&ctlr->slotlock);
+	qlock(&ctlr->cmdlock);
+
+	release(ctlr);
+	if(waserror()) {
+		print("xhci recovery failed: %s\n", up->errstr);
+	} else {
+		init(hp);
+		poperror();
+	}
+
+	qunlock(&ctlr->cmdlock);
+	qunlock(&ctlr->slotlock);
+}
+
+static void
 dump(Hci *)
 {
 }
@@ -679,14 +765,11 @@
 	char *err;
 
 	qlock(&ctlr->cmdlock);
-
-	if((ctlr->opr[USBSTS] & (HCH|HCE|HSE)) != 0){
+	if(needrecover(ctlr)){
 		qunlock(&ctlr->cmdlock);
-		return "xhci busted";
+		return Erecover;
 	}
-
 	ctlr->cr->stopped = 0;
-
 	queuetd(ctlr->cr, c, s, p, w);
 	err = waittd(ctlr, w, 5000);
 
@@ -779,11 +862,9 @@
 		case ER_HCE:
 			iprint("xhci: host controller error: %ux %ux %ux %ux\n",
 				td[0], td[1], td[2], td[3]);
-			ilock(ctlr->cr);
-			ctlr->cr->stopped = 1;
-			flushring(ctlr->cr);
-			iunlock(ctlr->cr);
-			break;
+			ctlr->er->stopped = 1;
+			wakeup(&ctlr->recover);
+			return;
 		case ER_PORTSC:
 			break;
 		case ER_BWREQ:
@@ -802,17 +883,18 @@
 freeslot(void *arg)
 {
 	Slot *slot;
-	Ctlr *ctlr;
 
 	if(arg == nil)
 		return;
 	slot = arg;
-	ctlr = slot->ctlr;
 	if(slot->id != 0){
+		Ctlr *ctlr = slot->ctlr;
 		qlock(&ctlr->slotlock);
-		ctlrcmd(ctlr, CR_DISABLESLOT | (slot->id<<24), 0, 0, nil);
-		ctlr->dcba[slot->id] = 0;
-		ctlr->slot[slot->id] = nil;
+		if(ctlr->slot != nil && ctlr->slot[slot->id] == slot){
+			ctlrcmd(ctlr, CR_DISABLESLOT | (slot->id<<24), 0, 0, nil);
+			ctlr->dcba[slot->id] = 0;
+			ctlr->slot[slot->id] = nil;
+		}
 		qunlock(&ctlr->slotlock);
 	}
 	freering(&slot->epr[0]);
@@ -843,6 +925,8 @@
 		freeslot(slot);
 		nexterror();
 	}
+	if(ctlr->slot == nil)
+		error(Erecover);
 	slot->ibase = mallocalign(32*33 << ctlr->csz, 64, 0, ctlr->pagesize);
 	slot->obase = mallocalign(32*32 << ctlr->csz, 64, 0, ctlr->pagesize);
 	if(slot->ibase == nil || slot->obase == nil)
@@ -1065,7 +1149,8 @@
 
 	if(ep->dev->isroot)
 		return;
-
+	if(needrecover(ctlr))
+		error(Erecover);
 	io = malloc(sizeof(Epio)*2);
 	if(io == nil)
 		error(Enomem);
@@ -1171,6 +1256,8 @@
 	}
 	µ = io->period;
 	ctlr = ep->hp->aux;
+	if(needrecover(ctlr))
+		error(Erecover);
 	for(i = io->frame;; i++){
 		for(;;){
 			m = (int)(io->ring->wp - io->ring->rp);
@@ -1439,10 +1526,13 @@
 portstatus(Hci *hp, int port)
 {
 	Ctlr *ctlr = hp->aux;
-	Port *pp = &ctlr->port[port-1];
-	u32int psc, ps = 0;
+	u32int psc, ps;
 
-	psc = pp->reg[PORTSC];
+	if(ctlr->port == nil || needrecover(ctlr))
+		return 0;
+
+	ps = 0;
+	psc = ctlr->port[port-1].reg[PORTSC];
 	if(psc & CCS)	ps |= HPpresent;
 	if(psc & PED)	ps |= HPenable;
 	if(psc & OCA)	ps |= HPovercurrent;
@@ -1488,13 +1578,14 @@
 portreset(Hci *hp, int port, int on)
 {
 	Ctlr *ctlr = hp->aux;
-	Port *pp = &ctlr->port[port-1];
 
+	if(ctlr->port == nil || needrecover(ctlr))
+		return 0;
+
 	if(on){
-		pp->reg[PORTSC] |= PR;
+		ctlr->port[port-1].reg[PORTSC] |= PR;
 		tsleep(&up->sleep, return0, nil, 200);
 	}
-
 	return 0;
 }