summaryrefslogtreecommitdiff
path: root/sys/src/9/bcm/usbdwc.c
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2018-10-20 19:56:31 +0200
committercinap_lenrek <cinap_lenrek@felloff.net>2018-10-20 19:56:31 +0200
commit83e20b4df18d539db59c8e1090f77a6565df250e (patch)
treed42f2d4c7fdd8cb1526131515690bc9229150505 /sys/src/9/bcm/usbdwc.c
parent796e5e6000677a39577d545e4603ce251e7cbfe9 (diff)
bcm: import changes for raspi2/3 from richard miller
Diffstat (limited to 'sys/src/9/bcm/usbdwc.c')
-rw-r--r--sys/src/9/bcm/usbdwc.c172
1 files changed, 115 insertions, 57 deletions
diff --git a/sys/src/9/bcm/usbdwc.c b/sys/src/9/bcm/usbdwc.c
index a7292ba5f..b41da760b 100644
--- a/sys/src/9/bcm/usbdwc.c
+++ b/sys/src/9/bcm/usbdwc.c
@@ -33,16 +33,29 @@ enum
Read = 0,
Write = 1,
+
+ /*
+ * Workaround for an unexplained glitch where an Ack interrupt
+ * is received without Chhltd, whereupon all channels remain
+ * permanently busy and can't be halted. This was only seen
+ * 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.
+ */
+ Slowbulkin = 0,
};
typedef struct Ctlr Ctlr;
typedef struct Epio Epio;
struct Ctlr {
+ Lock;
Dwcregs *regs; /* controller registers */
int nchan; /* number of host channels */
ulong chanbusy; /* bitmap of in-use channels */
- QLock chanlock; /* serialise access to chanbusy */
+ 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 */
@@ -52,7 +65,11 @@ struct Ctlr {
};
struct Epio {
- QLock;
+ union {
+ QLock rlock;
+ QLock ctllock;
+ };
+ QLock wlock;
Block *cb;
ulong lastpoll;
};
@@ -61,29 +78,48 @@ static Ctlr dwc;
static int debug;
static char Ebadlen[] = "bad usb request length";
-static char Enotconfig[] = "usb endpoint not configured";
static void clog(Ep *ep, Hostchan *hc);
static void logdump(Ep *ep);
+static void
+filock(Lock *l)
+{
+ int x;
+
+ x = splfhi();
+ ilock(l);
+ l->sr = x;
+}
+
+static void
+fiunlock(Lock *l)
+{
+ iunlock(l);
+}
+
static Hostchan*
chanalloc(Ep *ep)
{
Ctlr *ctlr;
int bitmap, i;
+ static int first;
ctlr = ep->hp->aux;
- qlock(&ctlr->chanlock);
+retry:
+ lock(&ctlr->chanlock);
bitmap = ctlr->chanbusy;
for(i = 0; i < ctlr->nchan; i++)
if((bitmap & (1<<i)) == 0){
ctlr->chanbusy = bitmap | 1<<i;
- qunlock(&ctlr->chanlock);
+ unlock(&ctlr->chanlock);
return &ctlr->regs->hchan[i];
}
- qunlock(&ctlr->chanlock);
- panic("miller is a lazy git");
- return nil;
+ unlock(&ctlr->chanlock);
+ if(!first++)
+ print("usbdwc: all host channels busy - retrying\n");
+ tsleep(&up->sleep, return0, 0, 1);
+ goto retry;
}
static void
@@ -94,9 +130,9 @@ chanrelease(Ep *ep, Hostchan *chan)
ctlr = ep->hp->aux;
i = chan - ctlr->regs->hchan;
- qlock(&ctlr->chanlock);
+ lock(&ctlr->chanlock);
ctlr->chanbusy &= ~(1<<i);
- qunlock(&ctlr->chanlock);
+ unlock(&ctlr->chanlock);
}
static void
@@ -158,23 +194,22 @@ sofdone(void *a)
Dwcregs *r;
r = a;
- return r->gintsts & Sofintr;
+ return (r->gintmsk & Sofintr) == 0;
}
static void
sofwait(Ctlr *ctlr, int n)
{
Dwcregs *r;
- int x;
r = ctlr->regs;
do{
+ filock(ctlr);
r->gintsts = Sofintr;
- x = splfhi();
ctlr->sofchan |= 1<<n;
r->gintmsk |= Sofintr;
+ fiunlock(ctlr);
sleep(&ctlr->chanintr[n], sofdone, r);
- splx(x);
}while((r->hfnum & 7) == 6);
}
@@ -192,7 +227,7 @@ chandone(void *a)
static int
chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
{
- int intr, n, x, ointr;
+ int intr, n, ointr;
ulong start, now;
Dwcregs *r;
@@ -200,13 +235,14 @@ chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
n = hc - r->hchan;
for(;;){
restart:
- x = splfhi();
+ filock(ctlr);
r->haintmsk |= 1<<n;
hc->hcintmsk = mask;
- sleep(&ctlr->chanintr[n], chandone, hc);
+ fiunlock(ctlr);
+ tsleep(&ctlr->chanintr[n], chandone, hc, 1000);
+ if((intr = hc->hcint) == 0)
+ goto restart;
hc->hcintmsk = 0;
- splx(x);
- intr = hc->hcint;
if(intr & Chhltd)
return intr;
start = fastticks(0);
@@ -218,13 +254,14 @@ restart:
if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
intr != (Ack|Chhltd|Xfercomp) ||
(now - start) > 60)
- dprint("await %x after %ld %x -> %x\n",
+ dprint("await %x after %ldµs %x -> %x\n",
mask, now - start, ointr, intr);
return intr;
}
if((intr & mask) == 0){
- dprint("ep%d.%d await %x intr %x -> %x\n",
- ep->dev->nb, ep->nb, mask, ointr, intr);
+ if(intr != Nak)
+ dprint("ep%d.%d await %x after %ldµs intr %x -> %x\n",
+ ep->dev->nb, ep->nb, mask, now - start, ointr, intr);
goto restart;
}
now = fastticks(0);
@@ -254,6 +291,8 @@ chanintr(Ctlr *ctlr, int n)
int i;
hc = &ctlr->regs->hchan[n];
+ if((hc->hcint & hc->hcintmsk) == 0)
+ return 1;
if(ctlr->debugchan & (1<<n))
clog(nil, hc);
if((hc->hcsplt & Spltena) == 0)
@@ -347,7 +386,7 @@ chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
else
n = len;
hc->hctsiz = n | npkt<<OPktcnt | pid;
- hc->hcdma = PADDR(a);
+ hc->hcdma = dmaaddr(a);
nleft = len;
logstart(ep);
@@ -378,13 +417,19 @@ chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
}
hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
clog(ep, hc);
+wait:
if(ep->ttype == Tbulk && dir == Epin)
- i = chanwait(ep, ctlr, hc, /* Ack| */ Chhltd);
+ 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);
clog(ep, hc);
+ if(hc->hcint != i){
+ dprint("chanwait intr %ux->%ux\n", i, hc->hcint);
+ if((i = hc->hcint) == 0)
+ goto wait;
+ }
hc->hcint = i;
if(hc->hcsplt & Spltena){
@@ -405,12 +450,12 @@ chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
continue;
}
logdump(ep);
- print("usbotg: ep%d.%d error intr %8.8ux\n",
+ print("usbdwc: ep%d.%d error intr %8.8ux\n",
ep->dev->nb, ep->nb, i);
if(i & ~(Chhltd|Ack))
error(Eio);
if(hc->hcdma != hcdma)
- print("usbotg: weird hcdma %x->%x intr %x->%x\n",
+ print("usbdwc: weird hcdma %ux->%ux intr %ux->%ux\n",
hcdma, hc->hcdma, i, hc->hcint);
}
n = hc->hcdma - hcdma;
@@ -420,13 +465,13 @@ chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
else
continue;
}
- if(dir == Epin && ep->ttype == Tbulk && n == nleft){
+ if(dir == Epin && ep->ttype == Tbulk){
nt = (hctsiz & Xfersize) - (hc->hctsiz & Xfersize);
if(nt != n){
if(n == ROUND(nt, 4))
n = nt;
else
- print("usbotg: intr %8.8ux "
+ print("usbdwc: intr %8.8ux "
"dma %8.8ux-%8.8ux "
"hctsiz %8.8ux-%8.ux\n",
i, hcdma, hc->hcdma, hctsiz,
@@ -491,7 +536,7 @@ eptrans(Ep *ep, int rw, void *a, long n)
nexterror();
}
chansetup(hc, ep);
- if(rw == Read && ep->ttype == Tbulk)
+ if(Slowbulkin && rw == Read && ep->ttype == Tbulk)
n = multitrans(ep, hc, rw, a, n);
else{
n = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw],
@@ -524,8 +569,8 @@ ctltrans(Ep *ep, uchar *req, long n)
if(datalen <= 0 || datalen > Maxctllen)
error(Ebadlen);
/* XXX cache madness */
- epio->cb = b = allocb(ROUND(datalen, ep->maxpkt) + CACHELINESZ);
- b->wp = (uchar*)ROUND((uintptr)b->wp, CACHELINESZ);
+ epio->cb = b = allocb(ROUND(datalen, ep->maxpkt));
+ assert(((uintptr)b->wp & (BLOCKALIGN-1)) == 0);
memset(b->wp, 0x55, b->lim - b->wp);
cachedwbinvse(b->wp, b->lim - b->wp);
data = b->wp;
@@ -550,6 +595,7 @@ ctltrans(Ep *ep, uchar *req, long n)
}else
b->wp += chanio(ep, hc, Epin, DATA1, data, datalen);
chanio(ep, hc, Epout, DATA1, nil, 0);
+ cachedinvse(b->rp, BLEN(b));
n = Rsetuplen;
}else{
if(datalen > 0)
@@ -627,7 +673,7 @@ init(Hci *hp)
greset(r, Rxfflsh);
r->grstctl = TXF_ALL;
greset(r, Txfflsh);
- dprint("usbotg: FIFO depth %d sizes rx/nptx/ptx %8.8ux %8.8ux %8.8ux\n",
+ dprint("usbdwc: FIFO depth %d sizes rx/nptx/ptx %8.8ux %8.8ux %8.8ux\n",
n, r->grxfsiz, r->gnptxfsiz, r->hptxfsiz);
r->hport0 = Prtpwr|Prtconndet|Prtenchng|Prtovrcurrchng;
@@ -654,6 +700,7 @@ fiqintr(Ureg*, void *a)
ctlr = hp->aux;
r = ctlr->regs;
wakechan = 0;
+ filock(ctlr);
intr = r->gintsts;
if(intr & Hcintr){
haint = r->haint & r->haintmsk;
@@ -679,6 +726,7 @@ fiqintr(Ureg*, void *a)
ctlr->wakechan |= wakechan;
armtimerset(1);
}
+ fiunlock(ctlr);
}
static void
@@ -686,14 +734,14 @@ irqintr(Ureg*, void *a)
{
Ctlr *ctlr;
uint wakechan;
- int i, x;
+ int i;
ctlr = a;
- x = splfhi();
+ filock(ctlr);
armtimerset(0);
wakechan = ctlr->wakechan;
ctlr->wakechan = 0;
- splx(x);
+ fiunlock(ctlr);
for(i = 0; wakechan; i++){
if(wakechan & 1)
wakeup(&ctlr->chanintr[i]);
@@ -704,11 +752,12 @@ irqintr(Ureg*, void *a)
static void
epopen(Ep *ep)
{
- ddprint("usbotg: epopen ep%d.%d ttype %d\n",
+ ddprint("usbdwc: epopen ep%d.%d ttype %d\n",
ep->dev->nb, ep->nb, ep->ttype);
switch(ep->ttype){
- case Tnone:
- error(Enotconfig);
+ default:
+ error("endpoint type not supported");
+ return;
case Tintr:
assert(ep->pollival > 0);
/* fall through */
@@ -717,6 +766,8 @@ epopen(Ep *ep)
ep->toggle[Read] = DATA0;
if(ep->toggle[Write] == 0)
ep->toggle[Write] = DATA0;
+ /* fall through */
+ case Tctl:
break;
}
ep->aux = malloc(sizeof(Epio));
@@ -727,7 +778,7 @@ epopen(Ep *ep)
static void
epclose(Ep *ep)
{
- ddprint("usbotg: epclose ep%d.%d ttype %d\n",
+ ddprint("usbdwc: epclose ep%d.%d ttype %d\n",
ep->dev->nb, ep->nb, ep->ttype);
switch(ep->ttype){
case Tctl:
@@ -743,6 +794,7 @@ static long
epread(Ep *ep, void *a, long n)
{
Epio *epio;
+ QLock *q;
Block *b;
uchar *p;
ulong elapsed;
@@ -750,10 +802,11 @@ epread(Ep *ep, void *a, long n)
ddprint("epread ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
epio = ep->aux;
+ q = ep->ttype == Tctl? &epio->ctllock : &epio->rlock;
b = nil;
- qlock(epio);
+ qlock(q);
if(waserror()){
- qunlock(epio);
+ qunlock(q);
if(b)
freeb(b);
nexterror();
@@ -763,7 +816,7 @@ epread(Ep *ep, void *a, long n)
error(Egreg);
case Tctl:
nr = ctldata(ep, a, n);
- qunlock(epio);
+ qunlock(q);
poperror();
return nr;
case Tintr:
@@ -773,13 +826,15 @@ epread(Ep *ep, void *a, long n)
/* fall through */
case Tbulk:
/* XXX cache madness */
- b = allocb(ROUND(n, ep->maxpkt) + CACHELINESZ);
- p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
- cachedwbinvse(p, n);
+ b = allocb(ROUND(n, ep->maxpkt));
+ p = b->rp;
+ assert(((uintptr)p & (BLOCKALIGN-1)) == 0);
+ cachedinvse(p, n);
nr = eptrans(ep, Read, p, n);
+ cachedinvse(p, nr);
epio->lastpoll = TK2MS(m->ticks);
memmove(a, p, nr);
- qunlock(epio);
+ qunlock(q);
freeb(b);
poperror();
return nr;
@@ -790,16 +845,18 @@ static long
epwrite(Ep *ep, void *a, long n)
{
Epio *epio;
+ QLock *q;
Block *b;
uchar *p;
ulong elapsed;
ddprint("epwrite ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
epio = ep->aux;
+ q = ep->ttype == Tctl? &epio->ctllock : &epio->wlock;
b = nil;
- qlock(epio);
+ qlock(q);
if(waserror()){
- qunlock(epio);
+ qunlock(q);
if(b)
freeb(b);
nexterror();
@@ -815,8 +872,9 @@ epwrite(Ep *ep, void *a, long n)
case Tctl:
case Tbulk:
/* XXX cache madness */
- b = allocb(n + CACHELINESZ);
- p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
+ b = allocb(n);
+ p = b->wp;
+ assert(((uintptr)p & (BLOCKALIGN-1)) == 0);
memmove(p, a, n);
cachedwbse(p, n);
if(ep->ttype == Tctl)
@@ -825,7 +883,7 @@ epwrite(Ep *ep, void *a, long n)
n = eptrans(ep, Write, p, n);
epio->lastpoll = TK2MS(m->ticks);
}
- qunlock(epio);
+ qunlock(q);
freeb(b);
poperror();
return n;
@@ -847,11 +905,11 @@ portenable(Hci *hp, int port, int on)
assert(port == 1);
ctlr = hp->aux;
r = ctlr->regs;
- dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
+ dprint("usbdwc enable=%d; sts %#x\n", on, r->hport0);
if(!on)
r->hport0 = Prtpwr | Prtena;
tsleep(&up->sleep, return0, 0, Enabledelay);
- dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
+ dprint("usbdwc enable=%d; sts %#x\n", on, r->hport0);
return 0;
}
@@ -865,7 +923,7 @@ portreset(Hci *hp, int port, int on)
assert(port == 1);
ctlr = hp->aux;
r = ctlr->regs;
- dprint("usbotg reset=%d; sts %#x\n", on, r->hport0);
+ dprint("usbdwc reset=%d; sts %#x\n", on, r->hport0);
if(!on)
return 0;
r->hport0 = Prtpwr | Prtrst;
@@ -876,9 +934,9 @@ portreset(Hci *hp, int port, int on)
b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
if(b != 0)
r->hport0 = Prtpwr | b;
- dprint("usbotg reset=%d; sts %#x\n", on, s);
+ dprint("usbdwc reset=%d; sts %#x\n", on, s);
if((s & Prtena) == 0)
- print("usbotg: host port not enabled after reset");
+ print("usbdwc: host port not enabled after reset");
return 0;
}
@@ -948,7 +1006,7 @@ reset(Hci *hp)
id = ctlr->regs->gsnpsid;
if((id>>16) != ('O'<<8 | 'T'))
return -1;
- dprint("usbotg: rev %d.%3.3x\n", (id>>12)&0xF, id&0xFFF);
+ dprint("usbdwc: rev %d.%3.3x\n", (id>>12)&0xF, id&0xFFF);
intrenable(IRQtimerArm, irqintr, ctlr, 0, "dwc");