usbxhci: implement recovery from host controller errors

This commit is contained in:
cinap_lenrek 2017-08-07 01:59:54 +02:00
parent 7d1b9e39f7
commit e0474599dd

View file

@ -249,6 +249,7 @@ struct Ctlr
void (*setrptr)(u32int*, u64int); void (*setrptr)(u32int*, u64int);
Rendez recover;
void *active; void *active;
uintptr base; uintptr base;
}; };
@ -271,6 +272,7 @@ struct Epio
static char Ebadlen[] = "bad usb request length"; static char Ebadlen[] = "bad usb request length";
static char Enotconfig[] = "usb endpoint not configured"; static char Enotconfig[] = "usb endpoint not configured";
static char Erecover[] = "xhci controller needs reset";
static void static void
setrptr32(u32int *reg, u64int pa) setrptr32(u32int *reg, u64int pa)
@ -406,6 +408,26 @@ shutdown(Hci *hp)
pciclrbme(ctlr->pcidev); pciclrbme(ctlr->pcidev);
} }
static void
release(Ctlr *ctlr)
{
int i;
freering(ctlr->cr);
for(i=0; i<nelem(ctlr->er); i++){
freering(&ctlr->er[i]);
free(ctlr->erst[i]);
ctlr->erst[i] = nil;
}
free(ctlr->port), ctlr->port = nil;
free(ctlr->slot), ctlr->slot = nil;
free(ctlr->dcba), ctlr->dcba = nil;
free(ctlr->sba), ctlr->sba = nil;
free(ctlr->sbp), ctlr->sbp = nil;
}
static void recover(void *arg);
static void static void
init(Hci *hp) init(Hci *hp)
{ {
@ -437,17 +459,7 @@ init(Hci *hp)
if(waserror()){ if(waserror()){
shutdown(hp); shutdown(hp);
freering(ctlr->cr); release(ctlr);
for(i=0; i<nelem(ctlr->er); i++){
freering(&ctlr->er[i]);
free(ctlr->erst[i]);
ctlr->erst[i] = nil;
}
free(ctlr->port), ctlr->port = nil;
free(ctlr->slot), ctlr->slot = nil;
free(ctlr->dcba), ctlr->dcba = nil;
free(ctlr->sba), ctlr->sba = nil;
free(ctlr->sbp), ctlr->sbp = nil;
nexterror(); nexterror();
} }
@ -544,6 +556,80 @@ init(Hci *hp)
ctlr->opr[USBCMD] = RUNSTOP|INTE|HSEE|EWE; ctlr->opr[USBCMD] = RUNSTOP|INTE|HSEE|EWE;
for(i=0; (ctlr->opr[USBSTS] & (CNR|HCH)) != 0 && i<100; i++) for(i=0; (ctlr->opr[USBSTS] & (CNR|HCH)) != 0 && i<100; i++)
tsleep(&up->sleep, return0, nil, 10); tsleep(&up->sleep, return0, nil, 10);
kproc("xhcirecover", recover, hp);
}
static int
needrecover(void *arg)
{
Ctlr *ctlr = arg;
return ctlr->er->stopped ||
(ctlr->opr[USBSTS] & (HCH|HCE|HSE)) != 0;
}
static void
recover(void *arg)
{
Hci *hp = arg;
Ctlr *ctlr = hp->aux;
while(waserror())
;
while(!needrecover(ctlr))
tsleep(&ctlr->recover, needrecover, ctlr, 1000);
shutdown(hp);
/*
* flush all transactions and wait until all devices have
* been detached by usbd.
*/
for(;;){
int i, j, active;
ilock(ctlr->cr);
ctlr->cr->stopped = 1;
flushring(ctlr->cr);
iunlock(ctlr->cr);
active = 0;
qlock(&ctlr->slotlock);
for(i=1; i<=ctlr->nslots; i++){
Slot *slot = ctlr->slot[i];
if(slot == nil)
continue;
active++;
for(j=0; j < slot->nep; j++){
Ring *ring = &slot->epr[j];
if(ring->base == nil)
continue;
ilock(ring);
ring->stopped = 1;
flushring(ring);
iunlock(ring);
}
}
qunlock(&ctlr->slotlock);
if(active == 0)
break;
tsleep(&up->sleep, return0, nil, 100);
}
qlock(&ctlr->slotlock);
qlock(&ctlr->cmdlock);
release(ctlr);
if(waserror()) {
print("xhci recovery failed: %s\n", up->errstr);
} else {
init(hp);
poperror();
}
qunlock(&ctlr->cmdlock);
qunlock(&ctlr->slotlock);
} }
static void static void
@ -679,14 +765,11 @@ ctlrcmd(Ctlr *ctlr, u32int c, u32int s, u64int p, u32int *er)
char *err; char *err;
qlock(&ctlr->cmdlock); qlock(&ctlr->cmdlock);
if(needrecover(ctlr)){
if((ctlr->opr[USBSTS] & (HCH|HCE|HSE)) != 0){
qunlock(&ctlr->cmdlock); qunlock(&ctlr->cmdlock);
return "xhci busted"; return Erecover;
} }
ctlr->cr->stopped = 0; ctlr->cr->stopped = 0;
queuetd(ctlr->cr, c, s, p, w); queuetd(ctlr->cr, c, s, p, w);
err = waittd(ctlr, w, 5000); err = waittd(ctlr, w, 5000);
@ -779,11 +862,9 @@ interrupt(Ureg*, void *arg)
case ER_HCE: case ER_HCE:
iprint("xhci: host controller error: %ux %ux %ux %ux\n", iprint("xhci: host controller error: %ux %ux %ux %ux\n",
td[0], td[1], td[2], td[3]); td[0], td[1], td[2], td[3]);
ilock(ctlr->cr); ctlr->er->stopped = 1;
ctlr->cr->stopped = 1; wakeup(&ctlr->recover);
flushring(ctlr->cr); return;
iunlock(ctlr->cr);
break;
case ER_PORTSC: case ER_PORTSC:
break; break;
case ER_BWREQ: case ER_BWREQ:
@ -802,17 +883,18 @@ static void
freeslot(void *arg) freeslot(void *arg)
{ {
Slot *slot; Slot *slot;
Ctlr *ctlr;
if(arg == nil) if(arg == nil)
return; return;
slot = arg; slot = arg;
ctlr = slot->ctlr;
if(slot->id != 0){ if(slot->id != 0){
Ctlr *ctlr = slot->ctlr;
qlock(&ctlr->slotlock); qlock(&ctlr->slotlock);
ctlrcmd(ctlr, CR_DISABLESLOT | (slot->id<<24), 0, 0, nil); if(ctlr->slot != nil && ctlr->slot[slot->id] == slot){
ctlr->dcba[slot->id] = 0; ctlrcmd(ctlr, CR_DISABLESLOT | (slot->id<<24), 0, 0, nil);
ctlr->slot[slot->id] = nil; ctlr->dcba[slot->id] = 0;
ctlr->slot[slot->id] = nil;
}
qunlock(&ctlr->slotlock); qunlock(&ctlr->slotlock);
} }
freering(&slot->epr[0]); freering(&slot->epr[0]);
@ -843,6 +925,8 @@ allocslot(Ctlr *ctlr, Udev *dev)
freeslot(slot); freeslot(slot);
nexterror(); nexterror();
} }
if(ctlr->slot == nil)
error(Erecover);
slot->ibase = mallocalign(32*33 << ctlr->csz, 64, 0, ctlr->pagesize); slot->ibase = mallocalign(32*33 << ctlr->csz, 64, 0, ctlr->pagesize);
slot->obase = mallocalign(32*32 << ctlr->csz, 64, 0, ctlr->pagesize); slot->obase = mallocalign(32*32 << ctlr->csz, 64, 0, ctlr->pagesize);
if(slot->ibase == nil || slot->obase == nil) if(slot->ibase == nil || slot->obase == nil)
@ -1065,7 +1149,8 @@ epopen(Ep *ep)
if(ep->dev->isroot) if(ep->dev->isroot)
return; return;
if(needrecover(ctlr))
error(Erecover);
io = malloc(sizeof(Epio)*2); io = malloc(sizeof(Epio)*2);
if(io == nil) if(io == nil)
error(Enomem); error(Enomem);
@ -1171,6 +1256,8 @@ isowrite(Ep *ep, uchar *p, long n)
} }
µ = io->period; µ = io->period;
ctlr = ep->hp->aux; ctlr = ep->hp->aux;
if(needrecover(ctlr))
error(Erecover);
for(i = io->frame;; i++){ for(i = io->frame;; i++){
for(;;){ for(;;){
m = (int)(io->ring->wp - io->ring->rp); m = (int)(io->ring->wp - io->ring->rp);
@ -1439,10 +1526,13 @@ static int
portstatus(Hci *hp, int port) portstatus(Hci *hp, int port)
{ {
Ctlr *ctlr = hp->aux; Ctlr *ctlr = hp->aux;
Port *pp = &ctlr->port[port-1]; u32int psc, ps;
u32int psc, ps = 0;
psc = pp->reg[PORTSC]; if(ctlr->port == nil || needrecover(ctlr))
return 0;
ps = 0;
psc = ctlr->port[port-1].reg[PORTSC];
if(psc & CCS) ps |= HPpresent; if(psc & CCS) ps |= HPpresent;
if(psc & PED) ps |= HPenable; if(psc & PED) ps |= HPenable;
if(psc & OCA) ps |= HPovercurrent; if(psc & OCA) ps |= HPovercurrent;
@ -1488,13 +1578,14 @@ static int
portreset(Hci *hp, int port, int on) portreset(Hci *hp, int port, int on)
{ {
Ctlr *ctlr = hp->aux; Ctlr *ctlr = hp->aux;
Port *pp = &ctlr->port[port-1];
if(ctlr->port == nil || needrecover(ctlr))
return 0;
if(on){ if(on){
pp->reg[PORTSC] |= PR; ctlr->port[port-1].reg[PORTSC] |= PR;
tsleep(&up->sleep, return0, nil, 200); tsleep(&up->sleep, return0, nil, 200);
} }
return 0; return 0;
} }