diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2018-10-20 19:56:31 +0200 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2018-10-20 19:56:31 +0200 |
commit | 83e20b4df18d539db59c8e1090f77a6565df250e (patch) | |
tree | d42f2d4c7fdd8cb1526131515690bc9229150505 /sys/src/9/bcm/usbdwc.c | |
parent | 796e5e6000677a39577d545e4603ce251e7cbfe9 (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.c | 172 |
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"); |