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 if(ep->dev->hub > 1){
142 hc->hcsplt = Spltena | POS_ALL | ep->dev->hub<<OHubaddr |
161 return r->gintsts & Sofintr;
165 sofwait(Ctlr *ctlr, int n)
172 r->gintsts = Sofintr;
174 ctlr->sofchan |= 1<<n;
175 r->gintmsk |= Sofintr;
176 sleep(&ctlr->chanintr[n], sofdone, r);
178 }while((r->hfnum & 7) == 6);
187 if(hc->hcint == (Chhltd|Ack))
189 return (hc->hcint & hc->hcintmsk) != 0;
193 chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
195 int intr, n, x, ointr;
206 sleep(&ctlr->chanintr[n], chandone, hc);
212 start = fastticks(0);
218 if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
219 intr != (Ack|Chhltd|Xfercomp) ||
221 dprint("await %x after %ld %x -> %x\n",
222 mask, now - start, ointr, intr);
225 if((intr & mask) == 0){
226 dprint("ep%d.%d await %x intr %x -> %x\n",
227 ep->dev->nb, ep->nb, mask, ointr, intr);
231 }while(now - start < 100);
232 dprint("ep%d.%d halting channel %8.8ux hcchar %8.8ux "
233 "grxstsr %8.8ux gnptxsts %8.8ux hptxsts %8.8ux\n",
234 ep->dev->nb, ep->nb, intr, hc->hcchar, r->grxstsr,
235 r->gnptxsts, r->hptxsts);
239 while(hc->hcchar & Chen){
240 if(m->ticks - start >= 100){
241 print("ep%d.%d channel won't halt hcchar %8.8ux\n",
242 ep->dev->nb, ep->nb, hc->hcchar);
251 chanintr(Ctlr *ctlr, int n)
256 hc = &ctlr->regs->hchan[n];
257 if(ctlr->debugchan & (1<<n))
259 if((hc->hcsplt & Spltena) == 0)
262 if(i == (Chhltd|Ack)){
263 hc->hcsplt |= Compsplt;
264 ctlr->splitretry = 0;
265 }else if(i == (Chhltd|Nyet)){
266 if(++ctlr->splitretry >= 3)
270 if(hc->hcchar & Chen){
271 iprint("hcchar %8.8ux hcint %8.8ux", hc->hcchar, hc->hcint);
272 hc->hcchar |= Chen | Chdis;
273 while(hc->hcchar&Chen)
275 iprint(" %8.8ux\n", hc->hcint);
278 if(ctlr->regs->hfnum & 1)
279 hc->hcchar &= ~Oddfrm;
281 hc->hcchar |= Oddfrm;
282 hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
286 static Reg chanlog[32][5];
297 clog(Ep *ep, Hostchan *hc)
301 if(ep != nil && !ep->debug)
305 p = chanlog[nchanlog];
306 p[0] = dwc.regs->hfnum;
323 for(i = 0; i < nchanlog; i++){
324 print("%5.5d.%5.5d %8.8ux %8.8ux %8.8ux %8.8ux\n",
325 p[0]&0xFFFF, p[0]>>16, p[1], p[2], p[3], p[4]);
332 chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
335 int nleft, n, nt, i, maxpkt, npkt;
340 npkt = HOWMANY(len, ep->maxpkt);
344 hc->hcchar = (hc->hcchar & ~Epdir) | dir;
346 n = ROUND(len, ep->maxpkt);
349 hc->hctsiz = n | npkt<<OPktcnt | pid;
350 hc->hcdma = PADDR(a);
357 hc->hctsiz = hctsiz & ~Dopng;
359 dprint("ep%d.%d before chanio hcchar=%8.8ux\n",
360 ep->dev->nb, ep->nb, hc->hcchar);
361 hc->hcchar |= Chen | Chdis;
362 while(hc->hcchar&Chen)
366 if((i = hc->hcint) != 0){
367 dprint("ep%d.%d before chanio hcint=%8.8ux\n",
368 ep->dev->nb, ep->nb, i);
371 if(hc->hcsplt & Spltena){
373 sofwait(ctlr, hc - ctlr->regs->hchan);
374 if((dwc.regs->hfnum & 1) == 0)
375 hc->hcchar &= ~Oddfrm;
377 hc->hcchar |= Oddfrm;
379 hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
381 if(ep->ttype == Tbulk && dir == Epin)
382 i = chanwait(ep, ctlr, hc, /* Ack| */ Chhltd);
383 else if(ep->ttype == Tintr && (hc->hcsplt & Spltena))
384 i = chanwait(ep, ctlr, hc, Chhltd);
386 i = chanwait(ep, ctlr, hc, Chhltd|Nak);
390 if(hc->hcsplt & Spltena){
391 hc->hcsplt &= ~Compsplt;
392 qunlock(&ctlr->split);
395 if((i & Xfercomp) == 0 && i != (Chhltd|Ack) && i != Chhltd){
398 if(i & (Nyet|Frmovrun))
401 if(ep->ttype == Tintr)
402 tsleep(&up->sleep, return0, 0, ep->pollival);
404 tsleep(&up->sleep, return0, 0, 1);
408 print("usbotg: ep%d.%d error intr %8.8ux\n",
409 ep->dev->nb, ep->nb, i);
410 if(i & ~(Chhltd|Ack))
412 if(hc->hcdma != hcdma)
413 print("usbotg: weird hcdma %x->%x intr %x->%x\n",
414 hcdma, hc->hcdma, i, hc->hcint);
416 n = hc->hcdma - hcdma;
418 if((hc->hctsiz & Pktcnt) != (hctsiz & Pktcnt))
423 if(dir == Epin && ep->ttype == Tbulk && n == nleft){
424 nt = (hctsiz & Xfersize) - (hc->hctsiz & Xfersize);
426 if(n == ROUND(nt, 4))
429 print("usbotg: intr %8.8ux "
431 "hctsiz %8.8ux-%8.ux\n",
432 i, hcdma, hc->hcdma, hctsiz,
437 if(n != ROUND(nleft, 4))
438 dprint("too much: wanted %d got %d\n",
439 len, len - nleft + n);
443 if(nleft == 0 || (n % maxpkt) != 0)
445 if((i & Xfercomp) && ep->ttype != Tctl)
448 dprint("too little: nleft %d hcdma %x->%x hctsiz %x->%x intr %x\n",
449 nleft, hcdma, hc->hcdma, hctsiz, hc->hctsiz, i);
456 multitrans(Ep *ep, Hostchan *hc, int rw, void *a, long n)
465 m = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw],
466 (char*)a + sofar, m);
467 ep->toggle[rw] = hc->hctsiz & Pid;
469 }while(sofar < n && m == ep->maxpkt);
474 eptrans(Ep *ep, int rw, void *a, long n)
480 if(ep->mode != OREAD)
481 ep->toggle[Write] = DATA0;
482 if(ep->mode != OWRITE)
483 ep->toggle[Read] = DATA0;
487 ep->toggle[rw] = hc->hctsiz & Pid;
489 if(strcmp(up->errstr, Estalled) == 0)
494 if(rw == Read && ep->ttype == Tbulk)
495 n = multitrans(ep, hc, rw, a, n);
497 n = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw],
499 ep->toggle[rw] = hc->hctsiz & Pid;
507 ctltrans(Ep *ep, uchar *req, long n)
522 if(req[Rtype] & Rd2h){
523 datalen = GET2(req+Rcount);
524 if(datalen <= 0 || datalen > Maxctllen)
526 /* XXX cache madness */
527 epio->cb = b = allocb(ROUND(datalen, ep->maxpkt) + CACHELINESZ);
528 b->wp = (uchar*)ROUND((uintptr)b->wp, CACHELINESZ);
529 memset(b->wp, 0x55, b->lim - b->wp);
530 cachedwbinvse(b->wp, b->lim - b->wp);
534 datalen = n - Rsetuplen;
535 data = req + Rsetuplen;
540 if(strcmp(up->errstr, Estalled) == 0)
545 chanio(ep, hc, Epout, SETUP, req, Rsetuplen);
546 if(req[Rtype] & Rd2h){
547 if(ep->dev->hub <= 1){
548 ep->toggle[Read] = DATA1;
549 b->wp += multitrans(ep, hc, Read, data, datalen);
551 b->wp += chanio(ep, hc, Epin, DATA1, data, datalen);
552 chanio(ep, hc, Epout, DATA1, nil, 0);
556 chanio(ep, hc, Epout, DATA1, data, datalen);
557 chanio(ep, hc, Epin, DATA1, nil, 0);
558 n = Rsetuplen + datalen;
566 ctldata(Ep *ep, void *a, long n)
577 memmove(a, b->rp, n);
587 greset(Dwcregs *r, int bits)
590 while(r->grstctl & bits)
605 ctlr->nchan = 1 + ((r->ghwcfg2 & Num_host_chan) >> ONum_host_chan);
606 ctlr->chanintr = malloc(ctlr->nchan * sizeof(Rendez));
609 setpower(PowerUsb, 1);
611 while((r->grstctl&Ahbidle) == 0)
615 r->gusbcfg |= Force_host_mode;
616 tsleep(&up->sleep, return0, 0, 25);
617 r->gahbcfg |= Dmaenable;
619 n = (r->ghwcfg3 & Dfifo_depth) >> ODfifo_depth;
624 r->gnptxfsiz = rx | tx<<ODepth;
625 tsleep(&up->sleep, return0, 0, 1);
626 r->hptxfsiz = (rx + tx) | ptx << ODepth;
628 r->grstctl = TXF_ALL;
630 dprint("usbotg: FIFO depth %d sizes rx/nptx/ptx %8.8ux %8.8ux %8.8ux\n",
631 n, r->grxfsiz, r->gnptxfsiz, r->hptxfsiz);
633 r->hport0 = Prtpwr|Prtconndet|Prtenchng|Prtovrcurrchng;
636 r->gahbcfg |= Glblintrmsk;
645 fiqintr(Ureg*, void *a)
650 uint intr, haint, wakechan;
659 haint = r->haint & r->haintmsk;
660 for(i = 0; haint; i++){
662 if(chanintr(ctlr, i) == 0){
663 r->haintmsk &= ~(1<<i);
671 r->gintsts = Sofintr;
672 if((r->hfnum&7) != 6){
673 r->gintmsk &= ~Sofintr;
674 wakechan |= ctlr->sofchan;
679 ctlr->wakechan |= wakechan;
685 irqintr(Ureg*, void *a)
694 wakechan = ctlr->wakechan;
697 for(i = 0; wakechan; i++){
699 wakeup(&ctlr->chanintr[i]);
707 ddprint("usbotg: epopen ep%d.%d ttype %d\n",
708 ep->dev->nb, ep->nb, ep->ttype);
713 assert(ep->pollival > 0);
716 if(ep->toggle[Read] == 0)
717 ep->toggle[Read] = DATA0;
718 if(ep->toggle[Write] == 0)
719 ep->toggle[Write] = DATA0;
722 ep->aux = malloc(sizeof(Epio));
730 ddprint("usbotg: epclose ep%d.%d ttype %d\n",
731 ep->dev->nb, ep->nb, ep->ttype);
734 freeb(((Epio*)ep->aux)->cb);
743 epread(Ep *ep, void *a, long n)
751 ddprint("epread ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
765 nr = ctldata(ep, a, n);
770 elapsed = TK2MS(m->ticks) - epio->lastpoll;
771 if(elapsed < ep->pollival)
772 tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
775 /* XXX cache madness */
776 b = allocb(ROUND(n, ep->maxpkt) + CACHELINESZ);
777 p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
779 nr = eptrans(ep, Read, p, n);
780 epio->lastpoll = TK2MS(m->ticks);
790 epwrite(Ep *ep, void *a, long n)
797 ddprint("epwrite ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
811 elapsed = TK2MS(m->ticks) - epio->lastpoll;
812 if(elapsed < ep->pollival)
813 tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
817 /* XXX cache madness */
818 b = allocb(n + CACHELINESZ);
819 p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
822 if(ep->ttype == Tctl)
823 n = ctltrans(ep, p, n);
825 n = eptrans(ep, Write, p, n);
826 epio->lastpoll = TK2MS(m->ticks);
836 seprintep(char *s, char*, Ep*)
842 portenable(Hci *hp, int port, int on)
850 dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
852 r->hport0 = Prtpwr | Prtena;
853 tsleep(&up->sleep, return0, 0, Enabledelay);
854 dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
859 portreset(Hci *hp, int port, int on)
868 dprint("usbotg reset=%d; sts %#x\n", on, r->hport0);
871 r->hport0 = Prtpwr | Prtrst;
872 tsleep(&up->sleep, return0, 0, ResetdelayHS);
874 tsleep(&up->sleep, return0, 0, Enabledelay);
876 b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
878 r->hport0 = Prtpwr | b;
879 dprint("usbotg reset=%d; sts %#x\n", on, s);
880 if((s & Prtena) == 0)
881 print("usbotg: host port not enabled after reset");
886 portstatus(Hci *hp, int port)
896 b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
898 r->hport0 = Prtpwr | b;
908 if(s & Prtovrcurract)
933 setdebug(Hci*, int d)
945 if(ctlr->regs != nil)
947 ctlr->regs = (Dwcregs*)USBREGS;
948 id = ctlr->regs->gsnpsid;
949 if((id>>16) != ('O'<<8 | 'T'))
951 dprint("usbotg: rev %d.%3.3x\n", (id>>12)&0xF, id&0xFFF);
953 intrenable(IRQtimerArm, irqintr, ctlr, 0, "dwc");
964 hp->interrupt = fiqintr;
966 hp->epclose = epclose;
968 hp->epwrite = epwrite;
969 hp->seprintep = seprintep;
970 hp->portenable = portenable;
971 hp->portreset = portreset;
972 hp->portstatus = portstatus;
973 hp->shutdown = shutdown;
974 hp->debug = setdebug;
977 intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, "usbdwcotg");
985 addhcitype("dwcotg", reset);