]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm/usbdwc.c
add raspberry pi kernel (from sources)
[plan9front.git] / sys / src / 9 / bcm / usbdwc.c
1 /*
2  * USB host driver for BCM2835
3  *      Synopsis DesignWare Core USB 2.0 OTG controller
4  *
5  * Copyright © 2012 Richard Miller <r.miller@acm.org>
6  *
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.
14  */
15
16 #include        "u.h"
17 #include        "../port/lib.h"
18 #include        "mem.h"
19 #include        "dat.h"
20 #include        "fns.h"
21 #include        "io.h"
22 #include        "../port/error.h"
23 #include        "../port/usb.h"
24
25 #include "dwcotg.h"
26
27 enum
28 {
29         USBREGS         = VIRTIO + 0x980000,
30         Enabledelay     = 50,
31         Resetdelay      = 10,
32         ResetdelayHS    = 50,
33
34         Read            = 0,
35         Write           = 1,
36 };
37
38 typedef struct Ctlr Ctlr;
39 typedef struct Epio Epio;
40
41 struct Ctlr {
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 */
52 };
53
54 struct Epio {
55         QLock;
56         Block   *cb;
57         ulong   lastpoll;
58 };
59
60 static Ctlr dwc;
61 static int debug;
62
63 static char Ebadlen[] = "bad usb request length";
64 static char Enotconfig[] = "usb endpoint not configured";
65
66 static void clog(Ep *ep, Hostchan *hc);
67 static void logdump(Ep *ep);
68
69 static Hostchan*
70 chanalloc(Ep *ep)
71 {
72         Ctlr *ctlr;
73         int bitmap, i;
74
75         ctlr = ep->hp->aux;
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];
83                 }
84         qunlock(&ctlr->chanlock);
85         panic("miller is a lazy git");
86         return nil;
87 }
88
89 static void
90 chanrelease(Ep *ep, Hostchan *chan)
91 {
92         Ctlr *ctlr;
93         int i;
94
95         ctlr = ep->hp->aux;
96         i = chan - ctlr->regs->hchan;
97         qlock(&ctlr->chanlock);
98         ctlr->chanbusy &= ~(1 << i);
99         qunlock(&ctlr->chanlock);
100 }
101
102 static void
103 chansetup(Hostchan *hc, Ep *ep)
104 {
105         int hcc;
106         Ctlr *ctlr = ep->hp->aux;
107
108         if(ep->debug)
109                 ctlr->debugchan |= 1 << (hc - ctlr->regs->hchan);
110         else
111                 ctlr->debugchan &= ~(1 << (hc - ctlr->regs->hchan));
112         switch(ep->dev->state){
113         case Dconfig:
114         case Dreset:
115                 hcc = 0;
116                 break;
117         default:
118                 hcc = ep->dev->nb<<ODevaddr;
119                 break;
120         }
121         hcc |= ep->maxpkt | 1<<OMulticnt | ep->nb<<OEpnum;
122         switch(ep->ttype){
123         case Tctl:
124                 hcc |= Epctl;
125                 break;
126         case Tiso:
127                 hcc |= Episo;
128                 break;
129         case Tbulk:
130                 hcc |= Epbulk;
131                 break;
132         case Tintr:
133                 hcc |= Epintr;
134                 break;
135         }
136         switch(ep->dev->speed){
137         case Lowspeed:
138                 hcc |= Lspddev;
139                 /* fall through */
140         case Fullspeed:
141                 hc->hcsplt = Spltena | POS_ALL | ep->dev->hub << OHubaddr |
142                         ep->dev->port;
143                 break;
144         default:
145                 hc->hcsplt = 0;
146                 break;
147         }
148         hc->hcchar = hcc;
149         hc->hcint = ~0;
150 }
151
152 static int
153 sofdone(void *a)
154 {
155         Dwcregs *r;
156
157         r = a;
158         return r->gintsts & Sofintr;
159 }
160
161 static void
162 sofwait(Ctlr *ctlr, int n)
163 {
164         Dwcregs *r;
165         int x;
166
167         r = ctlr->regs;
168         do{
169                 r->gintsts = Sofintr;
170                 x = splfhi();
171                 ctlr->sofchan |= 1 << n;
172                 r->gintmsk |= Sofintr;
173                 sleep(&ctlr->chanintr[n], sofdone, r);
174                 splx(x);
175         }while((r->hfnum & 7) == 6);
176 }
177
178 static int
179 chandone(void *a)
180 {
181         Hostchan *hc;
182
183         hc = a;
184         return (hc->hcint & hc->hcintmsk) != 0;
185 }
186
187 static int
188 chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
189 {
190         int intr, n, x, ointr;
191         ulong start, now;
192         Dwcregs *r;
193
194         r = ctlr->regs;
195         n = hc - r->hchan;
196         for(;;){
197 restart:
198                 x = splfhi();
199                 r->haintmsk |= 1 << n;
200                 hc->hcintmsk = mask;
201                 sleep(&ctlr->chanintr[n], chandone, hc);
202                 hc->hcintmsk = 0;
203                 splx(x);
204                 intr = hc->hcint;
205                 if(intr & Chhltd)
206                         return intr;
207                 start = fastticks(0);
208                 ointr = intr;
209                 now = start;
210                 do{
211                         intr = hc->hcint;
212                         if(intr & Chhltd){
213                                 if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
214                                     intr != (Ack|Chhltd|Xfercomp) ||
215                                     (now - start) > 60)
216                                         dprint("await %x after %ld %x -> %x\n",
217                                                 mask, now - start, ointr, intr);
218                                 return intr;
219                         }
220                         if((intr & mask) == 0){
221                                 dprint("ep%d.%d await %x intr %x -> %x\n",                                              ep->dev->nb, ep->nb, mask, ointr, intr);
222                                 goto restart;
223                         }
224                         now = fastticks(0);
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);
230                 mask = Chhltd;
231                 hc->hcchar |= Chdis;
232                 start = m->ticks;
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);
237                                 break;
238                         }
239                 }
240                 logdump(ep);
241         }
242 }
243
244 static int
245 chanintr(Ctlr *ctlr, int n)
246 {
247         Hostchan *hc;
248         int i;
249
250         hc = &ctlr->regs->hchan[n];
251         if(ctlr->debugchan & (1 << n))
252                 clog(nil, hc);
253         if((hc->hcsplt & Spltena) == 0)
254                 return 0;
255         i = hc->hcint;
256         if(i == (Chhltd|Ack)){
257                 hc->hcsplt |= Compsplt;
258                 ctlr->splitretry = 0;
259         }else if(i == (Chhltd|Nyet)){
260                 if(++ctlr->splitretry >= 3)
261                         return 0;
262         }else
263                 return 0;
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)
268                         ;
269                 iprint(" %8.8ux\n", hc->hcint);
270         }
271         hc->hcint = i;
272         if(ctlr->regs->hfnum & 1)
273                 hc->hcchar &= ~Oddfrm;
274         else
275                 hc->hcchar |= Oddfrm;
276         hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
277         return 1;
278 }
279
280 static Reg chanlog[32][5];
281 static int nchanlog;
282
283 static void
284 logstart(Ep *ep)
285 {
286         if(ep->debug)
287                 nchanlog = 0;
288 }
289
290 static void
291 clog(Ep *ep, Hostchan *hc)
292 {
293         Reg *p;
294
295         if(ep != nil && !ep->debug)
296                 return;
297         if(nchanlog == 32)
298                 nchanlog--;
299         p = chanlog[nchanlog];
300         p[0] = dwc.regs->hfnum;
301         p[1] = hc->hcchar;
302         p[2] = hc->hcint;
303         p[3] = hc->hctsiz;
304         p[4] = hc->hcdma;
305         nchanlog++;
306 }
307
308 static void
309 logdump(Ep *ep)
310 {
311         Reg *p;
312         int i;
313
314         if(!ep->debug)
315                 return;
316         p = chanlog[0];
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]);
320                 p += 5;
321         }
322         nchanlog = 0;
323 }
324
325 static int
326 chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
327 {
328         Ctlr *ctlr;
329         int nleft, n, nt, i, maxpkt, npkt;
330         uint hcdma, hctsiz;
331
332         ctlr = ep->hp->aux;
333         maxpkt = ep->maxpkt;
334         npkt = HOWMANY(len, ep->maxpkt);
335         if(npkt == 0)
336                 npkt = 1;
337
338         hc->hcchar = (hc->hcchar & ~Epdir) | dir;
339         if(dir == Epin)
340                 n = ROUND(len, ep->maxpkt);
341         else
342                 n = len;
343         hc->hctsiz = n | npkt << OPktcnt | pid;
344         hc->hcdma  = PADDR(a);
345
346         nleft = len;
347         logstart(ep);
348         for(;;){
349                 hcdma = hc->hcdma;
350                 hctsiz = hc->hctsiz;
351                 hc->hctsiz = hctsiz & ~Dopng;
352                 if(hc->hcchar&Chen){
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)
357                                 ;
358                         hc->hcint = Chhltd;
359                 }
360                 if((i = hc->hcint) != 0){
361                         dprint("ep%d.%d before chanio hcint=%8.8ux\n",
362                                 ep->dev->nb, ep->nb, i);
363                         hc->hcint = i;
364                 }
365                 if(hc->hcsplt & Spltena){
366                         qlock(&ctlr->split);
367                         sofwait(ctlr, hc - ctlr->regs->hchan);
368                         if((dwc.regs->hfnum & 1) == 0)
369                                 hc->hcchar &= ~Oddfrm;
370                         else
371                                 hc->hcchar |= Oddfrm;
372                 }
373                 hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
374                 clog(ep, hc);
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);
379                 else
380                         i = chanwait(ep, ctlr, hc, Chhltd|Nak);
381                 clog(ep, hc);
382                 hc->hcint = i;
383
384                 if(hc->hcsplt & Spltena){
385                         hc->hcsplt &= ~Compsplt;
386                         qunlock(&ctlr->split);
387                 }
388
389                 if((i & Xfercomp) == 0 && i != (Chhltd|Ack) && i != Chhltd){
390                         if(i & Stall)
391                                 error(Estalled);
392                         if(i & Nyet)
393                                 continue;
394                         if(i & Nak){
395                                 if(ep->ttype == Tintr)
396                                         tsleep(&up->sleep, return0, 0, ep->pollival);
397                                 else
398                                         tsleep(&up->sleep, return0, 0, 1);
399                                 continue;
400                         }
401                         print("usbotg: ep%d.%d error intr %8.8ux\n",
402                                 ep->dev->nb, ep->nb, i);
403                         if(i & ~(Chhltd|Ack))
404                                 error(Eio);
405                         if(hc->hcdma != hcdma)
406                                 print("usbotg: weird hcdma %x->%x intr %x->%x\n",
407                                         hcdma, hc->hcdma, i, hc->hcint);
408                 }
409                 n = hc->hcdma - hcdma;
410                 if(n == 0)
411                         if((hc->hctsiz & Pktcnt) != (hctsiz & Pktcnt))
412                                 break;
413                         else
414                                 continue;
415                 if(dir == Epin && ep->ttype == Tbulk && n == nleft){
416                         nt = (hctsiz & Xfersize) - (hc->hctsiz & Xfersize);
417                         if(nt != n)
418                                 if(n == ((nt+3) & ~3))
419                                         n = nt;
420                                 else
421                                         print("usbotg: intr %8.8ux dma "
422                                                 "%8.8ux-%8.8ux hctsiz "
423                                                 "%8.8ux-%8.ux\n",
424                                                 i, hcdma, hc->hcdma, hctsiz,
425                                                 hc->hctsiz);
426                 }
427                 if(n > nleft){
428                         if(n != ((nleft+3) & ~3))
429                                 dprint("too much: wanted %d got %d\n",
430                                         len, len - nleft + n);
431                         n = nleft;
432                 }
433                 nleft -= n;
434                 if(nleft == 0 || n % maxpkt != 0)
435                         break;
436                 if((i & Xfercomp) && ep->ttype != Tctl)
437                         break;
438                 if(dir == Epout)
439                         dprint("too little: nleft %d hcdma %x->%x hctsiz %x->%x intr %x\n",
440                                 nleft, hcdma, hc->hcdma, hctsiz, hc->hctsiz, i);
441         }
442         logdump(ep);
443         return len - nleft;
444 }
445
446 static long
447 eptrans(Ep *ep, int rw, void *a, long n)
448 {
449         Hostchan *hc;
450
451         if(ep->clrhalt){
452                 ep->clrhalt = 0;
453                 if(ep->mode != OREAD)
454                         ep->toggle[Write] = DATA0;
455                 if(ep->mode != OWRITE)
456                         ep->toggle[Read] = DATA0;
457         }
458         hc = chanalloc(ep);
459         if(waserror()){
460                 ep->toggle[rw] = hc->hctsiz & Pid;
461                 chanrelease(ep, hc);
462                 if(strcmp(up->errstr, Estalled) == 0)
463                         return 0;
464                 nexterror();
465         }
466         chansetup(hc, ep);
467         if(rw == Read && ep->ttype == Tbulk){
468                 long sofar, m;
469
470                 sofar = 0;
471                 do{
472                         m = n - sofar;
473                         if(m > ep->maxpkt)
474                                 m = ep->maxpkt;
475                         m = chanio(ep, hc, Epin, ep->toggle[rw],
476                                 (char*)a + sofar, m);
477                         ep->toggle[rw] = hc->hctsiz & Pid;
478                         sofar += m;
479                 }while(sofar < n && m == ep->maxpkt);
480                 n = sofar;
481         }else{
482                 n = chanio(ep, hc, rw == Read? Epin: Epout, ep->toggle[rw],
483                         a, n);
484                 ep->toggle[rw] = hc->hctsiz & Pid;
485         }
486         chanrelease(ep, hc);
487         poperror();
488         return n;
489 }
490
491 static long
492 ctltrans(Ep *ep, uchar *req, long n)
493 {
494         Hostchan *hc;
495         Epio *epio;
496         Block *b;
497         uchar *data;
498         int datalen;
499
500         epio = ep->aux;
501         if(epio->cb != nil){
502                 freeb(epio->cb);
503                 epio->cb = nil;
504         }
505         if(n < Rsetuplen)
506                 error(Ebadlen);
507         if(req[Rtype] & Rd2h){
508                 datalen = GET2(req+Rcount);
509                 if(datalen <= 0 || datalen > Maxctllen)
510                         error(Ebadlen);
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);
516                 data = b->wp;
517         }else{
518                 b = nil;
519                 datalen = n - Rsetuplen;
520                 data = req + Rsetuplen;
521         }
522         hc = chanalloc(ep);
523         if(waserror()){
524                 chanrelease(ep, hc);
525                 if(strcmp(up->errstr, Estalled) == 0)
526                         return 0;
527                 nexterror();
528         }
529         chansetup(hc, ep);
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);
534                 n = Rsetuplen;
535         }else{
536                 if(datalen > 0)
537                         chanio(ep, hc, Epout, DATA1, data, datalen);
538                 chanio(ep, hc, Epin, DATA1, nil, 0);
539                 n = Rsetuplen + datalen;
540         }
541         chanrelease(ep, hc);
542         poperror();
543         return n;
544 }
545
546 static long
547 ctldata(Ep *ep, void *a, long n)
548 {
549         Epio *epio;
550         Block *b;
551
552         epio = ep->aux;
553         b = epio->cb;
554         if(b == nil)
555                 return 0;
556         if(n > BLEN(b))
557                 n = BLEN(b);
558         memmove(a, b->rp, n);
559         b->rp += n;
560         if(BLEN(b) == 0){
561                 freeb(b);
562                 epio->cb = nil;
563         }
564         return n;
565 }
566
567 static void
568 greset(Dwcregs *r, int bits)
569 {
570         r->grstctl |= bits;
571         while(r->grstctl & bits)
572                 ;
573         microdelay(10);
574 }
575
576 static void
577 init(Hci *hp)
578 {
579         Ctlr *ctlr;
580         Dwcregs *r;
581         uint n, rx, tx, ptx;
582
583         ctlr = hp->aux;
584         r = ctlr->regs;
585
586         ctlr->nchan = 1 + ((r->ghwcfg2 & Num_host_chan) >> ONum_host_chan);
587         ctlr->chanintr = malloc(ctlr->nchan * sizeof(Rendez));
588
589         r->gahbcfg = 0;
590         setpower(PowerUsb, 1);
591
592         while((r->grstctl&Ahbidle) == 0)
593                 ;
594         greset(r, Csftrst);
595
596         r->gusbcfg |= Force_host_mode;
597         tsleep(&up->sleep, return0, 0, 25);
598         r->gahbcfg |= Dmaenable;
599
600         n = (r->ghwcfg3 & Dfifo_depth) >> ODfifo_depth;
601         rx = 0x306;
602         tx = 0x100;
603         ptx = 0x200;
604         r->grxfsiz = rx;
605         r->gnptxfsiz = rx | tx << ODepth;
606         tsleep(&up->sleep, return0, 0, 1);
607         r->hptxfsiz = (rx + tx) | ptx << ODepth;
608         greset(r, Rxfflsh);
609         r->grstctl = TXF_ALL;
610         greset(r, Txfflsh);
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);
613
614         r->hport0 = Prtpwr|Prtconndet|Prtenchng|Prtovrcurrchng;
615         r->gintsts = ~0;
616         r->gintmsk = Hcintr;
617         r->gahbcfg |= Glblintrmsk;
618 }
619
620 static void
621 dump(Hci*)
622 {
623 }
624
625 static void
626 fiqintr(Ureg*, void *a)
627 {
628         Hci *hp;
629         Ctlr *ctlr;
630         Dwcregs *r;
631         uint intr, haint, wakechan;
632         int i;
633
634         hp = a;
635         ctlr = hp->aux;
636         r = ctlr->regs;
637         wakechan = 0;
638         intr = r->gintsts;
639         if(intr & Hcintr){
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);
644                                 wakechan |= 1 << i;
645                         }
646                         haint >>= 1;
647                 }
648         }
649         if(intr & Sofintr){
650                 r->gintsts = Sofintr;
651                 if((r->hfnum&7) != 6){
652                         r->gintmsk &= ~Sofintr;
653                         wakechan |= ctlr->sofchan;
654                         ctlr->sofchan = 0;
655                 }
656         }
657         if(wakechan){
658                 ctlr->wakechan |= wakechan;
659                 armtimerset(1);
660         }
661 }
662
663 static void
664 irqintr(Ureg*, void *a)
665 {
666         Ctlr *ctlr;
667         uint wakechan;
668         int i, x;
669
670         ctlr = a;
671         x = splfhi();
672         armtimerset(0);
673         wakechan = ctlr->wakechan;
674         ctlr->wakechan = 0;
675         splx(x);
676         for(i = 0; wakechan; i++){
677                 if(wakechan & 1)
678                         wakeup(&ctlr->chanintr[i]);
679                 wakechan >>= 1;
680         }
681 }
682
683 static void
684 epopen(Ep *ep)
685 {
686         ddprint("usbotg: epopen ep%d.%d ttype %d\n",
687                 ep->dev->nb, ep->nb, ep->ttype);
688         switch(ep->ttype){
689         case Tnone:
690                 error(Enotconfig);
691         case Tintr:
692                 assert(ep->pollival > 0);
693                 /* fall through */
694         case Tbulk:
695                 if(ep->toggle[Read] == 0)
696                         ep->toggle[Read] = DATA0;
697                 if(ep->toggle[Write] == 0)
698                         ep->toggle[Write] = DATA0;
699                 break;
700         }
701         ep->aux = malloc(sizeof(Epio));
702         if(ep->aux == nil)
703                 error(Enomem);
704 }
705
706 static void
707 epclose(Ep *ep)
708 {
709         ddprint("usbotg: epclose ep%d.%d ttype %d\n",
710                 ep->dev->nb, ep->nb, ep->ttype);
711         switch(ep->ttype){
712         case Tctl:
713                 freeb(((Epio*)ep->aux)->cb);
714                 /* fall through */
715         default:
716                 free(ep->aux);
717                 break;
718         }
719 }
720
721 static long
722 epread(Ep *ep, void *a, long n)
723 {
724         Epio *epio;
725         Block *b;
726         uchar *p;
727         ulong elapsed;
728         long nr;
729
730         ddprint("epread ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
731         epio = ep->aux;
732         b = nil;
733         qlock(epio);
734         if(waserror()){
735                 qunlock(epio);
736                 if(b)
737                         freeb(b);
738                 nexterror();
739         }
740         switch(ep->ttype){
741         default:
742                 error(Egreg);
743         case Tctl:
744                 nr = ctldata(ep, a, n);
745                 qunlock(epio);
746                 poperror();
747                 return nr;
748         case Tintr:
749                 elapsed = TK2MS(m->ticks) - epio->lastpoll;
750                 if(elapsed < ep->pollival)
751                         tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
752                 /* fall through */
753         case Tbulk:
754                 /* XXX cache madness */
755                 b = allocb(ROUND(n, ep->maxpkt) + CACHELINESZ);
756                 p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
757                 cachedwbinvse(p, n);
758                 nr = eptrans(ep, Read, p, n);
759                 epio->lastpoll = TK2MS(m->ticks);
760                 memmove(a, p, nr);
761                 qunlock(epio);
762                 freeb(b);
763                 poperror();
764                 return nr;
765         }
766 }
767
768 static long
769 epwrite(Ep *ep, void *a, long n)
770 {
771         Epio *epio;
772         Block *b;
773         uchar *p;
774         ulong elapsed;
775
776         ddprint("epwrite ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
777         epio = ep->aux;
778         b = nil;
779         qlock(epio);
780         if(waserror()){
781                 qunlock(epio);
782                 if(b)
783                         freeb(b);
784                 nexterror();
785         }
786         switch(ep->ttype){
787         default:
788                 error(Egreg);
789         case Tintr:
790                 elapsed = TK2MS(m->ticks) - epio->lastpoll;
791                 if(elapsed < ep->pollival)
792                         tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
793                 /* fall through */
794         case Tctl:
795         case Tbulk:
796                 /* XXX cache madness */
797                 b = allocb(n + CACHELINESZ);
798                 p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
799                 memmove(p, a, n);
800                 cachedwbse(p, n);
801                 if(ep->ttype == Tctl)
802                         n = ctltrans(ep, p, n);
803                 else{
804                         n = eptrans(ep, Write, p, n);
805                         epio->lastpoll = TK2MS(m->ticks);
806                 }
807                 qunlock(epio);
808                 freeb(b);
809                 poperror();
810                 return n;
811         }
812 }
813
814 static char*
815 seprintep(char *s, char*, Ep*)
816 {
817         return s;
818 }
819         
820 static int
821 portenable(Hci *hp, int port, int on)
822 {
823         Ctlr *ctlr;
824         Dwcregs *r;
825
826         assert(port == 1);
827         ctlr = hp->aux;
828         r = ctlr->regs;
829         dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
830         if(!on)
831                 r->hport0 = Prtpwr | Prtena;
832         tsleep(&up->sleep, return0, 0, Enabledelay);
833         dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
834         return 0;
835 }
836
837 static int
838 portreset(Hci *hp, int port, int on)
839 {
840         Ctlr *ctlr;
841         Dwcregs *r;
842         int b, s;
843
844         assert(port == 1);
845         ctlr = hp->aux;
846         r = ctlr->regs;
847         dprint("usbotg reset=%d; sts %#x\n", on, r->hport0);
848         if(!on)
849                 return 0;
850         r->hport0 = Prtpwr | Prtrst;
851         tsleep(&up->sleep, return0, 0, ResetdelayHS);
852         r->hport0 = Prtpwr;
853         tsleep(&up->sleep, return0, 0, Enabledelay);
854         s = r->hport0;
855         b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
856         if(b != 0)
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");
861         return 0;
862 }
863
864 static int
865 portstatus(Hci *hp, int port)
866 {
867         Ctlr *ctlr;
868         Dwcregs *r;
869         int b, s;
870
871         assert(port == 1);
872         ctlr = hp->aux;
873         r = ctlr->regs;
874         s = r->hport0;
875         b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
876         if(b != 0)
877                 r->hport0 = Prtpwr | b;
878         b = 0;
879         if(s & Prtconnsts)
880                 b |= HPpresent;
881         if(s & Prtconndet)
882                 b |= HPstatuschg;
883         if(s & Prtena)
884                 b |= HPenable;
885         if(s & Prtenchng)
886                 b |= HPchange;
887         if(s & Prtovrcurract)
888                  b |= HPovercurrent;
889         if(s & Prtsusp)
890                 b |= HPsuspend;
891         if(s & Prtrst)
892                 b |= HPreset;
893         if(s & Prtpwr)
894                 b |= HPpower;
895         switch(s & Prtspd){
896         case HIGHSPEED:
897                 b |= HPhigh;
898                 break;
899         case LOWSPEED:
900                 b |= HPslow;
901                 break;
902         }
903         return b;
904 }
905
906 static void
907 shutdown(Hci*)
908 {
909 }
910
911 static void
912 setdebug(Hci*, int d)
913 {
914         debug = d;
915 }
916
917 static int
918 reset(Hci *hp)
919 {
920         Ctlr *ctlr;
921         uint id;
922
923         ctlr = &dwc;
924         if(ctlr->regs != nil)
925                 return -1;
926         ctlr->regs = (Dwcregs*)USBREGS;
927         id = ctlr->regs->gsnpsid;
928         if((id>>16) != ('O'<<8 | 'T'))
929                 return -1;
930         dprint("usbotg: rev %d.%3.3x\n", (id>>12)&0xF, id&0xFFF);
931
932         intrenable(IRQtimerArm, irqintr, ctlr, 0, "dwc");
933
934         hp->aux = ctlr;
935         hp->port = 0;
936         hp->irq = IRQusb;
937         hp->tbdf = 0;
938         hp->nports = 1;
939         hp->highspeed = 1;
940
941         hp->init = init;
942         hp->dump = dump;
943         hp->interrupt = fiqintr;
944         hp->epopen = epopen;
945         hp->epclose = epclose;
946         hp->epread = epread;
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;
954         hp->type = "dwcotg";
955
956         intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, "usbdwcotg");
957
958         return 0;
959 }
960
961 void
962 usbdwclink(void)
963 {
964         addhcitype("dwcotg", reset);
965 }