summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/src/9/bcm/usbdwc.c199
1 files changed, 121 insertions, 78 deletions
diff --git a/sys/src/9/bcm/usbdwc.c b/sys/src/9/bcm/usbdwc.c
index b41da760b..e38a30302 100644
--- a/sys/src/9/bcm/usbdwc.c
+++ b/sys/src/9/bcm/usbdwc.c
@@ -41,10 +41,9 @@ enum
* when the controller is reading a sequence of bulk input
* packets in DMA mode. Setting Slowbulkin=1 will avoid the
* lockup by reading packets individually with an interrupt
- * after each. More recent chips don't seem to exhibit the
- * problem, so it's probably safe to leave this off now.
+ * after each.
*/
- Slowbulkin = 0,
+ Slowbulkin = 1,
};
typedef struct Ctlr Ctlr;
@@ -54,13 +53,13 @@ struct Ctlr {
Lock;
Dwcregs *regs; /* controller registers */
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 */
QLock split; /* serialise split transactions */
int splitretry; /* count retries of Nyet */
- int sofchan; /* bitmap of channels waiting for sof */
- int wakechan; /* bitmap of channels to wakeup after fiq */
- int debugchan; /* bitmap of channels for interrupt debug */
+ uint sofchan; /* bitmap of channels waiting for sof */
+ uint wakechan; /* bitmap of channels to wakeup after fiq */
+ uint debugchan; /* bitmap of channels for interrupt debug */
Rendez *chanintr; /* sleep till interrupt on channel N */
};
@@ -75,13 +74,17 @@ struct Epio {
};
static Ctlr dwc;
-static int debug;
+static int debug = 0;
static char Ebadlen[] = "bad usb request length";
static void clog(Ep *ep, Hostchan *hc);
static void logdump(Ep *ep);
+static void dumpctlr(Ctlr *ctlr);
+static void dumphchan(Ctlr *ctlr, Hostchan *hc);
+static void dump(Hci *hp);
+
static void
filock(Lock *l)
{
@@ -102,7 +105,8 @@ static Hostchan*
chanalloc(Ep *ep)
{
Ctlr *ctlr;
- int bitmap, i;
+ int i;
+ uint bitmap;
static int first;
ctlr = ep->hp->aux;
@@ -123,13 +127,13 @@ retry:
}
static void
-chanrelease(Ep *ep, Hostchan *chan)
+chanrelease(Ep *ep, Hostchan *hc)
{
Ctlr *ctlr;
int i;
ctlr = ep->hp->aux;
- i = chan - ctlr->regs->hchan;
+ i = hc - ctlr->regs->hchan;
lock(&ctlr->chanlock);
ctlr->chanbusy &= ~(1<<i);
unlock(&ctlr->chanlock);
@@ -188,74 +192,68 @@ chansetup(Hostchan *hc, Ep *ep)
hc->hcint = ~0;
}
-static int
-sofdone(void *a)
+static void
+chanhalt(Ep *ep, Hostchan *hc)
{
- Dwcregs *r;
-
- r = a;
- return (r->gintmsk & Sofintr) == 0;
+ 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 void
-sofwait(Ctlr *ctlr, int n)
+static int
+sofdone(void *a)
{
- 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);
+ Ctlr *ctlr = a;
+ return ctlr->sofchan == 0;
}
static int
chandone(void *a)
{
Hostchan *hc;
+ int i;
hc = a;
- if(hc->hcint == (Chhltd|Ack))
+ i = hc->hcint;
+ if(i == (Chhltd|Ack))
return 0;
- return (hc->hcint & hc->hcintmsk) != 0;
+ return (i & hc->hcintmsk) != 0;
}
static int
chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
{
- int intr, n, ointr;
+ int intr, ointr, chan;
ulong start, now;
- Dwcregs *r;
- r = ctlr->regs;
- n = hc - r->hchan;
+ chan = hc - ctlr->regs->hchan;
for(;;){
restart:
- filock(ctlr);
- r->haintmsk |= 1<<n;
- hc->hcintmsk = mask;
- fiunlock(ctlr);
- tsleep(&ctlr->chanintr[n], chandone, hc, 1000);
+ tsleep(&ctlr->chanintr[chan], chandone, hc, 1000);
if((intr = hc->hcint) == 0)
goto restart;
- hc->hcintmsk = 0;
if(intr & Chhltd)
return intr;
- start = fastticks(0);
ointr = intr;
- now = start;
+ now = start = fastticks(0);
do{
intr = hc->hcint;
if(intr & Chhltd){
if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
intr != (Ack|Chhltd|Xfercomp) ||
(now - start) > 60)
- dprint("await %x after %ldµs %x -> %x\n",
- mask, now - start, ointr, intr);
+ dprint("ep%d.%d await %x after %ldµs %x -> %x\n",
+ ep->dev->nb, ep->nb, mask, now - start, ointr, intr);
return intr;
}
if((intr & mask) == 0){
@@ -266,21 +264,15 @@ restart:
}
now = fastticks(0);
}while(now - start < 100);
- dprint("ep%d.%d halting channel %8.8ux hcchar %8.8ux "
- "grxstsr %8.8ux gnptxsts %8.8ux hptxsts %8.8ux\n",
- ep->dev->nb, ep->nb, intr, hc->hcchar, r->grxstsr,
- r->gnptxsts, r->hptxsts);
- mask = Chhltd;
- 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;
- }
+ if(debug){
+ print("ep%d.%d halting chan %d intr %x\n",
+ ep->dev->nb, ep->nb, chan, intr);
+ dumphchan(ctlr, hc);
+ dumpctlr(ctlr);
}
+ chanhalt(ep, hc);
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)
{
Ctlr *ctlr;
- int nleft, n, nt, i, maxpkt, npkt;
+ int nleft, n, nt, i, imask, maxpkt, npkt, chan, split;
uint hcdma, hctsiz;
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->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;
logstart(ep);
for(;;){
+ Dwcregs *r;
+
hcdma = hc->hcdma;
hctsiz = hc->hctsiz;
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);
hc->hcint = i;
}
- if(hc->hcsplt & Spltena){
+
+ r = ctlr->regs;
+ chan = hc - r->hchan;
+ if(split){
qlock(&ctlr->split);
- sofwait(ctlr, hc - ctlr->regs->hchan);
- if((dwc.regs->hfnum & 1) == 0)
+ if(waserror()){
+ 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;
else
hc->hcchar |= Oddfrm;
+ } else {
+ filock(ctlr);
}
+ hc->hcintmsk = imask;
hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
clog(ep, hc);
-wait:
- if(ep->ttype == Tbulk && dir == Epin)
- i = chanwait(ep, ctlr, hc, Chhltd);
- else if(ep->ttype == Tintr && (hc->hcsplt & Spltena))
- i = chanwait(ep, ctlr, hc, Chhltd);
- else
- i = chanwait(ep, ctlr, hc, Chhltd|Nak);
+ r->haintmsk |= 1<<chan;
+ fiunlock(ctlr);
+
+ i = chanwait(ep, ctlr, hc, imask);
clog(ep, hc);
- if(hc->hcint != i){
- dprint("chanwait intr %ux->%ux\n", i, hc->hcint);
- if((i = hc->hcint) == 0)
- goto wait;
- }
+ hc->hcintmsk = 0;
hc->hcint = i;
- if(hc->hcsplt & Spltena){
+ if(split){
hc->hcsplt &= ~Compsplt;
qunlock(&ctlr->split);
+ poperror();
}
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);
if(waserror()){
ep->toggle[rw] = hc->hctsiz & Pid;
+ chanhalt(ep, hc);
chanrelease(ep, hc);
if(strcmp(up->errstr, Estalled) == 0)
return 0;
@@ -581,6 +594,7 @@ ctltrans(Ep *ep, uchar *req, long n)
}
hc = chanalloc(ep);
if(waserror()){
+ chanhalt(ep, hc);
chanrelease(ep, hc);
if(strcmp(up->errstr, Estalled) == 0)
return 0;
@@ -683,8 +697,34 @@ init(Hci *hp)
}
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
@@ -703,6 +743,7 @@ fiqintr(Ureg*, void *a)
filock(ctlr);
intr = r->gintsts;
if(intr & Hcintr){
+ r->haintmsk &= ctlr->chanbusy;
haint = r->haint & r->haintmsk;
for(i = 0; haint; i++){
if(haint & 1){
@@ -722,6 +763,7 @@ fiqintr(Ureg*, void *a)
ctlr->sofchan = 0;
}
}
+ wakechan &= ctlr->chanbusy;
if(wakechan){
ctlr->wakechan |= wakechan;
armtimerset(1);
@@ -742,6 +784,7 @@ irqintr(Ureg*, void *a)
wakechan = ctlr->wakechan;
ctlr->wakechan = 0;
fiunlock(ctlr);
+ wakechan &= ctlr->chanbusy;
for(i = 0; wakechan; i++){
if(wakechan & 1)
wakeup(&ctlr->chanintr[i]);
@@ -831,8 +874,8 @@ epread(Ep *ep, void *a, long n)
assert(((uintptr)p & (BLOCKALIGN-1)) == 0);
cachedinvse(p, n);
nr = eptrans(ep, Read, p, n);
- cachedinvse(p, nr);
epio->lastpoll = TK2MS(m->ticks);
+ cachedinvse(p, nr);
memmove(a, p, nr);
qunlock(q);
freeb(b);