changelog shortlog tags branches changeset files revisions annotate raw help

Mercurial > hg > plan9front / sys/src/9/bcm/usbdwc.c

changeset 7205: 3aeef7e94ea9
parent: f80792d28e0e
child: 4dbf2522f668
author: cinap_lenrek@felloff.net
date: Sun, 05 May 2019 13:34:02 +0200
permissions: -rw-r--r--
description: usbdwc: enable Slowbuilkin workarround, improve split transaction timing, handle erroring sleep(), debugging

i'v been seeing the error condition described above in the
Slowbulkin comment. so i'm enabling the work arround which
seems to fix the lockup.

in the split transaction case where we want to start the
transaction at frame start, acquire the ctlr lock *before*
checking if we are in the right frame number. so the start
will happen atomically. checking the software ctlr->sofchan
instead of checking the interrupt mask register seems to
be quicker.

setting the haint mask bit for the chan under ctlr lock
in chanio() instead of chanwait() avoids needing to acquire
the ctlr lock twice.

mask wakechan bits with busychan bitmap in interrupt handlers
so we will not try to wake up released chans by accident.

sleep() and tsleep() might get interrupted so we have to
release the split qlock in the split transaction case and
in all cases, make sure to halt the channel before release.

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