2 * USB host driver for BCM2835
3 * Synopsis DesignWare Core USB 2.0 OTG controller
5 * Copyright © 2012 Richard Miller <r.miller@acm.org>
7 * This is work in progress:
8 * - no isochronous pipes
9 * - no bandwidth budgeting
10 * - frame scheduling is crude
11 * - error handling is overly optimistic
12 * It should be just about adequate for a Plan 9 terminal with
13 * keyboard, mouse, ethernet adapter, and an external flash drive.
17 #include "../port/lib.h"
22 #include "../port/error.h"
23 #include "../port/usb.h"
29 USBREGS = VIRTIO + 0x980000,
38 typedef struct Ctlr Ctlr;
39 typedef struct Epio Epio;
42 Dwcregs *regs; /* controller registers */
43 int nchan; /* number of host channels */
44 ulong chanbusy; /* bitmap of in-use channels */
45 QLock chanlock; /* serialise access to chanbusy */
46 QLock split; /* serialise split transactions */
47 int splitretry; /* count retries of Nyet */
48 int sofchan; /* bitmap of channels waiting for sof */
49 int wakechan; /* bitmap of channels to wakeup after fiq */
50 int debugchan; /* bitmap of channels for interrupt debug */
51 Rendez *chanintr; /* sleep till interrupt on channel N */
63 static char Ebadlen[] = "bad usb request length";
64 static char Enotconfig[] = "usb endpoint not configured";
66 static void clog(Ep *ep, Hostchan *hc);
67 static void logdump(Ep *ep);
76 qlock(&ctlr->chanlock);
77 bitmap = ctlr->chanbusy;
78 for(i = 0; i < ctlr->nchan; i++)
79 if((bitmap & (1<<i)) == 0){
80 ctlr->chanbusy = bitmap | 1 << i;
81 qunlock(&ctlr->chanlock);
82 return &ctlr->regs->hchan[i];
84 qunlock(&ctlr->chanlock);
85 panic("miller is a lazy git");
90 chanrelease(Ep *ep, Hostchan *chan)
96 i = chan - ctlr->regs->hchan;
97 qlock(&ctlr->chanlock);
98 ctlr->chanbusy &= ~(1 << i);
99 qunlock(&ctlr->chanlock);
103 chansetup(Hostchan *hc, Ep *ep)
106 Ctlr *ctlr = ep->hp->aux;
109 ctlr->debugchan |= 1 << (hc - ctlr->regs->hchan);
111 ctlr->debugchan &= ~(1 << (hc - ctlr->regs->hchan));
112 switch(ep->dev->state){
118 hcc = ep->dev->nb<<ODevaddr;
121 hcc |= ep->maxpkt | 1<<OMulticnt | ep->nb<<OEpnum;
136 switch(ep->dev->speed){
141 hc->hcsplt = Spltena | POS_ALL | ep->dev->hub << OHubaddr |
158 return r->gintsts & Sofintr;
162 sofwait(Ctlr *ctlr, int n)
169 r->gintsts = Sofintr;
171 ctlr->sofchan |= 1 << n;
172 r->gintmsk |= Sofintr;
173 sleep(&ctlr->chanintr[n], sofdone, r);
175 }while((r->hfnum & 7) == 6);
184 return (hc->hcint & hc->hcintmsk) != 0;
188 chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
190 int intr, n, x, ointr;
199 r->haintmsk |= 1 << n;
201 sleep(&ctlr->chanintr[n], chandone, hc);
207 start = fastticks(0);
213 if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
214 intr != (Ack|Chhltd|Xfercomp) ||
216 dprint("await %x after %ld %x -> %x\n",
217 mask, now - start, ointr, intr);
220 if((intr & mask) == 0){
221 dprint("ep%d.%d await %x intr %x -> %x\n", ep->dev->nb, ep->nb, mask, ointr, intr);
225 }while(now - start < 100);
226 dprint("ep%d.%d halting channel %8.8ux hcchar %8.8ux "
227 "grxstsr %8.8ux gnptxsts %8.8ux hptxsts %8.8ux\n",
228 ep->dev->nb, ep->nb, intr, hc->hcchar, r->grxstsr,
229 r->gnptxsts, r->hptxsts);
233 while(hc->hcchar & Chen){
234 if(m->ticks - start >= 100){
235 print("ep%d.%d channel won't halt hcchar %8.8ux\n",
236 ep->dev->nb, ep->nb, hc->hcchar);
245 chanintr(Ctlr *ctlr, int n)
250 hc = &ctlr->regs->hchan[n];
251 if(ctlr->debugchan & (1 << n))
253 if((hc->hcsplt & Spltena) == 0)
256 if(i == (Chhltd|Ack)){
257 hc->hcsplt |= Compsplt;
258 ctlr->splitretry = 0;
259 }else if(i == (Chhltd|Nyet)){
260 if(++ctlr->splitretry >= 3)
264 if(hc->hcchar & Chen){
265 iprint("hcchar %8.8ux hcint %8.8ux", hc->hcchar, hc->hcint);
266 hc->hcchar |= Chen | Chdis;
267 while(hc->hcchar&Chen)
269 iprint(" %8.8ux\n", hc->hcint);
272 if(ctlr->regs->hfnum & 1)
273 hc->hcchar &= ~Oddfrm;
275 hc->hcchar |= Oddfrm;
276 hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
280 static Reg chanlog[32][5];
291 clog(Ep *ep, Hostchan *hc)
295 if(ep != nil && !ep->debug)
299 p = chanlog[nchanlog];
300 p[0] = dwc.regs->hfnum;
317 for(i = 0; i < nchanlog; i++){
318 print("%5.5d.%5.5d %8.8ux %8.8ux %8.8ux %8.8ux\n",
319 p[0]&0xFFFF, p[0]>>16, p[1], p[2], p[3], p[4]);
326 chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
329 int nleft, n, nt, i, maxpkt, npkt;
334 npkt = HOWMANY(len, ep->maxpkt);
338 hc->hcchar = (hc->hcchar & ~Epdir) | dir;
340 n = ROUND(len, ep->maxpkt);
343 hc->hctsiz = n | npkt << OPktcnt | pid;
344 hc->hcdma = PADDR(a);
351 hc->hctsiz = hctsiz & ~Dopng;
353 dprint("ep%d.%d before chanio hcchar=%8.8ux\n",
354 ep->dev->nb, ep->nb, hc->hcchar);
355 hc->hcchar |= Chen | Chdis;
356 while(hc->hcchar&Chen)
360 if((i = hc->hcint) != 0){
361 dprint("ep%d.%d before chanio hcint=%8.8ux\n",
362 ep->dev->nb, ep->nb, i);
365 if(hc->hcsplt & Spltena){
367 sofwait(ctlr, hc - ctlr->regs->hchan);
368 if((dwc.regs->hfnum & 1) == 0)
369 hc->hcchar &= ~Oddfrm;
371 hc->hcchar |= Oddfrm;
373 hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
375 if(ep->ttype == Tbulk && dir == Epin)
376 i = chanwait(ep, ctlr, hc, /* Ack| */ Chhltd);
377 else if(ep->ttype == Tintr && (hc->hcsplt & Spltena))
378 i = chanwait(ep, ctlr, hc, Chhltd);
380 i = chanwait(ep, ctlr, hc, Chhltd|Nak);
384 if(hc->hcsplt & Spltena){
385 hc->hcsplt &= ~Compsplt;
386 qunlock(&ctlr->split);
389 if((i & Xfercomp) == 0 && i != (Chhltd|Ack) && i != Chhltd){
395 if(ep->ttype == Tintr)
396 tsleep(&up->sleep, return0, 0, ep->pollival);
398 tsleep(&up->sleep, return0, 0, 1);
401 print("usbotg: ep%d.%d error intr %8.8ux\n",
402 ep->dev->nb, ep->nb, i);
403 if(i & ~(Chhltd|Ack))
405 if(hc->hcdma != hcdma)
406 print("usbotg: weird hcdma %x->%x intr %x->%x\n",
407 hcdma, hc->hcdma, i, hc->hcint);
409 n = hc->hcdma - hcdma;
411 if((hc->hctsiz & Pktcnt) != (hctsiz & Pktcnt))
415 if(dir == Epin && ep->ttype == Tbulk && n == nleft){
416 nt = (hctsiz & Xfersize) - (hc->hctsiz & Xfersize);
418 if(n == ((nt+3) & ~3))
421 print("usbotg: intr %8.8ux dma "
422 "%8.8ux-%8.8ux hctsiz "
424 i, hcdma, hc->hcdma, hctsiz,
428 if(n != ((nleft+3) & ~3))
429 dprint("too much: wanted %d got %d\n",
430 len, len - nleft + n);
434 if(nleft == 0 || n % maxpkt != 0)
436 if((i & Xfercomp) && ep->ttype != Tctl)
439 dprint("too little: nleft %d hcdma %x->%x hctsiz %x->%x intr %x\n",
440 nleft, hcdma, hc->hcdma, hctsiz, hc->hctsiz, i);
447 eptrans(Ep *ep, int rw, void *a, long n)
453 if(ep->mode != OREAD)
454 ep->toggle[Write] = DATA0;
455 if(ep->mode != OWRITE)
456 ep->toggle[Read] = DATA0;
460 ep->toggle[rw] = hc->hctsiz & Pid;
462 if(strcmp(up->errstr, Estalled) == 0)
467 if(rw == Read && ep->ttype == Tbulk){
475 m = chanio(ep, hc, Epin, ep->toggle[rw],
476 (char*)a + sofar, m);
477 ep->toggle[rw] = hc->hctsiz & Pid;
479 }while(sofar < n && m == ep->maxpkt);
482 n = chanio(ep, hc, rw == Read? Epin: Epout, ep->toggle[rw],
484 ep->toggle[rw] = hc->hctsiz & Pid;
492 ctltrans(Ep *ep, uchar *req, long n)
507 if(req[Rtype] & Rd2h){
508 datalen = GET2(req+Rcount);
509 if(datalen <= 0 || datalen > Maxctllen)
511 /* XXX cache madness */
512 epio->cb = b = allocb(ROUND(datalen, ep->maxpkt) + CACHELINESZ);
513 b->wp = (uchar*)ROUND((uintptr)b->wp, CACHELINESZ);
514 memset(b->wp, 0x55, b->lim - b->wp);
515 cachedwbinvse(b->wp, b->lim - b->wp);
519 datalen = n - Rsetuplen;
520 data = req + Rsetuplen;
525 if(strcmp(up->errstr, Estalled) == 0)
530 chanio(ep, hc, Epout, SETUP, req, Rsetuplen);
531 if(req[Rtype] & Rd2h){
532 b->wp += chanio(ep, hc, Epin, DATA1, data, datalen);
533 chanio(ep, hc, Epout, DATA1, nil, 0);
537 chanio(ep, hc, Epout, DATA1, data, datalen);
538 chanio(ep, hc, Epin, DATA1, nil, 0);
539 n = Rsetuplen + datalen;
547 ctldata(Ep *ep, void *a, long n)
558 memmove(a, b->rp, n);
568 greset(Dwcregs *r, int bits)
571 while(r->grstctl & bits)
586 ctlr->nchan = 1 + ((r->ghwcfg2 & Num_host_chan) >> ONum_host_chan);
587 ctlr->chanintr = malloc(ctlr->nchan * sizeof(Rendez));
590 setpower(PowerUsb, 1);
592 while((r->grstctl&Ahbidle) == 0)
596 r->gusbcfg |= Force_host_mode;
597 tsleep(&up->sleep, return0, 0, 25);
598 r->gahbcfg |= Dmaenable;
600 n = (r->ghwcfg3 & Dfifo_depth) >> ODfifo_depth;
605 r->gnptxfsiz = rx | tx << ODepth;
606 tsleep(&up->sleep, return0, 0, 1);
607 r->hptxfsiz = (rx + tx) | ptx << ODepth;
609 r->grstctl = TXF_ALL;
611 dprint("usbotg: FIFO depth %d sizes rx/nptx/ptx %8.8ux %8.8ux %8.8ux\n",
612 n, r->grxfsiz, r->gnptxfsiz, r->hptxfsiz);
614 r->hport0 = Prtpwr|Prtconndet|Prtenchng|Prtovrcurrchng;
617 r->gahbcfg |= Glblintrmsk;
626 fiqintr(Ureg*, void *a)
631 uint intr, haint, wakechan;
640 haint = r->haint & r->haintmsk;
641 for(i = 0; haint; i++){
642 if(haint & 1 && chanintr(ctlr, i) == 0){
643 r->haintmsk &= ~(1 << i);
650 r->gintsts = Sofintr;
651 if((r->hfnum&7) != 6){
652 r->gintmsk &= ~Sofintr;
653 wakechan |= ctlr->sofchan;
658 ctlr->wakechan |= wakechan;
664 irqintr(Ureg*, void *a)
673 wakechan = ctlr->wakechan;
676 for(i = 0; wakechan; i++){
678 wakeup(&ctlr->chanintr[i]);
686 ddprint("usbotg: epopen ep%d.%d ttype %d\n",
687 ep->dev->nb, ep->nb, ep->ttype);
692 assert(ep->pollival > 0);
695 if(ep->toggle[Read] == 0)
696 ep->toggle[Read] = DATA0;
697 if(ep->toggle[Write] == 0)
698 ep->toggle[Write] = DATA0;
701 ep->aux = malloc(sizeof(Epio));
709 ddprint("usbotg: epclose ep%d.%d ttype %d\n",
710 ep->dev->nb, ep->nb, ep->ttype);
713 freeb(((Epio*)ep->aux)->cb);
722 epread(Ep *ep, void *a, long n)
730 ddprint("epread ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
744 nr = ctldata(ep, a, n);
749 elapsed = TK2MS(m->ticks) - epio->lastpoll;
750 if(elapsed < ep->pollival)
751 tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
754 /* XXX cache madness */
755 b = allocb(ROUND(n, ep->maxpkt) + CACHELINESZ);
756 p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
758 nr = eptrans(ep, Read, p, n);
759 epio->lastpoll = TK2MS(m->ticks);
769 epwrite(Ep *ep, void *a, long n)
776 ddprint("epwrite ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
790 elapsed = TK2MS(m->ticks) - epio->lastpoll;
791 if(elapsed < ep->pollival)
792 tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
796 /* XXX cache madness */
797 b = allocb(n + CACHELINESZ);
798 p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
801 if(ep->ttype == Tctl)
802 n = ctltrans(ep, p, n);
804 n = eptrans(ep, Write, p, n);
805 epio->lastpoll = TK2MS(m->ticks);
815 seprintep(char *s, char*, Ep*)
821 portenable(Hci *hp, int port, int on)
829 dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
831 r->hport0 = Prtpwr | Prtena;
832 tsleep(&up->sleep, return0, 0, Enabledelay);
833 dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
838 portreset(Hci *hp, int port, int on)
847 dprint("usbotg reset=%d; sts %#x\n", on, r->hport0);
850 r->hport0 = Prtpwr | Prtrst;
851 tsleep(&up->sleep, return0, 0, ResetdelayHS);
853 tsleep(&up->sleep, return0, 0, Enabledelay);
855 b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
857 r->hport0 = Prtpwr | b;
858 dprint("usbotg reset=%d; sts %#x\n", on, s);
859 if((s & Prtena) == 0)
860 print("usbotg: host port not enabled after reset");
865 portstatus(Hci *hp, int port)
875 b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
877 r->hport0 = Prtpwr | b;
887 if(s & Prtovrcurract)
912 setdebug(Hci*, int d)
924 if(ctlr->regs != nil)
926 ctlr->regs = (Dwcregs*)USBREGS;
927 id = ctlr->regs->gsnpsid;
928 if((id>>16) != ('O'<<8 | 'T'))
930 dprint("usbotg: rev %d.%3.3x\n", (id>>12)&0xF, id&0xFFF);
932 intrenable(IRQtimerArm, irqintr, ctlr, 0, "dwc");
943 hp->interrupt = fiqintr;
945 hp->epclose = epclose;
947 hp->epwrite = epwrite;
948 hp->seprintep = seprintep;
949 hp->portenable = portenable;
950 hp->portreset = portreset;
951 hp->portstatus = portstatus;
952 hp->shutdown = shutdown;
953 hp->debug = setdebug;
956 intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, "usbdwcotg");
964 addhcitype("dwcotg", reset);