mirror of
git://git.9front.org/plan9front/plan9front
synced 2025-01-12 11:10:06 +00:00
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.
This commit is contained in:
parent
ff65b74935
commit
2f12ad38ca
7 changed files with 227 additions and 259 deletions
|
@ -27,9 +27,6 @@
|
|||
enum
|
||||
{
|
||||
USBREGS = VIRTIO + 0x980000,
|
||||
Enabledelay = 50,
|
||||
Resetdelay = 10,
|
||||
ResetdelayHS = 50,
|
||||
|
||||
Read = 0,
|
||||
Write = 1,
|
||||
|
@ -936,61 +933,52 @@ seprintep(char *s, char*, Ep*)
|
|||
{
|
||||
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 void
|
||||
portreset(Hci *hp, int, int on)
|
||||
{
|
||||
Ctlr *ctlr = hp->aux;
|
||||
Dwcregs *r = ctlr->regs;
|
||||
|
||||
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
|
||||
portreset(Hci *hp, int port, int on)
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
static int
|
||||
portstatus(Hci *hp, int port)
|
||||
{
|
||||
Ctlr *ctlr;
|
||||
Dwcregs *r;
|
||||
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 @@ reset(Hci *hp)
|
|||
hp->seprintep = seprintep;
|
||||
hp->portenable = portenable;
|
||||
hp->portreset = portreset;
|
||||
hp->portpower = portpower;
|
||||
hp->portstatus = portstatus;
|
||||
hp->shutdown = shutdown;
|
||||
hp->debug = setdebug;
|
||||
|
|
|
@ -43,7 +43,6 @@ enum
|
|||
|
||||
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 Edpool
|
|||
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 @@ seprintep(char* s, char* e, Ep *ep)
|
|||
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 @@ epclose(Ep *ep)
|
|||
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;
|
||||
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] = Spr;
|
||||
else if(ctlr->ohci->rhportsts[port - 1] & Prsc)
|
||||
ctlr->ohci->rhportsts[port - 1] = Prsc;
|
||||
iunlock(ctlr);
|
||||
poperror();
|
||||
qunlock(&ctlr->resetl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
static void
|
||||
portpower(Hci *hp, int port, int on)
|
||||
{
|
||||
Ctlr *ctlr;
|
||||
|
||||
ctlr = hp->aux;
|
||||
ilock(ctlr);
|
||||
if(on)
|
||||
ctlr->ohci->rhportsts[port - 1] = Spp;
|
||||
else
|
||||
ctlr->ohci->rhportsts[port - 1] = Cpp;
|
||||
iunlock(ctlr);
|
||||
}
|
||||
|
||||
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 @@ reset(Hci *hp)
|
|||
hp->seprintep = seprintep;
|
||||
hp->portenable = portenable;
|
||||
hp->portreset = portreset;
|
||||
hp->portpower = portpower;
|
||||
hp->portstatus = portstatus;
|
||||
hp->shutdown = shutdown;
|
||||
hp->debug = usbdebug;
|
||||
|
|
|
@ -30,7 +30,6 @@ typedef struct Tdpool Tdpool;
|
|||
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,79 +2022,52 @@ seprintep(char *s, char *e, Ep *ep)
|
|||
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)
|
||||
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 @@ portstatus(Hci *hp, int port)
|
|||
}
|
||||
iunlock(ctlr);
|
||||
qunlock(&ctlr->portlck);
|
||||
poperror();
|
||||
|
||||
/*
|
||||
* We must return status bits as a
|
||||
|
|
|
@ -93,6 +93,8 @@ enum
|
|||
/* Hub feature selectors */
|
||||
Rportenable = 1,
|
||||
Rportreset = 4,
|
||||
Rportpower = 8,
|
||||
Rbhportreset = 28,
|
||||
|
||||
};
|
||||
|
||||
|
@ -505,6 +507,9 @@ newdev(Hci *hp, Ep *hub, int port, int speed)
|
|||
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 @@ newdev(Hci *hp, Ep *hub, int port, int speed)
|
|||
}
|
||||
}
|
||||
d->rootport = rootport(hub, port);
|
||||
if(d->rootport == 0)
|
||||
if(d->rootport <= 0)
|
||||
error(Ebadport);
|
||||
} else {
|
||||
d->hub = nil;
|
||||
|
@ -1014,7 +1019,8 @@ usbopen(Chan *c, int omode)
|
|||
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 @@ static long
|
|||
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 @@ rhubwrite(Ep *ep, void *a, long n)
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -117,9 +117,11 @@ struct Hciimpl
|
|||
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 @@ struct Ep
|
|||
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 @@ struct Udev
|
|||
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*));
|
||||
|
|
|
@ -51,7 +51,6 @@ enum
|
|||
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 @@ interrupt(Ureg*, void* a)
|
|||
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 @@ portlend(Ctlr *ctlr, int port, char *ss)
|
|||
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;
|
||||
|
||||
if(on == 0)
|
||||
return 0;
|
||||
Eopio *opio;
|
||||
int s;
|
||||
|
||||
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);
|
||||
|
||||
*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 */
|
||||
|
||||
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");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
portreset(Hci *hp, int port, int on)
|
||||
{
|
||||
Eopio *opio;
|
||||
Ctlr *ctlr;
|
||||
|
||||
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 @@ portstatus(Hci *hp, int port)
|
|||
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 @@ portstatus(Hci *hp, int port)
|
|||
}
|
||||
iunlock(ctlr);
|
||||
qunlock(&ctlr->portlck);
|
||||
poperror();
|
||||
|
||||
/*
|
||||
* We must return status bits as a
|
||||
|
@ -1822,7 +1763,6 @@ seprintep(char *s, char *e, Ep *ep)
|
|||
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:
|
||||
|
|
|
@ -1785,26 +1785,61 @@ portstatus(Hci *hp, int port)
|
|||
|
||||
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(!on || ctlr->port == nil || needrecover(ctlr))
|
||||
return;
|
||||
portsc = &ctlr->port[port-1].reg[PORTSC];
|
||||
*portsc = (*portsc & ~(RW1CS|RW1S)) | PR;
|
||||
}
|
||||
|
||||
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 0;
|
||||
|
||||
if(on){
|
||||
ctlr->port[port-1].reg[PORTSC] |= PR;
|
||||
tsleep(&up->sleep, return0, nil, 200);
|
||||
}
|
||||
return 0;
|
||||
return;
|
||||
portsc = &ctlr->port[port-1].reg[PORTSC];
|
||||
if(on)
|
||||
*portsc = (*portsc & ~(RW1CS|RW1S|PP)) | PP;
|
||||
else
|
||||
*portsc = (*portsc & ~(RW1CS|RW1S|PP));
|
||||
}
|
||||
|
||||
static u64int
|
||||
|
@ -1851,6 +1886,8 @@ xhcilinkage(Hci *hp, Xhci *ctlr)
|
|||
hp->devclose = devclose;
|
||||
hp->portenable = portenable;
|
||||
hp->portreset = portreset;
|
||||
hp->bhportreset = bhportreset;
|
||||
hp->portpower = portpower;
|
||||
hp->portstatus = portstatus;
|
||||
|
||||
hp->debug = setdebug;
|
||||
|
|
Loading…
Reference in a new issue