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;
--
⑨