ref: c74538a67e57ab9e37826104210d1c1f64711a04
parent: aaf6d7c5586d442c0bc10ed50558b984faeb51aa
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Fri Jul 28 21:02:23 EDT 2017
usbxhci: implement controller handoff, ignore usb3.0 ports, honor pollival for isochronous endpoints
--- a/sys/src/9/pc/usbxhci.c
+++ b/sys/src/9/pc/usbxhci.c
@@ -147,6 +147,7 @@
typedef struct Ring Ring;
typedef struct Slot Slot;
typedef struct Epio Epio;
+typedef struct Port Port;
struct Wait
{
@@ -193,6 +194,14 @@
Ring epr[32];
};
+struct Port
+{
+ char spec[4];
+ int proto;
+
+ u32int *reg;
+};
+
struct Ctlr
{
Pcidev *pcidev;
@@ -200,7 +209,6 @@
u32int *mmio;
u32int *opr; /* operational registers */
- u32int *prt; /* port register set */
u32int *rts; /* runtime registers */
u32int *dba; /* doorbell array */
@@ -213,10 +221,11 @@
Ring er[1]; /* event ring segment */
Ring cr[1]; /* command ring segment */
- u32int mfwrap;
+ u32int µframe;
QLock slotlock;
Slot **slot; /* slots by slot id */
+ Port *port;
u32int hccparams;
@@ -239,10 +248,12 @@
Block *b;
/* iso */
- int nleft;
u32int frame;
+ u32int period;
u32int incr;
u32int tdsz;
+
+ int nleft;
};
static char Ebadlen[] = "bad usb request length";
@@ -262,15 +273,14 @@
}
static u32int
-mfindex(Ctlr *ctlr)
+µframe(Ctlr *ctlr)
{
- u32int lo, hi;
-
+ u32int µ;
do {
- hi = ctlr->mfwrap;
- lo = ctlr->rts[MFINDEX];
- } while(hi != ctlr->mfwrap);
- return (lo & (1<<14)-1) | hi<<14;
+ µ = (ctlr->rts[MFINDEX] & (1<<14)-1) |
+ (ctlr->µframe & ~((1<<14)-1));
+ } while((int)(µ - ctlr->µframe) < 0);
+ return µ;
}
static void
@@ -302,25 +312,67 @@
return r;
}
+static u32int*
+xecp(Ctlr *ctlr, uchar id, u32int *p)
+{
+ u32int x;
+
+ if(p != nil) {
+ x = *p;
+ goto Next;
+ }
+ x = ctlr->hccparams>>16;
+ if(x == 0)
+ return nil;
+ p = ctlr->mmio + x;
+ while(((x = *p) & 255) != id){
+ Next:
+ x >>= 8, x &= 255;
+ if(x == 0)
+ return nil;
+ p += x;
+ }
+ return p;
+}
+
static void
+handoff(Ctlr *ctlr)
+{
+ u32int *r;
+ int i;
+
+ if((r = xecp(ctlr, 1, nil)) == nil)
+ return;
+ r[0] |= 1<<24; /* request ownership */
+ for(i = 0; (r[0] & (1<<16)) != 0 && i<100; i++)
+ tsleep(&up->sleep, return0, nil, 10);
+ r[1] = 0; /* disable SMI interrupts */
+ r[0] &= ~(1<<16); /* in case of timeout */
+}
+
+static void
init(Hci *hp)
{
Ctlr *ctlr;
+ Port *pp;
+ u32int *x;
uchar *p;
- int i;
+ int i, j;
ctlr = hp->aux;
ctlr->opr = &ctlr->mmio[(ctlr->mmio[CAPLENGTH]&0xFF)/4];
- ctlr->prt = &ctlr->opr[0x400/4];
ctlr->dba = &ctlr->mmio[ctlr->mmio[DBOFF]/4];
ctlr->rts = &ctlr->mmio[ctlr->mmio[RTSOFF]/4];
- while(ctlr->opr[USBSTS] & CNR)
+ ctlr->hccparams = ctlr->mmio[HCCPARAMS];
+ handoff(ctlr);
+
+ for(i=0; (ctlr->opr[USBSTS] & CNR) != 0 && i<100; i++)
tsleep(&up->sleep, return0, nil, 10);
ctlr->opr[USBCMD] = HCRST;
- while((ctlr->opr[USBSTS] & (CNR|HCH)) != HCH)
+ for(i=0; (ctlr->opr[USBSTS] & (CNR|HCH)) != HCH && i<100; i++)
tsleep(&up->sleep, return0, nil, 10);
pcisetbme(ctlr->pcidev);
@@ -327,13 +379,11 @@
pcisetpms(ctlr->pcidev, 0);
intrenable(ctlr->pcidev->intl, hp->interrupt, hp, ctlr->pcidev->tbdf, hp->type);
- ctlr->hccparams = ctlr->mmio[HCCPARAMS];
ctlr->csz = (ctlr->hccparams & CSZ) != 0;
if(ctlr->hccparams & AC64)
ctlr->setrptr = setrptr64;
else
ctlr->setrptr = setrptr32;
-
ctlr->pagesize = ctlr->opr[PAGESIZE]<<12;
ctlr->nscratch = (ctlr->mmio[HCSPARAMS2] >> 27) & 0x1F;
@@ -341,6 +391,24 @@
ctlr->nslots = (ctlr->mmio[HCSPARAMS1] >> 0) & 0xFF;
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];
+
+ x = nil;
+ while((x = xecp(ctlr, 2, x)) != nil){
+ i = x[2]&255;
+ j = (x[2]>>8)&255;
+ while(j--){
+ if(i < 1 || i > hp->nports)
+ break;
+ pp = &ctlr->port[i-1];
+ pp->proto = x[0]>>16;
+ memmove(pp->spec, &x[1], 4);
+ i++;
+ }
+ }
+
ctlr->slot = malloc((1+ctlr->nslots)*sizeof(ctlr->slot[0]));
ctlr->dcba = mallocalign((1+ctlr->nslots)*8, 64, 0, ctlr->pagesize);
if(ctlr->nscratch != 0){
@@ -391,10 +459,9 @@
irs[IMOD] = 0;
}
- ctlr->mfwrap = 0;
-
+ ctlr->µframe = 0;
ctlr->opr[USBCMD] = RUNSTOP|INTE|HSEE|EWE;
- while(ctlr->opr[USBSTS] & (CNR|HCH))
+ for(i=0; (ctlr->opr[USBSTS] & (CNR|HCH)) != 0 && i<100; i++)
tsleep(&up->sleep, return0, nil, 10);
}
@@ -580,6 +647,9 @@
Slot *slot;
u32int *irs, *td, x;
+ if(ring->base == nil)
+ return;
+
irs = &ctlr->rts[IR0];
x = irs[IMAN];
if(x & 1) irs[IMAN] = x & 3;
@@ -603,7 +673,8 @@
completering(&slot->epr[(td[3]>>16)-1&31], td);
break;
case ER_MFINDEXWRAP:
- ctlr->mfwrap++;
+ ctlr->µframe = (ctlr->rts[MFINDEX] & (1<<14)-1) |
+ (ctlr->µframe+(1<<14) & ~((1<<14)-1));
break;
case ER_HCE:
iprint("xhci: host controller error: %ux %ux %ux %ux\n",
@@ -787,10 +858,9 @@
if(io->ring == nil)
return;
io->frame = 0;
- io->incr = (ep->hz<<8)/1000;
+ io->period = ep->pollival<<3*(ep->dev->speed == Fullspeed);
+ io->incr = (ep->hz*io->period<<8)/8000;
io->tdsz = (io->incr+255>>8)*ep->samplesz;
- if(io->tdsz > ep->maxpkt*ep->ntds)
- error(Egreg);
io->b = allocb((io->ring->mask+1)*io->tdsz);
}
@@ -977,36 +1047,6 @@
poperror();
}
-static char*
-unstall(Ring *r)
-{
- u64int qp;
- char *err;
-
- switch(r->ctx[0]&7){
- case 0: /* disabled */
- case 1: /* running */
- case 3: /* stopped */
- break;
- case 2: /* halted */
- case 4: /* error */
- ilock(r);
- r->rp = r->wp;
- qp = PADDR(&r->base[4*(r->wp & r->mask)]) | ((~r->wp>>r->shift) & 1);
- iunlock(r);
-
- err = ctlrcmd(r->slot->ctlr, CR_RESETEP | (r->id<<16) | (r->slot->id<<24), 0, 0, nil);
- ctlrcmd(r->slot->ctlr, CR_SETTRDQP | (r->id<<16) | (r->slot->id<<24), 0, qp, nil);
- if(err != nil)
- return err;
- }
-
- if(r->wp - r->rp >= r->mask)
- return "Ring Full";
-
- return nil;
-}
-
static long
isoread(Ep *, uchar *, long)
{
@@ -1020,7 +1060,7 @@
uchar *s, *d;
Ctlr *ctlr;
Epio *io;
- u32int x;
+ u32int i, µ;
long m;
s = p;
@@ -1030,21 +1070,20 @@
qunlock(io);
nexterror();
}
-
+ µ = io->period;
ctlr = ep->hp->aux;
- for(x = io->frame;; x++){
+ for(i = io->frame;; i++){
for(;;){
m = (int)(io->ring->wp - io->ring->rp);
if(m <= 0)
- x = 10 + mfindex(ctlr)/8;
- if(m < io->ring->mask-1)
+ i = (80 + µframe(ctlr))/µ;
+ if(m < io->ring->mask)
break;
- coherence();
*io->ring->doorbell = io->ring->id;
tsleep(&up->sleep, return0, nil, 5);
}
- m = ((io->incr + (x*io->incr&255))>>8)*ep->samplesz;
- d = io->b->rp + (x&io->ring->mask)*io->tdsz;
+ m = ((io->incr + (i*io->incr&255))>>8)*ep->samplesz;
+ d = io->b->rp + (i&io->ring->mask)*io->tdsz;
m -= io->nleft, d += io->nleft;
if(n < m){
memmove(d, p, n);
@@ -1056,28 +1095,56 @@
p += m, n -= m;
m += io->nleft, d -= io->nleft;
io->nleft = 0;
-
+ coherence();
ilock(io->ring);
- queuetd(io->ring, TR_ISOCH | (x & 0x7ff)<<20 | 1<<5, m, PADDR(d), nil);
+ queuetd(io->ring, TR_ISOCH | (i*µ/8 & 0x7ff)<<20 | 1<<5, m, PADDR(d), nil);
iunlock(io->ring);
}
- io->frame = x;
- coherence();
- *io->ring->doorbell = io->ring->id;
- qunlock(io);
- poperror();
-
- for(;;){
- int d = (int)(x - mfindex(ctlr)/8);
+ io->frame = i;
+ while(io->ring->rp != io->ring->wp){
+ int d = (int)(i*µ - µframe(ctlr))/8;
d -= ep->sampledelay*1000 / ep->hz;
if(d < 5)
break;
+ *io->ring->doorbell = io->ring->id;
tsleep(&up->sleep, return0, nil, d);
}
+ qunlock(io);
+ poperror();
return p - s;
}
+static char*
+unstall(Ring *r)
+{
+ u64int qp;
+ char *err;
+
+ switch(r->ctx[0]&7){
+ case 0: /* disabled */
+ case 1: /* running */
+ case 3: /* stopped */
+ break;
+ case 2: /* halted */
+ case 4: /* error */
+ ilock(r);
+ r->rp = r->wp;
+ qp = PADDR(&r->base[4*(r->wp & r->mask)]) | ((~r->wp>>r->shift) & 1);
+ iunlock(r);
+
+ err = ctlrcmd(r->slot->ctlr, CR_RESETEP | (r->id<<16) | (r->slot->id<<24), 0, 0, nil);
+ ctlrcmd(r->slot->ctlr, CR_SETTRDQP | (r->id<<16) | (r->slot->id<<24), 0, qp, nil);
+ if(err != nil)
+ return err;
+ }
+
+ if(r->wp - r->rp >= r->mask)
+ return "Ring Full";
+
+ return nil;
+}
+
static long
epread(Ep *ep, void *va, long n)
{
@@ -1269,9 +1336,13 @@
portstatus(Hci *hp, int port)
{
Ctlr *ctlr = hp->aux;
- u32int psc = ctlr->prt[PORTSC + (port-1)*4];
+ Port *pp = &ctlr->port[port-1];
+ u32int psc = pp->reg[PORTSC];
int ps = 0;
+ if(memcmp(pp->spec, "USB", 3) != 0 || pp->proto > 0x0200)
+ return 0;
+
if(psc & CCS) ps |= HPpresent;
if(psc & PED) ps |= HPenable;
if(psc & OCA) ps |= HPovercurrent;
@@ -1287,8 +1358,7 @@
case 3:
ps |= HPhigh;
break;
- case 4:
- /* super speed */
+ case 4: /* super speed */
break;
}
}
@@ -1310,10 +1380,13 @@
portreset(Hci *hp, int port, int on)
{
Ctlr *ctlr = hp->aux;
- u32int *r = &ctlr->prt[PORTSC + (port-1)*4];
+ Port *pp = &ctlr->port[port-1];
+ if(memcmp(pp->spec, "USB", 3) != 0 || pp->proto > 0x0200)
+ return 0;
+
if(on){
- *r |= PR;
+ pp->reg[PORTSC] |= PR;
tsleep(&up->sleep, return0, nil, 200);
}