from richard miller: /n/sources/patch/applied/rpi-model-a-usb Empirical adjustments to usb for Raspberry Pi Model A (which has no built-in hub). Reference: /n/atom/patch/applied2013/rpia Date: Mon Jun 3 05:50:03 CES 2013 Signed-off-by: quanstro@quanstro.net --- /sys/src/9/bcm/usbdwc.c Mon Jun 3 05:48:57 2013 +++ /sys/src/9/bcm/usbdwc.c Mon Jun 3 05:48:57 2013 @@ -24,38 +24,37 @@ #include "dwcotg.h" -#define USBREGS (VIRTIO+0x980000) - enum { - Enabledelay = 50, - Resetdelay = 10, + USBREGS = VIRTIO + 0x980000, + Enabledelay = 50, + Resetdelay = 10, ResetdelayHS = 50, - Read = 0, - Write = 1, + Read = 0, + Write = 1, }; typedef struct Ctlr Ctlr; typedef struct Epio Epio; struct Ctlr { - Dwcregs *regs; /* controller registers */ - int nchan; /* number of host channels */ - ulong chanbusy; /* bitmap of in-use channels */ - QLock 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 */ - Rendez *chanintr; /* sleep till interrupt on channel N */ + Dwcregs *regs; /* controller registers */ + int nchan; /* number of host channels */ + ulong chanbusy; /* bitmap of in-use channels */ + QLock 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 */ + Rendez *chanintr; /* sleep till interrupt on channel N */ }; struct Epio { QLock; - Block *cb; - ulong lastpoll; + Block *cb; + ulong lastpoll; }; static Ctlr dwc; @@ -77,8 +76,8 @@ qlock(&ctlr->chanlock); bitmap = ctlr->chanbusy; for(i = 0; i < ctlr->nchan; i++) - if((bitmap&(1<chanbusy = bitmap|(1<chanbusy = bitmap | 1<chanlock); return &ctlr->regs->hchan[i]; } @@ -107,9 +106,9 @@ Ctlr *ctlr = ep->hp->aux; if(ep->debug) - ctlr->debugchan |= 1<<(hc - ctlr->regs->hchan); + ctlr->debugchan |= 1 << (hc - ctlr->regs->hchan); else - ctlr->debugchan &= ~(1<<(hc - ctlr->regs->hchan)); + ctlr->debugchan &= ~(1 << (hc - ctlr->regs->hchan)); switch(ep->dev->state){ case Dconfig: case Dreset: @@ -117,6 +116,7 @@ break; default: hcc = ep->dev->nb<maxpkt | 1<nb<ttype){ @@ -138,10 +138,15 @@ hcc |= Lspddev; /* fall through */ case Fullspeed: - hc->hcsplt = Spltena | POS_ALL | ep->dev->hub<dev->port; - break; + if(ep->dev->hub > 1){ + hc->hcsplt = Spltena | POS_ALL | ep->dev->hub<dev->port; + break; + } + /* fall through */ default: hc->hcsplt = 0; + break; } hc->hcchar = hcc; hc->hcint = ~0; @@ -153,7 +158,7 @@ Dwcregs *r; r = a; - return (r->gintsts & Sofintr); + return r->gintsts & Sofintr; } static void @@ -170,7 +175,7 @@ r->gintmsk |= Sofintr; sleep(&ctlr->chanintr[n], sofdone, r); splx(x); - }while((r->hfnum&7) == 6); + }while((r->hfnum & 7) == 6); } static int @@ -179,52 +184,62 @@ Hostchan *hc; hc = a; + if(hc->hcint == (Chhltd|Ack)) + return 0; return (hc->hcint & hc->hcintmsk) != 0; } static int chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask) { + int intr, n, x, ointr; ulong start, now; Dwcregs *r; - int intr, n, x; - int ointr; r = ctlr->regs; n = hc - r->hchan; for(;;){ - restart: +restart: x = splfhi(); r->haintmsk |= 1<hcintmsk = mask; sleep(&ctlr->chanintr[n], chandone, hc); hc->hcintmsk = 0; splx(x); - if(((intr = hc->hcint)&Chhltd) != 0) + intr = hc->hcint; + if(intr & Chhltd) return intr; start = fastticks(0); ointr = intr; now = start; do{ - if(((intr = hc->hcint)&Chhltd) != 0){ + intr = hc->hcint; + if(intr & Chhltd){ if((ointr != Ack && ointr != (Ack|Xfercomp)) || - intr != (Ack|Chhltd|Xfercomp) || (now - start) > 60) - dprint("await %x after %ld %x -> %x\n", mask, now - start, ointr, intr); + intr != (Ack|Chhltd|Xfercomp) || + (now - start) > 60) + dprint("await %x after %ld %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 & mask) == 0){ + dprint("ep%d.%d await %x intr %x -> %x\n", + ep->dev->nb, ep->nb, mask, ointr, intr); goto 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); + 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){ + 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); + print("ep%d.%d channel won't halt hcchar %8.8ux\n", + ep->dev->nb, ep->nb, hc->hcchar); break; } } @@ -252,7 +267,7 @@ return 0; }else return 0; - if(hc->hcchar&Chen){ + if(hc->hcchar & Chen){ iprint("hcchar %8.8ux hcint %8.8ux", hc->hcchar, hc->hcint); hc->hcchar |= Chen | Chdis; while(hc->hcchar&Chen) @@ -306,7 +321,8 @@ return; p = chanlog[0]; for(i = 0; i < nchanlog; i++){ - print("%5.5d.%5.5d %8.8ux %8.8ux %8.8ux %8.8ux\n", p[0]&0xFFFF, p[0]>>16, p[1], p[2], p[3], p[4]); + print("%5.5d.%5.5d %8.8ux %8.8ux %8.8ux %8.8ux\n", + p[0]&0xFFFF, p[0]>>16, p[1], p[2], p[3], p[4]); p += 5; } nchanlog = 0; @@ -340,17 +356,19 @@ hctsiz = hc->hctsiz; hc->hctsiz = hctsiz & ~Dopng; if(hc->hcchar&Chen){ - dprint("ep%d.%d before chanio hcchar=%8.8ux\n", ep->dev->nb, ep->nb, hc->hcchar); + dprint("ep%d.%d before chanio hcchar=%8.8ux\n", + ep->dev->nb, ep->nb, hc->hcchar); hc->hcchar |= Chen | Chdis; while(hc->hcchar&Chen) ; hc->hcint = Chhltd; } if((i = hc->hcint) != 0){ - dprint("ep%d.%d before chanio hcint=%8.8ux\n", ep->dev->nb, ep->nb, i); + dprint("ep%d.%d before chanio hcint=%8.8ux\n", + ep->dev->nb, ep->nb, i); hc->hcint = i; } - if(hc->hcsplt&Spltena){ + if(hc->hcsplt & Spltena){ qlock(&ctlr->split); sofwait(ctlr, hc - ctlr->regs->hchan); if((dwc.regs->hfnum & 1) == 0) @@ -361,72 +379,98 @@ hc->hcchar = (hc->hcchar &~ Chdis) | Chen; clog(ep, hc); if(ep->ttype == Tbulk && dir == Epin) - i = chanwait(ep, ctlr, hc, /* Ack| */Chhltd); - else if(ep->ttype == Tintr && (hc->hcsplt&Spltena)) + i = chanwait(ep, ctlr, hc, /* Ack| */ 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); hc->hcint = i; - if(hc->hcsplt&Spltena){ + if(hc->hcsplt & Spltena){ hc->hcsplt &= ~Compsplt; qunlock(&ctlr->split); } - if((i&Xfercomp) == 0 && i != (Chhltd|Ack) && i != Chhltd){ - if(i&Stall) + if((i & Xfercomp) == 0 && i != (Chhltd|Ack) && i != Chhltd){ + if(i & Stall) error(Estalled); - if(i&Nyet) + if(i & (Nyet|Frmovrun)) continue; - if(i&Nak){ + if(i & Nak){ if(ep->ttype == Tintr) tsleep(&up->sleep, return0, 0, ep->pollival); else tsleep(&up->sleep, return0, 0, 1); continue; } + logdump(ep); print("usbotg: ep%d.%d error intr %8.8ux\n", ep->dev->nb, ep->nb, i); - if(i&~(Chhltd|Ack)) + if(i & ~(Chhltd|Ack)) error(Eio); if(hc->hcdma != hcdma) - print("usbotg: weird hcdma %x->%x intr %x->%x\n", hcdma, hc->hcdma, i, hc->hcint); + print("usbotg: weird hcdma %x->%x intr %x->%x\n", + hcdma, hc->hcdma, i, hc->hcint); } n = hc->hcdma - hcdma; if(n == 0){ - if((hc->hctsiz&Pktcnt) != (hctsiz&Pktcnt)) + if((hc->hctsiz & Pktcnt) != (hctsiz & Pktcnt)) break; else continue; } if(dir == Epin && ep->ttype == Tbulk && n == nleft){ - nt = (hctsiz&Xfersize) - (hc->hctsiz&Xfersize); + nt = (hctsiz & Xfersize) - (hc->hctsiz & Xfersize); if(nt != n){ - if(n == ((nt+3)&~3)) + if(n == ROUND(nt, 4)) n = nt; else - print("usbotg: intr %8.8ux dma %8.8ux-%8.8ux hctsiz %8.8ux-%8.ux\n", - i, hcdma, hc->hcdma, hctsiz, hc->hctsiz); + print("usbotg: intr %8.8ux " + "dma %8.8ux-%8.8ux " + "hctsiz %8.8ux-%8.ux\n", + i, hcdma, hc->hcdma, hctsiz, + hc->hctsiz); } } if(n > nleft){ - if(n != ((nleft+3)&~3)) - dprint("too much: wanted %d got %d\n", len, len - nleft + n); + if(n != ROUND(nleft, 4)) + dprint("too much: wanted %d got %d\n", + len, len - nleft + n); n = nleft; } - if((nleft -= n) == 0 || (n % maxpkt) != 0) + nleft -= n; + if(nleft == 0 || (n % maxpkt) != 0) + break; + if((i & Xfercomp) && ep->ttype != Tctl) break; - if((i&Xfercomp) && ep->ttype != Tctl) - break; if(dir == Epout) - dprint("too little: nleft %d hcdma %x->%x hctsiz %x->%x intr %x\n", nleft, hcdma, hc->hcdma, hctsiz, hc->hctsiz, i); + dprint("too little: nleft %d hcdma %x->%x hctsiz %x->%x intr %x\n", + nleft, hcdma, hc->hcdma, hctsiz, hc->hctsiz, i); } logdump(ep); return len - nleft; } static long +multitrans(Ep *ep, Hostchan *hc, int rw, void *a, long n) +{ + long sofar, m; + + sofar = 0; + do{ + m = n - sofar; + if(m > ep->maxpkt) + m = ep->maxpkt; + m = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw], + (char*)a + sofar, m); + ep->toggle[rw] = hc->hctsiz & Pid; + sofar += m; + }while(sofar < n && m == ep->maxpkt); + return sofar; +} + +static long eptrans(Ep *ep, int rw, void *a, long n) { Hostchan *hc; @@ -447,20 +491,11 @@ nexterror(); } chansetup(hc, ep); - if(rw == Read && ep->ttype == Tbulk){ - long sofar, m; - sofar = 0; - do{ - m = n - sofar; - if(m > ep->maxpkt) - m = ep->maxpkt; - m = chanio(ep, hc, Epin, ep->toggle[rw], (char*)a + sofar, m); - ep->toggle[rw] = hc->hctsiz & Pid; - sofar += m; - }while(sofar < n && m == ep->maxpkt); - n = sofar; - }else{ - n = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw], a, n); + if(rw == Read && ep->ttype == Tbulk) + n = multitrans(ep, hc, rw, a, n); + else{ + n = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw], + a, n); ep->toggle[rw] = hc->hctsiz & Pid; } chanrelease(ep, hc); @@ -509,7 +544,11 @@ chansetup(hc, ep); chanio(ep, hc, Epout, SETUP, req, Rsetuplen); if(req[Rtype] & Rd2h){ - b->wp += chanio(ep, hc, Epin, DATA1, data, datalen); + if(ep->dev->hub <= 1){ + ep->toggle[Read] = DATA1; + b->wp += multitrans(ep, hc, Read, data, datalen); + }else + b->wp += chanio(ep, hc, Epin, DATA1, data, datalen); chanio(ep, hc, Epout, DATA1, nil, 0); n = Rsetuplen; }else{ @@ -548,7 +587,7 @@ greset(Dwcregs *r, int bits) { r->grstctl |= bits; - while(r->grstctl&bits) + while(r->grstctl & bits) ; microdelay(10); } @@ -563,7 +602,7 @@ ctlr = hp->aux; r = ctlr->regs; - ctlr->nchan = 1 + ((r->ghwcfg2&Num_host_chan) >> ONum_host_chan); + ctlr->nchan = 1 + ((r->ghwcfg2 & Num_host_chan) >> ONum_host_chan); ctlr->chanintr = malloc(ctlr->nchan * sizeof(Rendez)); r->gahbcfg = 0; @@ -577,14 +616,14 @@ tsleep(&up->sleep, return0, 0, 25); r->gahbcfg |= Dmaenable; - n = (r->ghwcfg3&Dfifo_depth)>>ODfifo_depth; + n = (r->ghwcfg3 & Dfifo_depth) >> ODfifo_depth; rx = 0x306; tx = 0x100; ptx = 0x200; r->grxfsiz = rx; r->gnptxfsiz = rx | tx<sleep, return0, 0, 1); - r->hptxfsiz = (rx+tx) | ptx<hptxfsiz = (rx + tx) | ptx << ODepth; greset(r, Rxfflsh); r->grstctl = TXF_ALL; greset(r, Txfflsh); @@ -665,7 +704,8 @@ static void epopen(Ep *ep) { - ddprint("usbotg: epopen ep%d.%d ttype %d\n", ep->dev->nb, ep->nb, ep->ttype); + ddprint("usbotg: epopen ep%d.%d ttype %d\n", + ep->dev->nb, ep->nb, ep->ttype); switch(ep->ttype){ case Tnone: error(Enotconfig); @@ -677,6 +717,7 @@ ep->toggle[Read] = DATA0; if(ep->toggle[Write] == 0) ep->toggle[Write] = DATA0; + break; } ep->aux = malloc(sizeof(Epio)); if(ep->aux == nil) @@ -686,13 +727,15 @@ static void epclose(Ep *ep) { - ddprint("usbotg: epclose ep%d.%d ttype %d\n", ep->dev->nb, ep->nb, ep->ttype); + ddprint("usbotg: epclose ep%d.%d ttype %d\n", + ep->dev->nb, ep->nb, ep->ttype); switch(ep->ttype){ case Tctl: freeb(((Epio*)ep->aux)->cb); /* fall through */ default: free(ep->aux); + break; } } @@ -830,10 +873,11 @@ r->hport0 = Prtpwr; tsleep(&up->sleep, return0, 0, Enabledelay); s = r->hport0; - if((b = (s&(Prtconndet|Prtenchng|Prtovrcurrchng))) != 0) + b = s & (Prtconndet|Prtenchng|Prtovrcurrchng); + if(b != 0) r->hport0 = Prtpwr | b; dprint("usbotg reset=%d; sts %#x\n", on, s); - if((s&Prtena) == 0) + if((s & Prtena) == 0) print("usbotg: host port not enabled after reset"); return 0; } @@ -849,7 +893,8 @@ ctlr = hp->aux; r = ctlr->regs; s = r->hport0; - if((b = (s&(Prtconndet|Prtenchng|Prtovrcurrchng))) != 0) + b = s & (Prtconndet|Prtenchng|Prtovrcurrchng); + if(b != 0) r->hport0 = Prtpwr | b; b = 0; if(s & Prtconnsts)