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