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