xref: /plan9/sys/src/9/bcm/usbdwc.c (revision a587111c8770e522e3667ff2b63cba8a77811dd9)
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