Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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*
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
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
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
155
sofdone(void *a)
156
{
157
	Dwcregs *r;
158
 
159
	r = a;
160
	return r->gintsts & Sofintr;
161
}
162
 
163
static void
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
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
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
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
289
logstart(Ep *ep)
290
{
291
	if(ep->debug)
292
		nchanlog = 0;
293
}
294
 
295
static void
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
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
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
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
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
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
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
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
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
639
dump(Hci*)
640
{
641
}
642
 
643
static void
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
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
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
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
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
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*
835
seprintep(char *s, char*, Ep*)
836
{
837
	return s;
838
}
839
 
840
static int
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
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
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
927
shutdown(Hci*)
928
{
929
}
930
 
931
static void
932
setdebug(Hci*, int d)
933
{
934
	debug = d;
935
}
936
 
937
static int
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
979
usbdwclink(void)
980
{
981
	addhcitype("dwcotg", reset);
982
}