usbdwc: enable Slowbuilkin workarround, improve split transaction timing, handle erroring sleep(), debugging
i'v been seeing the error condition described above in the Slowbulkin comment. so i'm enabling the work arround which seems to fix the lockup. in the split transaction case where we want to start the transaction at frame start, acquire the ctlr lock *before* checking if we are in the right frame number. so the start will happen atomically. checking the software ctlr->sofchan instead of checking the interrupt mask register seems to be quicker. setting the haint mask bit for the chan under ctlr lock in chanio() instead of chanwait() avoids needing to acquire the ctlr lock twice. mask wakechan bits with busychan bitmap in interrupt handlers so we will not try to wake up released chans by accident. sleep() and tsleep() might get interrupted so we have to release the split qlock in the split transaction case and in all cases, make sure to halt the channel before release. add some common debug functions to dump channel and controller registers.
This commit is contained in:
parent
21ce34bd7f
commit
4d96c40a47
1 changed files with 122 additions and 79 deletions
|
@ -41,10 +41,9 @@ enum
|
||||||
* when the controller is reading a sequence of bulk input
|
* when the controller is reading a sequence of bulk input
|
||||||
* packets in DMA mode. Setting Slowbulkin=1 will avoid the
|
* packets in DMA mode. Setting Slowbulkin=1 will avoid the
|
||||||
* lockup by reading packets individually with an interrupt
|
* lockup by reading packets individually with an interrupt
|
||||||
* after each. More recent chips don't seem to exhibit the
|
* after each.
|
||||||
* problem, so it's probably safe to leave this off now.
|
|
||||||
*/
|
*/
|
||||||
Slowbulkin = 0,
|
Slowbulkin = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct Ctlr Ctlr;
|
typedef struct Ctlr Ctlr;
|
||||||
|
@ -54,13 +53,13 @@ struct Ctlr {
|
||||||
Lock;
|
Lock;
|
||||||
Dwcregs *regs; /* controller registers */
|
Dwcregs *regs; /* controller registers */
|
||||||
int nchan; /* number of host channels */
|
int nchan; /* number of host channels */
|
||||||
ulong chanbusy; /* bitmap of in-use channels */
|
uint chanbusy; /* bitmap of in-use channels */
|
||||||
Lock chanlock; /* serialise access to chanbusy */
|
Lock chanlock; /* serialise access to chanbusy */
|
||||||
QLock split; /* serialise split transactions */
|
QLock split; /* serialise split transactions */
|
||||||
int splitretry; /* count retries of Nyet */
|
int splitretry; /* count retries of Nyet */
|
||||||
int sofchan; /* bitmap of channels waiting for sof */
|
uint sofchan; /* bitmap of channels waiting for sof */
|
||||||
int wakechan; /* bitmap of channels to wakeup after fiq */
|
uint wakechan; /* bitmap of channels to wakeup after fiq */
|
||||||
int debugchan; /* bitmap of channels for interrupt debug */
|
uint debugchan; /* bitmap of channels for interrupt debug */
|
||||||
Rendez *chanintr; /* sleep till interrupt on channel N */
|
Rendez *chanintr; /* sleep till interrupt on channel N */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -75,13 +74,17 @@ struct Epio {
|
||||||
};
|
};
|
||||||
|
|
||||||
static Ctlr dwc;
|
static Ctlr dwc;
|
||||||
static int debug;
|
static int debug = 0;
|
||||||
|
|
||||||
static char Ebadlen[] = "bad usb request length";
|
static char Ebadlen[] = "bad usb request length";
|
||||||
|
|
||||||
static void clog(Ep *ep, Hostchan *hc);
|
static void clog(Ep *ep, Hostchan *hc);
|
||||||
static void logdump(Ep *ep);
|
static void logdump(Ep *ep);
|
||||||
|
|
||||||
|
static void dumpctlr(Ctlr *ctlr);
|
||||||
|
static void dumphchan(Ctlr *ctlr, Hostchan *hc);
|
||||||
|
static void dump(Hci *hp);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
filock(Lock *l)
|
filock(Lock *l)
|
||||||
{
|
{
|
||||||
|
@ -102,7 +105,8 @@ static Hostchan*
|
||||||
chanalloc(Ep *ep)
|
chanalloc(Ep *ep)
|
||||||
{
|
{
|
||||||
Ctlr *ctlr;
|
Ctlr *ctlr;
|
||||||
int bitmap, i;
|
int i;
|
||||||
|
uint bitmap;
|
||||||
static int first;
|
static int first;
|
||||||
|
|
||||||
ctlr = ep->hp->aux;
|
ctlr = ep->hp->aux;
|
||||||
|
@ -123,13 +127,13 @@ retry:
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
chanrelease(Ep *ep, Hostchan *chan)
|
chanrelease(Ep *ep, Hostchan *hc)
|
||||||
{
|
{
|
||||||
Ctlr *ctlr;
|
Ctlr *ctlr;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
ctlr = ep->hp->aux;
|
ctlr = ep->hp->aux;
|
||||||
i = chan - ctlr->regs->hchan;
|
i = hc - ctlr->regs->hchan;
|
||||||
lock(&ctlr->chanlock);
|
lock(&ctlr->chanlock);
|
||||||
ctlr->chanbusy &= ~(1<<i);
|
ctlr->chanbusy &= ~(1<<i);
|
||||||
unlock(&ctlr->chanlock);
|
unlock(&ctlr->chanlock);
|
||||||
|
@ -188,74 +192,68 @@ chansetup(Hostchan *hc, Ep *ep)
|
||||||
hc->hcint = ~0;
|
hc->hcint = ~0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
chanhalt(Ep *ep, Hostchan *hc)
|
||||||
|
{
|
||||||
|
ulong start;
|
||||||
|
|
||||||
|
hc->hcintmsk = 0;
|
||||||
|
hc->hcchar |= Chdis;
|
||||||
|
start = m->ticks;
|
||||||
|
while(hc->hcchar & Chen){
|
||||||
|
if(m->ticks - start >= 100){
|
||||||
|
print("ep%d.%d channel won't halt hcchar %8.8ux\n",
|
||||||
|
ep->dev->nb, ep->nb, hc->hcchar);
|
||||||
|
dump(ep->hp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
sofdone(void *a)
|
sofdone(void *a)
|
||||||
{
|
{
|
||||||
Dwcregs *r;
|
Ctlr *ctlr = a;
|
||||||
|
return ctlr->sofchan == 0;
|
||||||
r = a;
|
|
||||||
return (r->gintmsk & Sofintr) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sofwait(Ctlr *ctlr, int n)
|
|
||||||
{
|
|
||||||
Dwcregs *r;
|
|
||||||
|
|
||||||
r = ctlr->regs;
|
|
||||||
do{
|
|
||||||
filock(ctlr);
|
|
||||||
r->gintsts = Sofintr;
|
|
||||||
ctlr->sofchan |= 1<<n;
|
|
||||||
r->gintmsk |= Sofintr;
|
|
||||||
fiunlock(ctlr);
|
|
||||||
sleep(&ctlr->chanintr[n], sofdone, r);
|
|
||||||
}while((r->hfnum & 7) == 6);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
chandone(void *a)
|
chandone(void *a)
|
||||||
{
|
{
|
||||||
Hostchan *hc;
|
Hostchan *hc;
|
||||||
|
int i;
|
||||||
|
|
||||||
hc = a;
|
hc = a;
|
||||||
if(hc->hcint == (Chhltd|Ack))
|
i = hc->hcint;
|
||||||
|
if(i == (Chhltd|Ack))
|
||||||
return 0;
|
return 0;
|
||||||
return (hc->hcint & hc->hcintmsk) != 0;
|
return (i & hc->hcintmsk) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
|
chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
|
||||||
{
|
{
|
||||||
int intr, n, ointr;
|
int intr, ointr, chan;
|
||||||
ulong start, now;
|
ulong start, now;
|
||||||
Dwcregs *r;
|
|
||||||
|
|
||||||
r = ctlr->regs;
|
chan = hc - ctlr->regs->hchan;
|
||||||
n = hc - r->hchan;
|
|
||||||
for(;;){
|
for(;;){
|
||||||
restart:
|
restart:
|
||||||
filock(ctlr);
|
tsleep(&ctlr->chanintr[chan], chandone, hc, 1000);
|
||||||
r->haintmsk |= 1<<n;
|
|
||||||
hc->hcintmsk = mask;
|
|
||||||
fiunlock(ctlr);
|
|
||||||
tsleep(&ctlr->chanintr[n], chandone, hc, 1000);
|
|
||||||
if((intr = hc->hcint) == 0)
|
if((intr = hc->hcint) == 0)
|
||||||
goto restart;
|
goto restart;
|
||||||
hc->hcintmsk = 0;
|
|
||||||
if(intr & Chhltd)
|
if(intr & Chhltd)
|
||||||
return intr;
|
return intr;
|
||||||
start = fastticks(0);
|
|
||||||
ointr = intr;
|
ointr = intr;
|
||||||
now = start;
|
now = start = fastticks(0);
|
||||||
do{
|
do{
|
||||||
intr = hc->hcint;
|
intr = hc->hcint;
|
||||||
if(intr & Chhltd){
|
if(intr & Chhltd){
|
||||||
if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
|
if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
|
||||||
intr != (Ack|Chhltd|Xfercomp) ||
|
intr != (Ack|Chhltd|Xfercomp) ||
|
||||||
(now - start) > 60)
|
(now - start) > 60)
|
||||||
dprint("await %x after %ldµs %x -> %x\n",
|
dprint("ep%d.%d await %x after %ldµs %x -> %x\n",
|
||||||
mask, now - start, ointr, intr);
|
ep->dev->nb, ep->nb, mask, now - start, ointr, intr);
|
||||||
return intr;
|
return intr;
|
||||||
}
|
}
|
||||||
if((intr & mask) == 0){
|
if((intr & mask) == 0){
|
||||||
|
@ -266,21 +264,15 @@ restart:
|
||||||
}
|
}
|
||||||
now = fastticks(0);
|
now = fastticks(0);
|
||||||
}while(now - start < 100);
|
}while(now - start < 100);
|
||||||
dprint("ep%d.%d halting channel %8.8ux hcchar %8.8ux "
|
if(debug){
|
||||||
"grxstsr %8.8ux gnptxsts %8.8ux hptxsts %8.8ux\n",
|
print("ep%d.%d halting chan %d intr %x\n",
|
||||||
ep->dev->nb, ep->nb, intr, hc->hcchar, r->grxstsr,
|
ep->dev->nb, ep->nb, chan, intr);
|
||||||
r->gnptxsts, r->hptxsts);
|
dumphchan(ctlr, hc);
|
||||||
mask = Chhltd;
|
dumpctlr(ctlr);
|
||||||
hc->hcchar |= Chdis;
|
|
||||||
start = m->ticks;
|
|
||||||
while(hc->hcchar & Chen){
|
|
||||||
if(m->ticks - start >= 100){
|
|
||||||
print("ep%d.%d channel won't halt hcchar %8.8ux\n",
|
|
||||||
ep->dev->nb, ep->nb, hc->hcchar);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
chanhalt(ep, hc);
|
||||||
logdump(ep);
|
logdump(ep);
|
||||||
|
hc->hcintmsk = mask = Chhltd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,7 +363,7 @@ static int
|
||||||
chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
|
chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
|
||||||
{
|
{
|
||||||
Ctlr *ctlr;
|
Ctlr *ctlr;
|
||||||
int nleft, n, nt, i, maxpkt, npkt;
|
int nleft, n, nt, i, imask, maxpkt, npkt, chan, split;
|
||||||
uint hcdma, hctsiz;
|
uint hcdma, hctsiz;
|
||||||
|
|
||||||
ctlr = ep->hp->aux;
|
ctlr = ep->hp->aux;
|
||||||
|
@ -388,9 +380,17 @@ chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
|
||||||
hc->hctsiz = n | npkt<<OPktcnt | pid;
|
hc->hctsiz = n | npkt<<OPktcnt | pid;
|
||||||
hc->hcdma = dmaaddr(a);
|
hc->hcdma = dmaaddr(a);
|
||||||
|
|
||||||
|
split = hc->hcsplt & Spltena;
|
||||||
|
if(ep->ttype == Tbulk && dir == Epin || ep->ttype == Tintr && split)
|
||||||
|
imask = Chhltd;
|
||||||
|
else
|
||||||
|
imask = Chhltd|Nak;
|
||||||
|
|
||||||
nleft = len;
|
nleft = len;
|
||||||
logstart(ep);
|
logstart(ep);
|
||||||
for(;;){
|
for(;;){
|
||||||
|
Dwcregs *r;
|
||||||
|
|
||||||
hcdma = hc->hcdma;
|
hcdma = hc->hcdma;
|
||||||
hctsiz = hc->hctsiz;
|
hctsiz = hc->hctsiz;
|
||||||
hc->hctsiz = hctsiz & ~Dopng;
|
hc->hctsiz = hctsiz & ~Dopng;
|
||||||
|
@ -407,34 +407,46 @@ chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
|
||||||
ep->dev->nb, ep->nb, i);
|
ep->dev->nb, ep->nb, i);
|
||||||
hc->hcint = i;
|
hc->hcint = i;
|
||||||
}
|
}
|
||||||
if(hc->hcsplt & Spltena){
|
|
||||||
|
r = ctlr->regs;
|
||||||
|
chan = hc - r->hchan;
|
||||||
|
if(split){
|
||||||
qlock(&ctlr->split);
|
qlock(&ctlr->split);
|
||||||
sofwait(ctlr, hc - ctlr->regs->hchan);
|
if(waserror()){
|
||||||
if((dwc.regs->hfnum & 1) == 0)
|
qunlock(&ctlr->split);
|
||||||
|
nexterror();
|
||||||
|
}
|
||||||
|
filock(ctlr);
|
||||||
|
do {
|
||||||
|
ctlr->sofchan = 1<<chan;
|
||||||
|
r->gintmsk |= Sofintr;
|
||||||
|
fiunlock(ctlr);
|
||||||
|
sleep(&ctlr->chanintr[chan], sofdone, ctlr);
|
||||||
|
filock(ctlr);
|
||||||
|
i = r->hfnum;
|
||||||
|
} while((i & 7) == 6);
|
||||||
|
if((i & 1) == 0)
|
||||||
hc->hcchar &= ~Oddfrm;
|
hc->hcchar &= ~Oddfrm;
|
||||||
else
|
else
|
||||||
hc->hcchar |= Oddfrm;
|
hc->hcchar |= Oddfrm;
|
||||||
|
} else {
|
||||||
|
filock(ctlr);
|
||||||
}
|
}
|
||||||
|
hc->hcintmsk = imask;
|
||||||
hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
|
hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
|
||||||
clog(ep, hc);
|
clog(ep, hc);
|
||||||
wait:
|
r->haintmsk |= 1<<chan;
|
||||||
if(ep->ttype == Tbulk && dir == Epin)
|
fiunlock(ctlr);
|
||||||
i = chanwait(ep, ctlr, hc, Chhltd);
|
|
||||||
else if(ep->ttype == Tintr && (hc->hcsplt & Spltena))
|
i = chanwait(ep, ctlr, hc, imask);
|
||||||
i = chanwait(ep, ctlr, hc, Chhltd);
|
|
||||||
else
|
|
||||||
i = chanwait(ep, ctlr, hc, Chhltd|Nak);
|
|
||||||
clog(ep, hc);
|
clog(ep, hc);
|
||||||
if(hc->hcint != i){
|
hc->hcintmsk = 0;
|
||||||
dprint("chanwait intr %ux->%ux\n", i, hc->hcint);
|
|
||||||
if((i = hc->hcint) == 0)
|
|
||||||
goto wait;
|
|
||||||
}
|
|
||||||
hc->hcint = i;
|
hc->hcint = i;
|
||||||
|
|
||||||
if(hc->hcsplt & Spltena){
|
if(split){
|
||||||
hc->hcsplt &= ~Compsplt;
|
hc->hcsplt &= ~Compsplt;
|
||||||
qunlock(&ctlr->split);
|
qunlock(&ctlr->split);
|
||||||
|
poperror();
|
||||||
}
|
}
|
||||||
|
|
||||||
if((i & Xfercomp) == 0 && i != (Chhltd|Ack) && i != Chhltd){
|
if((i & Xfercomp) == 0 && i != (Chhltd|Ack) && i != Chhltd){
|
||||||
|
@ -530,6 +542,7 @@ eptrans(Ep *ep, int rw, void *a, long n)
|
||||||
hc = chanalloc(ep);
|
hc = chanalloc(ep);
|
||||||
if(waserror()){
|
if(waserror()){
|
||||||
ep->toggle[rw] = hc->hctsiz & Pid;
|
ep->toggle[rw] = hc->hctsiz & Pid;
|
||||||
|
chanhalt(ep, hc);
|
||||||
chanrelease(ep, hc);
|
chanrelease(ep, hc);
|
||||||
if(strcmp(up->errstr, Estalled) == 0)
|
if(strcmp(up->errstr, Estalled) == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -581,6 +594,7 @@ ctltrans(Ep *ep, uchar *req, long n)
|
||||||
}
|
}
|
||||||
hc = chanalloc(ep);
|
hc = chanalloc(ep);
|
||||||
if(waserror()){
|
if(waserror()){
|
||||||
|
chanhalt(ep, hc);
|
||||||
chanrelease(ep, hc);
|
chanrelease(ep, hc);
|
||||||
if(strcmp(up->errstr, Estalled) == 0)
|
if(strcmp(up->errstr, Estalled) == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -683,8 +697,34 @@ init(Hci *hp)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dump(Hci*)
|
dumphchan(Ctlr *ctlr, Hostchan *hc)
|
||||||
{
|
{
|
||||||
|
int chan = hc - ctlr->regs->hchan;
|
||||||
|
|
||||||
|
print("hchan[%d] hcchar %ux hcsplt %ux hcint %ux hcintmsk %ux hctsiz %ux hcdma %ux\n",
|
||||||
|
chan, hc->hcchar, hc->hcsplt, hc->hcint, hc->hcintmsk, hc->hctsiz, hc->hcdma);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dumpctlr(Ctlr *ctlr)
|
||||||
|
{
|
||||||
|
Dwcregs *r = ctlr->regs;
|
||||||
|
|
||||||
|
print("grxstsr %ux gnptxsts %ux hptxsts %ux\n",
|
||||||
|
r->grxstsr, r->gnptxsts, r->hptxsts);
|
||||||
|
print("gintsts %ux gintmsk %ux, haint %ux haintmsk %ux\n",
|
||||||
|
r->gintsts, r->gintmsk, r->haint, r->haintmsk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dump(Hci *hp)
|
||||||
|
{
|
||||||
|
Ctlr *ctlr = hp->aux;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
dumpctlr(ctlr);
|
||||||
|
for(i = 0; i < ctlr->nchan; i++)
|
||||||
|
dumphchan(ctlr, &ctlr->regs->hchan[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -703,6 +743,7 @@ fiqintr(Ureg*, void *a)
|
||||||
filock(ctlr);
|
filock(ctlr);
|
||||||
intr = r->gintsts;
|
intr = r->gintsts;
|
||||||
if(intr & Hcintr){
|
if(intr & Hcintr){
|
||||||
|
r->haintmsk &= ctlr->chanbusy;
|
||||||
haint = r->haint & r->haintmsk;
|
haint = r->haint & r->haintmsk;
|
||||||
for(i = 0; haint; i++){
|
for(i = 0; haint; i++){
|
||||||
if(haint & 1){
|
if(haint & 1){
|
||||||
|
@ -722,6 +763,7 @@ fiqintr(Ureg*, void *a)
|
||||||
ctlr->sofchan = 0;
|
ctlr->sofchan = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wakechan &= ctlr->chanbusy;
|
||||||
if(wakechan){
|
if(wakechan){
|
||||||
ctlr->wakechan |= wakechan;
|
ctlr->wakechan |= wakechan;
|
||||||
armtimerset(1);
|
armtimerset(1);
|
||||||
|
@ -742,6 +784,7 @@ irqintr(Ureg*, void *a)
|
||||||
wakechan = ctlr->wakechan;
|
wakechan = ctlr->wakechan;
|
||||||
ctlr->wakechan = 0;
|
ctlr->wakechan = 0;
|
||||||
fiunlock(ctlr);
|
fiunlock(ctlr);
|
||||||
|
wakechan &= ctlr->chanbusy;
|
||||||
for(i = 0; wakechan; i++){
|
for(i = 0; wakechan; i++){
|
||||||
if(wakechan & 1)
|
if(wakechan & 1)
|
||||||
wakeup(&ctlr->chanintr[i]);
|
wakeup(&ctlr->chanintr[i]);
|
||||||
|
@ -831,8 +874,8 @@ epread(Ep *ep, void *a, long n)
|
||||||
assert(((uintptr)p & (BLOCKALIGN-1)) == 0);
|
assert(((uintptr)p & (BLOCKALIGN-1)) == 0);
|
||||||
cachedinvse(p, n);
|
cachedinvse(p, n);
|
||||||
nr = eptrans(ep, Read, p, n);
|
nr = eptrans(ep, Read, p, n);
|
||||||
cachedinvse(p, nr);
|
|
||||||
epio->lastpoll = TK2MS(m->ticks);
|
epio->lastpoll = TK2MS(m->ticks);
|
||||||
|
cachedinvse(p, nr);
|
||||||
memmove(a, p, nr);
|
memmove(a, p, nr);
|
||||||
qunlock(q);
|
qunlock(q);
|
||||||
freeb(b);
|
freeb(b);
|
||||||
|
|
Loading…
Reference in a new issue