usbohci: implement smm handover, timeouts, donehead dequeue.
implement SMM emulation driver handover in ohcireset(). this fixes hang and defunct internal keyboard problems on a acer notebook. dont spin forever waiting for the controller on soft reset in init(). check both, donehead pointer *and* interrupt status for processed td event (Wdh) similar to the ohci spec example for processed tds and unlink immidiately. acknowledge *all* the interrupt status bits before masking. mask out unhandled events. various stuff: check for christmas light interrupt status (cardbus controller removed?) add (missing?) break for Tddataovr error case in qhinterrupt(). (changed on sources, not clear why?) mask interrupt events on shutdown() (from sources).
This commit is contained in:
parent
4b4070a8b9
commit
f37d68003d
1 changed files with 84 additions and 50 deletions
|
@ -108,6 +108,7 @@ enum
|
|||
Cie = 0x08, /* iso. list enable */
|
||||
Ccle = 0x10, /* ctl list enable */
|
||||
Cble = 0x20, /* bulk list enable */
|
||||
Cir = 0x100, /* interrupt routing (smm active) */
|
||||
Cfsmask = 3 << 6, /* functional state... */
|
||||
Cfsreset = 0 << 6,
|
||||
Cfsresume = 1 << 6,
|
||||
|
@ -115,6 +116,7 @@ enum
|
|||
Cfssuspend = 3 << 6,
|
||||
|
||||
/* command status */
|
||||
Socr = 1 << 3, /* ownership change request */
|
||||
Sblf = 1 << 2, /* bulk list (load) flag */
|
||||
Sclf = 1 << 1, /* control list (load) flag */
|
||||
Shcr = 1 << 0, /* host controller reset */
|
||||
|
@ -588,7 +590,7 @@ tdalloc(void)
|
|||
ddprint("ohci: tdalloc %d Tds\n", Incr);
|
||||
pool = xspanalloc(Incr*sizeof(Td), Align, 0);
|
||||
if(pool == nil)
|
||||
panic("tdalloc");
|
||||
panic("ohci: tdalloc");
|
||||
for(i=Incr; --i>=0;){
|
||||
pool[i].next = tdpool.free;
|
||||
tdpool.free = &pool[i];
|
||||
|
@ -636,7 +638,7 @@ edalloc(void)
|
|||
ddprint("ohci: edalloc %d Eds\n", Incr);
|
||||
pool = xspanalloc(Incr*sizeof(Ed), Align, 0);
|
||||
if(pool == nil)
|
||||
panic("edalloc");
|
||||
panic("ohci: edalloc");
|
||||
for(i=Incr; --i>=0;){
|
||||
pool[i].next = edpool.free;
|
||||
edpool.free = &pool[i];
|
||||
|
@ -1145,6 +1147,7 @@ qhinterrupt(Ctlr *, Ep *ep, Qio *io, Td *td, int)
|
|||
|
||||
switch(err){
|
||||
case Tddataovr: /* Overrun is not an error */
|
||||
break;
|
||||
case Tdok:
|
||||
/* virtualbox doesn't always report underflow on short packets */
|
||||
if(td->cbp == 0)
|
||||
|
@ -1263,57 +1266,66 @@ isointerrupt(Ctlr *ctlr, Ep *ep, Qio *io, Td *td, int)
|
|||
static void
|
||||
interrupt(Ureg *, void *arg)
|
||||
{
|
||||
Td *td, *ntd, *td0;
|
||||
Td *td, *ntd;
|
||||
Hci *hp;
|
||||
Ctlr *ctlr;
|
||||
ulong status, curred;
|
||||
ulong status, curred, done;
|
||||
int i, frno;
|
||||
|
||||
hp = arg;
|
||||
ctlr = hp->aux;
|
||||
ilock(ctlr);
|
||||
done = ctlr->hcca->donehead;
|
||||
status = ctlr->ohci->intrsts;
|
||||
if(status == ~0){
|
||||
iunlock(ctlr);
|
||||
return;
|
||||
}
|
||||
if(done & ~0xF){
|
||||
ctlr->hcca->donehead = 0;
|
||||
status |= Wdh;
|
||||
}
|
||||
else if(status & Wdh){
|
||||
done = ctlr->hcca->donehead;
|
||||
ctlr->hcca->donehead = 0;
|
||||
}
|
||||
status &= ~Mie;
|
||||
if(status == 0){
|
||||
iunlock(ctlr);
|
||||
return;
|
||||
}
|
||||
ctlr->ohci->intrsts = status;
|
||||
status &= ctlr->ohci->intrenable;
|
||||
status &= Oc|Rhsc|Fno|Ue|Rd|Sf|Wdh|So;
|
||||
frno = TRUNC(ctlr->ohci->fmnumber, Ntdframes);
|
||||
if((status & Wdh) != 0){
|
||||
/* lsb of donehead has bit to flag other intrs. */
|
||||
td = pa2ptr(ctlr->hcca->donehead & ~0xF);
|
||||
}else
|
||||
td = nil;
|
||||
td0 = td;
|
||||
|
||||
for(i = 0; td != nil && i < 1024; i++){
|
||||
if(0)ddprint("ohci tdinterrupt: td %#p\n", td);
|
||||
ntd = pa2ptr(td->nexttd & ~0xF);
|
||||
td->nexttd = 0;
|
||||
if(td->ep == nil || td->io == nil)
|
||||
panic("ohci: interrupt: ep %#p io %#p", td->ep, td->io);
|
||||
ohciinterrupts[td->ep->ttype]++;
|
||||
if(td->ep->ttype == Tiso)
|
||||
isointerrupt(ctlr, td->ep, td->io, td, frno);
|
||||
else
|
||||
qhinterrupt(ctlr, td->ep, td->io, td, frno);
|
||||
td = ntd;
|
||||
if(status & Wdh){
|
||||
frno = TRUNC(ctlr->ohci->fmnumber, Ntdframes);
|
||||
td = pa2ptr(done & ~0xF);
|
||||
for(i = 0; td != nil && i < 1024; i++){
|
||||
if(0)ddprint("ohci tdinterrupt: td %#p\n", td);
|
||||
ntd = pa2ptr(td->nexttd & ~0xF);
|
||||
td->nexttd = 0;
|
||||
if(td->ep == nil || td->io == nil)
|
||||
panic("ohci: interrupt: ep %#p io %#p", td->ep, td->io);
|
||||
ohciinterrupts[td->ep->ttype]++;
|
||||
if(td->ep->ttype == Tiso)
|
||||
isointerrupt(ctlr, td->ep, td->io, td, frno);
|
||||
else
|
||||
qhinterrupt(ctlr, td->ep, td->io, td, frno);
|
||||
td = ntd;
|
||||
}
|
||||
if(i == 1024)
|
||||
iprint("ohci: bug: more than 1024 done Tds?\n");
|
||||
status &= ~Wdh;
|
||||
}
|
||||
if(i == 1024)
|
||||
print("ohci: bug: more than 1024 done Tds?\n");
|
||||
|
||||
if(pa2ptr(ctlr->hcca->donehead & ~0xF) != td0)
|
||||
print("ohci: bug: donehead changed before ack\n");
|
||||
ctlr->hcca->donehead = 0;
|
||||
|
||||
ctlr->ohci->intrsts = status;
|
||||
status &= ~Wdh;
|
||||
status &= ~Sf;
|
||||
if(status & So){
|
||||
print("ohci: sched overrun: too much load\n");
|
||||
iprint("ohci: sched overrun: too much load\n");
|
||||
ctlr->overrun++;
|
||||
status &= ~So;
|
||||
}
|
||||
if((status & Ue) != 0){
|
||||
if(status & Ue){
|
||||
curred = ctlr->ohci->periodcurred;
|
||||
print("ohci: unrecoverable error frame 0x%.8lux ed 0x%.8lux, "
|
||||
iprint("ohci: unrecoverable error frame 0x%.8lux ed 0x%.8lux, "
|
||||
"ints %d %d %d %d\n",
|
||||
ctlr->ohci->fmnumber, curred,
|
||||
ohciinterrupts[Tctl], ohciinterrupts[Tintr],
|
||||
|
@ -1322,8 +1334,10 @@ interrupt(Ureg *, void *arg)
|
|||
dumped(pa2ptr(curred));
|
||||
status &= ~Ue;
|
||||
}
|
||||
if(status != 0)
|
||||
print("ohci interrupt: unhandled sts 0x%.8lux\n", status);
|
||||
if(status != 0){
|
||||
iprint("ohci interrupt: unhandled sts 0x%.8lux\n", status);
|
||||
ctlr->ohci->intrdisable = status;
|
||||
}
|
||||
iunlock(ctlr);
|
||||
}
|
||||
|
||||
|
@ -2301,11 +2315,16 @@ init(Hci *hp)
|
|||
dprint("ohci %#p init\n", ctlr->ohci);
|
||||
ohci = ctlr->ohci;
|
||||
|
||||
fmi = ctlr->ohci->fminterval;
|
||||
ctlr->ohci->cmdsts = Shcr; /* reset the block */
|
||||
while(ctlr->ohci->cmdsts & Shcr)
|
||||
delay(1); /* wait till reset complete, Ohci says 10us max. */
|
||||
ctlr->ohci->fminterval = fmi;
|
||||
fmi = ohci->fminterval;
|
||||
ohci->cmdsts = Shcr; /* reset the block */
|
||||
for(i = 0; i<100; i++){
|
||||
if((ohci->cmdsts & Shcr) == 0)
|
||||
break;
|
||||
delay(1); /* wait till reset complete, Ohci says 10us max. */
|
||||
}
|
||||
if(i == 100)
|
||||
print("ohci: reset timed out\n");
|
||||
ohci->fminterval = fmi;
|
||||
|
||||
/*
|
||||
* now that soft reset is done we are in suspend state.
|
||||
|
@ -2379,13 +2398,13 @@ scanpci(void)
|
|||
dprint("ohci: %x/%x port 0x%lux size 0x%x irq %d\n",
|
||||
p->vid, p->did, mem, p->mem[0].size, p->intl);
|
||||
if(mem == 0){
|
||||
print("usbohci: failed to map registers\n");
|
||||
print("ohci: failed to map registers\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
ctlr = malloc(sizeof(Ctlr));
|
||||
if(ctlr == nil){
|
||||
print("usbohci: no memory\n");
|
||||
print("ohci: no memory\n");
|
||||
continue;
|
||||
}
|
||||
ctlr->pcidev = p;
|
||||
|
@ -2399,7 +2418,7 @@ scanpci(void)
|
|||
break;
|
||||
}
|
||||
if(i == Nhcis)
|
||||
print("usbohci: bug: no more controllers\n");
|
||||
print("ohci: bug: no more controllers\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2424,16 +2443,16 @@ mkqhtree(Ctlr *ctlr)
|
|||
n = (1 << (depth+1)) - 1;
|
||||
qt = mallocz(sizeof(*qt), 1);
|
||||
if(qt == nil)
|
||||
panic("usb: can't allocate scheduling tree");
|
||||
panic("ohci: can't allocate scheduling tree");
|
||||
qt->nel = n;
|
||||
qt->depth = depth;
|
||||
qt->bw = mallocz(n * sizeof(qt->bw), 1);
|
||||
qt->root = tree = mallocz(n * sizeof(Ed *), 1);
|
||||
if(qt->bw == nil || qt->root == nil)
|
||||
panic("usb: can't allocate scheduling tree");
|
||||
panic("ohci: can't allocate scheduling tree");
|
||||
for(i = 0; i < n; i++){
|
||||
if((tree[i] = edalloc()) == nil)
|
||||
panic("mkqhtree");
|
||||
panic("ohci: mkqhtree");
|
||||
tree[i]->ctrl = (8 << Edmpsshift); /* not needed */
|
||||
tree[i]->ctrl |= Edskip;
|
||||
|
||||
|
@ -2473,7 +2492,7 @@ ohcimeminit(Ctlr *ctlr)
|
|||
|
||||
hcca = xspanalloc(sizeof(Hcca), 256, 0);
|
||||
if(hcca == nil)
|
||||
panic("usbhreset: no memory for Hcca");
|
||||
panic("ohci: no memory for Hcca");
|
||||
memset(hcca, 0, sizeof(*hcca));
|
||||
ctlr->hcca = hcca;
|
||||
|
||||
|
@ -2483,9 +2502,23 @@ ohcimeminit(Ctlr *ctlr)
|
|||
static void
|
||||
ohcireset(Ctlr *ctlr)
|
||||
{
|
||||
int i;
|
||||
|
||||
ilock(ctlr);
|
||||
dprint("ohci %#p reset\n", ctlr->ohci);
|
||||
|
||||
if(ctlr->ohci->control & Cir){
|
||||
dprint("ohci: smm active, taking over\n");
|
||||
ctlr->ohci->cmdsts |= Socr; /* take ownership */
|
||||
for(i = 0; i<100; i++){
|
||||
if((ctlr->ohci->control & Cir) == 0)
|
||||
break;
|
||||
delay(1);
|
||||
}
|
||||
if(i == 100)
|
||||
print("ohci: smm takeover timed out\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* usually enter here in reset, wait till its through,
|
||||
* then do our own so we are on known timing conditions.
|
||||
|
@ -2509,6 +2542,7 @@ shutdown(Hci *hp)
|
|||
ctlr = hp->aux;
|
||||
|
||||
ilock(ctlr);
|
||||
ctlr->ohci->intrdisable = Mie;
|
||||
ctlr->ohci->intrenable = 0;
|
||||
ctlr->ohci->control = 0;
|
||||
delay(100);
|
||||
|
|
Loading…
Reference in a new issue