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