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