shithub: front

Download patch

ref: 2f12ad38ca1ed1fae0c56c04909ce9c0e76a2070
parent: ff65b74935abd849fe8d83839c86abbd01fb44c6
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Fri Dec 6 11:07:44 EST 2024

devusb: handle root-port reset delays outside of hci driver, rootport power control

We want to avoid long delays with interrupts disabled,
so handle the delay from devusb/usbd.

Do not expect SetFeatrue/PortEnable request, this is
invalid by the usb standard. But some HCI's require
setting enable bit in port status/ctrl register of
rootports, so handle this internally.

xhci, dwc and ohci have a power-power control bit,
so implement standard Set/ClearFeature/PortPower in
roothub, which allows us to control port on some
rootports.

--- a/sys/src/9/bcm/usbdwc.c
+++ b/sys/src/9/bcm/usbdwc.c
@@ -27,9 +27,6 @@
 enum
 {
 	USBREGS		= VIRTIO + 0x980000,
-	Enabledelay	= 50,
-	Resetdelay	= 10,
-	ResetdelayHS	= 50,
 
 	Read		= 0,
 	Write		= 1,
@@ -936,61 +933,52 @@
 {
 	return s;
 }
+
+enum {
+	RW1C = Prtconndet | Prtena | Prtenchng | Prtovrcurrchng,
+};
 	
-static int
-portenable(Hci *hp, int port, int on)
+static void
+portenable(Hci *hp, int, int on)
 {
-	Ctlr *ctlr;
-	Dwcregs *r;
+	Ctlr *ctlr = hp->aux;
+	Dwcregs *r = ctlr->regs;
 
-	assert(port == 1);
-	ctlr = hp->aux;
-	r = ctlr->regs;
-	dprint("usbdwc enable=%d; sts %#x\n", on, r->hport0);
 	if(!on)
-		r->hport0 = Prtpwr | Prtena;
-	tsleep(&up->sleep, return0, 0, Enabledelay);
-	dprint("usbdwc enable=%d; sts %#x\n", on, r->hport0);
-	return 0;
+		r->hport0 = (r->hport0 & ~RW1C) | Prtena;
 }
 
-static int
-portreset(Hci *hp, int port, int on)
+static void
+portreset(Hci *hp, int, int on)
 {
-	Ctlr *ctlr;
-	Dwcregs *r;
-	int b, s;
+	Ctlr *ctlr = hp->aux;
+	Dwcregs *r = ctlr->regs;
 
-	assert(port == 1);
-	ctlr = hp->aux;
-	r = ctlr->regs;
-	dprint("usbdwc reset=%d; sts %#x\n", on, r->hport0);
-	if(!on)
-		return 0;
-	r->hport0 = Prtpwr | Prtrst;
-	tsleep(&up->sleep, return0, 0, ResetdelayHS);
-	r->hport0 = Prtpwr;
-	tsleep(&up->sleep, return0, 0, Enabledelay);
-	s = r->hport0;
-	b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
-	if(b != 0)
-		r->hport0 = Prtpwr | b;
-	dprint("usbdwc reset=%d; sts %#x\n", on, s);
-	if((s & Prtena) == 0)
-		print("usbdwc: host port not enabled after reset");
-	return 0;
+	if(on)
+		r->hport0 = (r->hport0 & ~RW1C) | Prtrst;
+	else
+		r->hport0 = (r->hport0 & ~RW1C) & ~Prtrst;
 }
 
+static void
+portpower(Hci *hp, int, int on)
+{
+	Ctlr *ctlr = hp->aux;
+	Dwcregs *r = ctlr->regs;
+
+	if(on)
+		r->hport0 = (r->hport0 & ~RW1C) | Prtpwr;
+	else
+		r->hport0 = (r->hport0 & ~RW1C) & ~Prtpwr;
+}
+
 static int
-portstatus(Hci *hp, int port)
+portstatus(Hci *hp, int)
 {
-	Ctlr *ctlr;
-	Dwcregs *r;
+	Ctlr *ctlr = hp->aux;
+	Dwcregs *r = ctlr->regs;
 	int b, s;
 
-	assert(port == 1);
-	ctlr = hp->aux;
-	r = ctlr->regs;
 	s = r->hport0;
 	b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
 	if(b != 0)
@@ -1067,6 +1055,7 @@
 	hp->seprintep = seprintep;
 	hp->portenable = portenable;
 	hp->portreset = portreset;
+	hp->portpower = portpower;
 	hp->portstatus = portstatus;
 	hp->shutdown = shutdown;
 	hp->debug = setdebug;
--- a/sys/src/9/pc/usbohci.c
+++ b/sys/src/9/pc/usbohci.c
@@ -43,7 +43,6 @@
 
 	Abortdelay	= 1,		/* delay after cancelling Tds (ms) */
 	Tdatomic		= 8,		/* max nb. of Tds per bulk I/O op. */
-	Enabledelay	= 100,		/* waiting for a port to enable */
 
 
 	/* Queue states (software) */
@@ -339,7 +338,6 @@
 struct Ctlr
 {
 	Lock;			/* for ilock; lists and basic ctlr I/O */
-	QLock	resetl;		/* lock controller during USB reset */
 	int	active;
 	Ctlr*	next;
 	int	nports;
@@ -950,7 +948,6 @@
 	case Tctl:
 		cio = ep->aux;
 		s = seprintio(s, e, cio, "c");
-		s = seprint(s, e, "\trepl %llux ndata %d\n", ep->rhrepl, cio->ndata);
 		break;
 	case Tbulk:
 	case Tintr:
@@ -2211,63 +2208,46 @@
 	ep->aux = nil;
 }
 
-static int
+static void
 portreset(Hci *hp, int port, int on)
 {
 	Ctlr *ctlr;
-	Ohci *ohci;
 
-	if(on == 0)
-		return 0;
+	ctlr = hp->aux;
+	ilock(ctlr);
+	if(on)
+		ctlr->ohci->rhportsts[port - 1] = Spr;
+	else if(ctlr->ohci->rhportsts[port - 1] & Prsc)
+		ctlr->ohci->rhportsts[port - 1] = Prsc;
+	iunlock(ctlr);
+}
 
+static void
+portpower(Hci *hp, int port, int on)
+{
+	Ctlr *ctlr;
+
 	ctlr = hp->aux;
-	eqlock(&ctlr->resetl);
-	if(waserror()){
-		qunlock(&ctlr->resetl);
-		nexterror();
-	}
 	ilock(ctlr);
-	ohci = ctlr->ohci;
-	ohci->rhportsts[port - 1] = Spp;
-	if((ohci->rhportsts[port - 1] & Ccs) == 0){
-		iunlock(ctlr);
-		error("port not connected");
-	}
-	ohci->rhportsts[port - 1] = Spr;
-	while((ohci->rhportsts[port - 1] & Prsc) == 0){
-		iunlock(ctlr);
-		dprint("ohci: portreset, wait for reset complete\n");
-		ilock(ctlr);
-	}
-	ohci->rhportsts[port - 1] = Prsc;
+	if(on)
+		ctlr->ohci->rhportsts[port - 1] = Spp;
+	else
+		ctlr->ohci->rhportsts[port - 1] = Cpp;
 	iunlock(ctlr);
-	poperror();
-	qunlock(&ctlr->resetl);
-	return 0;
 }
 
-static int
+static void
 portenable(Hci *hp, int port, int on)
 {
 	Ctlr *ctlr;
 
 	ctlr = hp->aux;
-	dprint("ohci: %#p port %d enable=%d\n", ctlr->ohci, port, on);
-	eqlock(&ctlr->resetl);
-	if(waserror()){
-		qunlock(&ctlr->resetl);
-		nexterror();
-	}
 	ilock(ctlr);
 	if(on)
-		ctlr->ohci->rhportsts[port - 1] = Spe | Spp;
+		ctlr->ohci->rhportsts[port - 1] = Spe;
 	else
 		ctlr->ohci->rhportsts[port - 1] = Cpe;
 	iunlock(ctlr);
-	tsleep(&up->sleep, return0, 0, Enabledelay);
-	poperror();
-	qunlock(&ctlr->resetl);
-	return 0;
 }
 
 static int
@@ -2626,6 +2606,7 @@
 	hp->seprintep = seprintep;
 	hp->portenable = portenable;
 	hp->portreset = portreset;
+	hp->portpower = portpower;
 	hp->portstatus = portstatus;
 	hp->shutdown = shutdown;
 	hp->debug = usbdebug;
--- a/sys/src/9/pc/usbuhci.c
+++ b/sys/src/9/pc/usbuhci.c
@@ -30,7 +30,6 @@
 enum
 {
 	Resetdelay	= 100,		/* delay after a controller reset (ms) */
-	Enabledelay	= 100,		/* waiting for a port to enable */
 	Abortdelay	= 10,		/* delay after cancelling Tds (ms) */
 	Incr		= 64,		/* for Td and Qh pools */
 
@@ -2023,21 +2022,15 @@
 	return s;
 }
 
-static int
+static void
 portenable(Hci *hp, int port, int on)
 {
-	int s;
-	int ioport;
 	Ctlr *ctlr;
+	int ioport, s;
 
 	ctlr = hp->aux;
-	dprint("uhci: %#x port %d enable=%d\n", ctlr->port, port, on);
 	ioport = PORT(port-1);
 	eqlock(&ctlr->portlck);
-	if(waserror()){
-		qunlock(&ctlr->portlck);
-		nexterror();
-	}
 	ilock(ctlr);
 	s = INS(ioport);
 	if(on)
@@ -2044,58 +2037,37 @@
 		OUTS(ioport, s | PSenable);
 	else
 		OUTS(ioport, s & ~PSenable);
-	microdelay(64);
 	iunlock(ctlr);
-	tsleep(&up->sleep, return0, 0, Enabledelay);
-	dprint("uhci %#ux port %d enable=%d: sts %#x\n",
-		ctlr->port, port, on, INS(ioport));
 	qunlock(&ctlr->portlck);
-	poperror();
-	return 0;
 }
 
-static int
+static void
 portreset(Hci *hp, int port, int on)
 {
-	int i, p;
 	Ctlr *ctlr;
+	int ioport;
 
-	if(on == 0)
-		return 0;
 	ctlr = hp->aux;
-	dprint("uhci: %#ux port %d reset\n", ctlr->port, port);
-	p = PORT(port-1);
+	ioport = PORT(port-1);
+	eqlock(&ctlr->portlck);
 	ilock(ctlr);
-	OUTS(p, PSreset);
-	delay(50);
-	OUTS(p, INS(p) & ~PSreset);
-	OUTS(p, INS(p) | PSenable);
-	microdelay(64);
-	for(i=0; i<1000 && (INS(p) & PSenable) == 0; i++)
-		;
-	OUTS(p, (INS(p) & ~PSreset)|PSenable);
+	if(on)
+		OUTS(ioport, PSreset);
+	else
+		OUTS(ioport, INS(ioport) & ~PSreset);
 	iunlock(ctlr);
-	dprint("uhci %#ux after port %d reset: sts %#x\n",
-		ctlr->port, port, INS(p));
-	return 0;
+	qunlock(&ctlr->portlck);
 }
 
 static int
 portstatus(Hci *hp, int port)
 {
-	int s;
-	int r;
-	int ioport;
 	Ctlr *ctlr;
+	int ioport, s, r;
 
 	ctlr = hp->aux;
 	ioport = PORT(port-1);
 	eqlock(&ctlr->portlck);
-	if(waserror()){
-		iunlock(ctlr);
-		qunlock(&ctlr->portlck);
-		nexterror();
-	}
 	ilock(ctlr);
 	s = INS(ioport);
 	if(s & (PSstatuschg | PSchange)){
@@ -2104,7 +2076,6 @@
 	}
 	iunlock(ctlr);
 	qunlock(&ctlr->portlck);
-	poperror();
 
 	/*
 	 * We must return status bits as a
--- a/sys/src/9/port/devusb.c
+++ b/sys/src/9/port/devusb.c
@@ -93,6 +93,8 @@
 	/* Hub feature selectors */
 	Rportenable	= 1,
 	Rportreset	= 4,
+	Rportpower	= 8,
+	Rbhportreset	= 28,
 
 };
 
@@ -505,6 +507,9 @@
 	d->ttport = d->ttt = d->mtt = 0;
 	d->tthub = nil;
 
+	d->rhrepl = -1;
+	d->rhresetport = 0;
+
 	if(hub != nil){
 		d->hub = hub->dev;
 		d->depth = d->hub->depth+1;
@@ -522,7 +527,7 @@
 			}
 		}
 		d->rootport = rootport(hub, port);
-		if(d->rootport == 0)
+		if(d->rootport <= 0)
 			error(Ebadport);
 	} else {
 		d->hub = nil;
@@ -1014,7 +1019,8 @@
 	if(ep->dev->state == Ddetach)
 		error(Edetach);
 	ep->clrhalt = 0;
-	ep->rhrepl = -1;
+	if(ep->ttype == Tctl)
+		ep->dev->rhrepl = -1;
 	if(ep->ttype == Tiso)
 		isotiming(ep);
 	if(ep->load == 0 && ep->dev->speed != Superspeed)
@@ -1127,20 +1133,23 @@
 rhubread(Ep *ep, void *a, long n)
 {
 	uchar b[8];
+	Udev *dev;
 
-	if(ep->dev->depth >= 0 || ep->nb != 0 || n < 2 || ep->rhrepl == -1)
+	dev = ep->dev;
+
+	if(dev->depth >= 0 || ep->nb != 0 || n < 2 || dev->rhrepl == -1)
 		return -1;
 
-	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;
+	b[0] = dev->rhrepl;
+	b[1] = dev->rhrepl>>8;
+	b[2] = dev->rhrepl>>16;
+	b[3] = dev->rhrepl>>24;
+	b[4] = dev->rhrepl>>32;
+	b[5] = dev->rhrepl>>40;
+	b[6] = dev->rhrepl>>48;
+	b[7] = dev->rhrepl>>56;
 
-	ep->rhrepl = -1;
+	dev->rhrepl = -1;
 
 	if(n > sizeof(b))
 		n = sizeof(b);
@@ -1157,33 +1166,70 @@
 	int feature;
 	int port;
 	Hci *hp;
+	Udev *dev;
 
-	if(ep->dev->depth >= 0 || ep->nb != 0)
+	hp = ep->hp;
+	dev = ep->dev;
+
+	if(dev->depth >= 0 || ep->nb != 0)
 		return -1;
 	if(n != Rsetuplen)
 		error("root hub is a toy hub");
-	ep->rhrepl = -1;
 	s = a;
 	if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
 		error("root hub is a toy hub");
-	hp = ep->hp;
+
+	/* terminate previous port reset */
+	port = dev->rhresetport;
+	if(port > 0){
+		dev->rhresetport = 0;
+
+		/* some controllers have to clear reset and set enable manually */
+		if(hp->portreset != nil){
+			(*hp->portreset)(hp, port, 0);
+			tsleep(&up->sleep, return0, nil, 50);
+		}
+		if(hp->portenable != nil){
+			(*hp->portenable)(hp, port, 1);
+			tsleep(&up->sleep, return0, nil, 50);
+		}
+	}
+
 	cmd = s[Rreq];
 	feature = GET2(s+Rvalue);
 	port = rootport(ep, GET2(s+Rindex));
-	if(port == 0)
+	if(port <= 0)
 		error(Ebadport);
+
+	dev->rhrepl = 0;
 	switch(feature){
+	case Rportpower:
+		if(hp->portpower == nil)
+			break;
+		(*hp->portpower)(hp, port, cmd == Rsetfeature);
+		break;
 	case Rportenable:
-		ep->rhrepl = (*hp->portenable)(hp, port, cmd == Rsetfeature);
+		if(cmd != Rclearfeature || hp->portenable == nil)
+			break;
+		(*hp->portenable)(hp, port, 0);
 		break;
+	case Rbhportreset:
+		if(cmd != Rsetfeature || hp->bhportreset == nil)
+			break;
+		(*hp->bhportreset)(hp, port, 1);
+		break;
 	case Rportreset:
-		ep->rhrepl = (*hp->portreset)(hp, port, cmd == Rsetfeature);
+		if(cmd != Rsetfeature || hp->portreset == nil)
+			break;
+		(*hp->portreset)(hp, port, 1);
+		/* port reset in progress */
+		dev->rhresetport = port;
 		break;
 	case Rgetstatus:
-		ep->rhrepl = (*hp->portstatus)(hp, port);
+		if(hp->portstatus == nil)
+			break;
+		dev->rhrepl = (*hp->portstatus)(hp, port);
 		break;
-	default:
-		ep->rhrepl = 0;
 	}
 	return n;
 }
--- a/sys/src/9/port/usb.h
+++ b/sys/src/9/port/usb.h
@@ -117,9 +117,11 @@
 	long	(*epwrite)(Ep*,void*,long);	/* receive data for ep */
 	char*	(*seprintep)(char*,char*,Ep*);	/* debug */
 	void	(*devclose)(Udev*);		/* release the device */
-	int	(*portenable)(Hci*, int, int);	/* enable/disable port */
-	int	(*portreset)(Hci*, int, int);	/* set/clear port reset */
-	int	(*portstatus)(Hci*, int);	/* get port status */
+	void	(*portenable)(Hci*, int, int);	/* enable/disable root port */
+	void	(*portreset)(Hci*, int, int);	/* set/clear root port reset */
+	void	(*bhportreset)(Hci*, int, int);	/* set/clear warm root port reset (usb3) */
+	void	(*portpower)(Hci*, int, int);	/* set/clear root port power state */
+	int	(*portstatus)(Hci*, int);	/* get root port status */
 	void	(*shutdown)(Hci*);		/* shutdown for reboot */
 	void	(*debug)(Hci*, int);		/* set/clear debug flag */
 };
@@ -167,7 +169,6 @@
 	int	ttype;		/* tranfer type */
 	ulong	load;		/* in µs, for a transfer of maxpkt bytes */
 	void*	aux;		/* for controller specific info */
-	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) */
@@ -204,6 +205,9 @@
 	void	*aux;
 
 	Ep	*eps[Ndeveps];	/* end points for this device (cached) */
+
+	u64int	rhrepl;		/* fake root hub replies */
+	int	rhresetport;	/* root port being reset */
 };
 
 void	addhcitype(char *type, int (*reset)(Hci*));
--- a/sys/src/9/port/usbehci.c
+++ b/sys/src/9/port/usbehci.c
@@ -51,7 +51,6 @@
 	Qclose,
 	Qfree,
 
-	Enabledelay	= 100,		/* waiting for a port to enable */
 	Abortdelay	= 10,		/* delay after cancelling Tds (ms) */
 
 	Incr		= 64,		/* for pools of Tds, Qhs, etc. */
@@ -1628,41 +1627,6 @@
 	ehciintr(a);
 }
 
-static int
-portenable(Hci *hp, int port, int on)
-{
-	Ctlr *ctlr;
-	Eopio *opio;
-	int s;
-
-	ctlr = hp->aux;
-	opio = ctlr->opio;
-	s = opio->portsc[port-1];
-	eqlock(&ctlr->portlck);
-	if(waserror()){
-		qunlock(&ctlr->portlck);
-		nexterror();
-	}
-	dprint("ehci %#p port %d enable=%d; sts %#x\n",
-		ctlr->capio, port, on, s);
-	ilock(ctlr);
-	if(s & (Psstatuschg | Pschange))
-		opio->portsc[port-1] = s;
-	if(on)
-		opio->portsc[port-1] |= Psenable;
-	else
-		opio->portsc[port-1] &= ~Psenable;
-	coherence();
-	microdelay(64);
-	iunlock(ctlr);
-	tsleep(&up->sleep, return0, 0, Enabledelay);
-	dprint("ehci %#p port %d enable=%d: sts %#lux\n",
-		ctlr->capio, port, on, opio->portsc[port-1]);
-	qunlock(&ctlr->portlck);
-	poperror();
-	return 0;
-}
-
 /*
  * If we detect during status that the port is low-speed or
  * during reset that it's full-speed, the device is not for
@@ -1678,67 +1642,53 @@
 	ulong s;
 
 	opio = ctlr->opio;
-
 	dprint("ehci %#p port %d: %s speed device: no longer owned\n",
 		ctlr->capio, port, ss);
 	s = opio->portsc[port-1] & ~(Pschange|Psstatuschg);
 	opio->portsc[port-1] = s | Psowner;
-	coherence();
 }
 
-static int
-portreset(Hci *hp, int port, int on)
+static void
+portenable(Hci *hp, int port, int on)
 {
-	ulong *portscp;
-	Eopio *opio;
 	Ctlr *ctlr;
-	int i;
+	Eopio *opio;
+	int s;
 
-	if(on == 0)
-		return 0;
-
 	ctlr = hp->aux;
 	opio = ctlr->opio;
 	eqlock(&ctlr->portlck);
-	if(waserror()){
-		iunlock(ctlr);
-		qunlock(&ctlr->portlck);
-		nexterror();
-	}
-	portscp = &opio->portsc[port-1];
-	dprint("ehci %#p port %d reset; sts %#lux\n", ctlr->capio, port, *portscp);
 	ilock(ctlr);
-	/* 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);
+	s = opio->portsc[port-1];
+	if(s & (Psstatuschg | Pschange))
+		opio->portsc[port-1] = s;
+	if(on){
+		/* not enabled after reset? */
+		if((s & Psenable) == 0)
+			portlend(ctlr, port, "full");
+	} else {
+		opio->portsc[port-1] &= ~Psenable;
+	}
+	iunlock(ctlr);
+	qunlock(&ctlr->portlck);
+}
 
-	*portscp = (*portscp & ~Psenable) | Psreset;	/* initiate reset */
-	/*
-	 * usb 2 spec: reset must finish within 20 ms.
-	 * linux says spec says it can take 50 ms. for hubs.
-	 */
-	delay(50);
-	*portscp &= ~Psreset;	/* terminate reset */
+static void
+portreset(Hci *hp, int port, int on)
+{
+	Eopio *opio;
+	Ctlr *ctlr;
 
-	delay(10);
-	for(i = 0; *portscp & Psreset && i < 10; i++)
-		delay(10);
-
-	if (*portscp & Psreset)
-		iprint("ehci %#p: port %d didn't reset; sts %#lux\n",
-			ctlr->capio, port, *portscp);
-
-	delay(10);			/* ehci spec: enable within 2 ms. */
-	if((*portscp & Psenable) == 0)
-		portlend(ctlr, port, "full");
-
+	ctlr = hp->aux;
+	opio = ctlr->opio;
+	eqlock(&ctlr->portlck);
+	ilock(ctlr);
+	if(on)
+		opio->portsc[port-1] = (opio->portsc[port-1] & ~Psenable) | Psreset;	/* initiate reset */
+	else
+		opio->portsc[port-1] &= ~Psreset;	/* terminate reset */
 	iunlock(ctlr);
-	dprint("ehci %#p after port %d reset; sts %#lux\n",
-		ctlr->capio, port, *portscp);
 	qunlock(&ctlr->portlck);
-	poperror();
-	return 0;
 }
 
 static int
@@ -1751,18 +1701,10 @@
 	ctlr = hp->aux;
 	opio = ctlr->opio;
 	eqlock(&ctlr->portlck);
-	if(waserror()){
-		iunlock(ctlr);
-		qunlock(&ctlr->portlck);
-		nexterror();
-	}
 	ilock(ctlr);
 	s = opio->portsc[port-1];
-	if(s & (Psstatuschg | Pschange)){
+	if(s & (Psstatuschg | Pschange))
 		opio->portsc[port-1] = s;
-		coherence();
-		ddprint("ehci %#p port %d status %#x\n", ctlr->capio, port, s);
-	}
 	/*
 	 * If the port is a low speed port we yield ownership now
 	 * to the [uo]hci companion controller and pretend it's not here.
@@ -1773,7 +1715,6 @@
 	}
 	iunlock(ctlr);
 	qunlock(&ctlr->portlck);
-	poperror();
 
 	/*
 	 * We must return status bits as a
@@ -1822,7 +1763,6 @@
 	case Tctl:
 		cio = ep->aux;
 		s = seprintio(s, e, cio, "c");
-		s = seprint(s, e, "\trepl %llux ndata %d\n", ep->rhrepl, cio->ndata);
 		break;
 	case Tbulk:
 	case Tintr:
--- a/sys/src/9/port/usbxhci.c
+++ b/sys/src/9/port/usbxhci.c
@@ -1785,28 +1785,63 @@
 
 	return ps;
 }
+
+enum {
+	RW1S = PR | WPR,
+	RW1CS = PED | CSC | PEC | WRC | OCC | PRC | PLC | CEC,
+};
 	
-static int
-portenable(Hci*, int, int)
+static void
+portenable(Hci *hp, int port, int on)
 {
-	return 0;
+	Ctlr *ctlr = hp->aux;
+	u32int *portsc;
+
+	if(on || ctlr->port == nil || needrecover(ctlr))
+		return;
+	portsc = &ctlr->port[port-1].reg[PORTSC];
+	*portsc = (*portsc & ~(RW1CS|RW1S)) | PED;
 }
 
-static int
+static void
 portreset(Hci *hp, int port, int on)
 {
 	Ctlr *ctlr = hp->aux;
+	u32int *portsc;
 
-	if(ctlr->port == nil || needrecover(ctlr))
-		return 0;
+	if(!on || ctlr->port == nil || needrecover(ctlr))
+		return;
+	portsc = &ctlr->port[port-1].reg[PORTSC];
+	*portsc = (*portsc & ~(RW1CS|RW1S)) | PR;
+}
 
-	if(on){
-		ctlr->port[port-1].reg[PORTSC] |= PR;
-		tsleep(&up->sleep, return0, nil, 200);
-	}
-	return 0;
+static void
+bhportreset(Hci *hp, int port, int on)
+{
+	Ctlr *ctlr = hp->aux;
+	u32int *portsc;
+
+	if(!on || ctlr->port == nil || needrecover(ctlr))
+		return;
+	portsc = &ctlr->port[port-1].reg[PORTSC];
+	*portsc = (*portsc & ~(RW1CS|RW1S)) | WPR;
 }
 
+static void
+portpower(Hci *hp, int port, int on)
+{
+	Ctlr *ctlr = hp->aux;
+	u32int *portsc;
+
+	if(ctlr->port == nil || needrecover(ctlr))
+		return;
+	portsc = &ctlr->port[port-1].reg[PORTSC];
+	if(on)
+		*portsc = (*portsc & ~(RW1CS|RW1S|PP)) | PP;
+	else
+		*portsc = (*portsc & ~(RW1CS|RW1S|PP));
+}
+
 static u64int
 physaddr(void *va)
 {
@@ -1851,6 +1886,8 @@
 	hp->devclose = devclose;
 	hp->portenable = portenable;
 	hp->portreset = portreset;
+	hp->bhportreset = bhportreset;
+	hp->portpower = portpower;
 	hp->portstatus = portstatus;
 
 	hp->debug = setdebug;
--