xref: /plan9-contrib/sys/src/9/bcm/usbdwc.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
15d9de2d3SDavid du Colombier /*
25d9de2d3SDavid du Colombier  * USB host driver for BCM2835
35d9de2d3SDavid du Colombier  *	Synopsis DesignWare Core USB 2.0 OTG controller
45d9de2d3SDavid du Colombier  *
55d9de2d3SDavid du Colombier  * Copyright © 2012 Richard Miller <r.miller@acm.org>
65d9de2d3SDavid du Colombier  *
75d9de2d3SDavid du Colombier  * This is work in progress:
85d9de2d3SDavid du Colombier  * - no isochronous pipes
95d9de2d3SDavid du Colombier  * - no bandwidth budgeting
105d9de2d3SDavid du Colombier  * - frame scheduling is crude
115d9de2d3SDavid du Colombier  * - error handling is overly optimistic
125d9de2d3SDavid du Colombier  * It should be just about adequate for a Plan 9 terminal with
135d9de2d3SDavid du Colombier  * keyboard, mouse, ethernet adapter, and an external flash drive.
145d9de2d3SDavid du Colombier  */
155d9de2d3SDavid du Colombier 
165d9de2d3SDavid du Colombier #include	"u.h"
175d9de2d3SDavid du Colombier #include	"../port/lib.h"
185d9de2d3SDavid du Colombier #include	"mem.h"
195d9de2d3SDavid du Colombier #include	"dat.h"
205d9de2d3SDavid du Colombier #include	"fns.h"
215d9de2d3SDavid du Colombier #include	"io.h"
225d9de2d3SDavid du Colombier #include	"../port/error.h"
23*5c47fe09SDavid du Colombier #include	"usb.h"
245d9de2d3SDavid du Colombier 
255d9de2d3SDavid du Colombier #include "dwcotg.h"
265d9de2d3SDavid du Colombier 
275d9de2d3SDavid du Colombier enum
285d9de2d3SDavid du Colombier {
295d9de2d3SDavid du Colombier 	USBREGS		= VIRTIO + 0x980000,
305d9de2d3SDavid du Colombier 	Enabledelay	= 50,
315d9de2d3SDavid du Colombier 	Resetdelay	= 10,
325d9de2d3SDavid du Colombier 	ResetdelayHS	= 50,
335d9de2d3SDavid du Colombier 
345d9de2d3SDavid du Colombier 	Read		= 0,
355d9de2d3SDavid du Colombier 	Write		= 1,
36*5c47fe09SDavid du Colombier 
37*5c47fe09SDavid du Colombier 	/*
38*5c47fe09SDavid du Colombier 	 * Workaround for an unexplained glitch where an Ack interrupt
39*5c47fe09SDavid du Colombier 	 * is received without Chhltd, whereupon all channels remain
40*5c47fe09SDavid du Colombier 	 * permanently busy and can't be halted.  This was only seen
41*5c47fe09SDavid du Colombier 	 * when the controller is reading a sequence of bulk input
42*5c47fe09SDavid du Colombier 	 * packets in DMA mode.  Setting Slowbulkin=1 will avoid the
43*5c47fe09SDavid du Colombier 	 * lockup by reading packets individually with an interrupt
44*5c47fe09SDavid du Colombier 	 * after each.  More recent chips don't seem to exhibit the
45*5c47fe09SDavid du Colombier 	 * problem, so it's probably safe to leave this off now.
46*5c47fe09SDavid du Colombier 	 */
47*5c47fe09SDavid du Colombier 	Slowbulkin	= 0,
485d9de2d3SDavid du Colombier };
495d9de2d3SDavid du Colombier 
505d9de2d3SDavid du Colombier typedef struct Ctlr Ctlr;
515d9de2d3SDavid du Colombier typedef struct Epio Epio;
525d9de2d3SDavid du Colombier 
535d9de2d3SDavid du Colombier struct Ctlr {
54*5c47fe09SDavid du Colombier 	Lock;
555d9de2d3SDavid du Colombier 	Dwcregs	*regs;		/* controller registers */
565d9de2d3SDavid du Colombier 	int	nchan;		/* number of host channels */
575d9de2d3SDavid du Colombier 	ulong	chanbusy;	/* bitmap of in-use channels */
585d9de2d3SDavid du Colombier 	QLock	chanlock;	/* serialise access to chanbusy */
595d9de2d3SDavid du Colombier 	QLock	split;		/* serialise split transactions */
605d9de2d3SDavid du Colombier 	int	splitretry;	/* count retries of Nyet */
615d9de2d3SDavid du Colombier 	int	sofchan;	/* bitmap of channels waiting for sof */
625d9de2d3SDavid du Colombier 	int	wakechan;	/* bitmap of channels to wakeup after fiq */
635d9de2d3SDavid du Colombier 	int	debugchan;	/* bitmap of channels for interrupt debug */
645d9de2d3SDavid du Colombier 	Rendez	*chanintr;	/* sleep till interrupt on channel N */
655d9de2d3SDavid du Colombier };
665d9de2d3SDavid du Colombier 
675d9de2d3SDavid du Colombier struct Epio {
68*5c47fe09SDavid du Colombier 	union {
69*5c47fe09SDavid du Colombier 		QLock	rlock;
70*5c47fe09SDavid du Colombier 		QLock	ctllock;
71*5c47fe09SDavid du Colombier 	};
72*5c47fe09SDavid du Colombier 	QLock	wlock;
735d9de2d3SDavid du Colombier 	Block	*cb;
745d9de2d3SDavid du Colombier 	ulong	lastpoll;
755d9de2d3SDavid du Colombier };
765d9de2d3SDavid du Colombier 
775d9de2d3SDavid du Colombier static Ctlr dwc;
785d9de2d3SDavid du Colombier static int debug;
795d9de2d3SDavid du Colombier 
805d9de2d3SDavid du Colombier static char Ebadlen[] = "bad usb request length";
815d9de2d3SDavid du Colombier 
825d9de2d3SDavid du Colombier static void clog(Ep *ep, Hostchan *hc);
835d9de2d3SDavid du Colombier static void logdump(Ep *ep);
845d9de2d3SDavid du Colombier 
85*5c47fe09SDavid du Colombier static void
filock(Lock * l)86*5c47fe09SDavid du Colombier filock(Lock *l)
87*5c47fe09SDavid du Colombier {
88*5c47fe09SDavid du Colombier 	int x;
89*5c47fe09SDavid du Colombier 
90*5c47fe09SDavid du Colombier 	x = splfhi();
91*5c47fe09SDavid du Colombier 	ilock(l);
92*5c47fe09SDavid du Colombier 	l->sr = x;
93*5c47fe09SDavid du Colombier }
94*5c47fe09SDavid du Colombier 
95*5c47fe09SDavid du Colombier static void
fiunlock(Lock * l)96*5c47fe09SDavid du Colombier fiunlock(Lock *l)
97*5c47fe09SDavid du Colombier {
98*5c47fe09SDavid du Colombier 	iunlock(l);
99*5c47fe09SDavid du Colombier }
100*5c47fe09SDavid du Colombier 
1015d9de2d3SDavid du Colombier static Hostchan*
chanalloc(Ep * ep)1025d9de2d3SDavid du Colombier chanalloc(Ep *ep)
1035d9de2d3SDavid du Colombier {
1045d9de2d3SDavid du Colombier 	Ctlr *ctlr;
1055d9de2d3SDavid du Colombier 	int bitmap, i;
106*5c47fe09SDavid du Colombier 	static int first;
1075d9de2d3SDavid du Colombier 
1085d9de2d3SDavid du Colombier 	ctlr = ep->hp->aux;
109*5c47fe09SDavid du Colombier retry:
1105d9de2d3SDavid du Colombier 	qlock(&ctlr->chanlock);
1115d9de2d3SDavid du Colombier 	bitmap = ctlr->chanbusy;
1125d9de2d3SDavid du Colombier 	for(i = 0; i < ctlr->nchan; i++)
1135d9de2d3SDavid du Colombier 		if((bitmap & (1<<i)) == 0){
1145d9de2d3SDavid du Colombier 			ctlr->chanbusy = bitmap | 1<<i;
1155d9de2d3SDavid du Colombier 			qunlock(&ctlr->chanlock);
1165d9de2d3SDavid du Colombier 			return &ctlr->regs->hchan[i];
1175d9de2d3SDavid du Colombier 		}
1185d9de2d3SDavid du Colombier 	qunlock(&ctlr->chanlock);
119*5c47fe09SDavid du Colombier 	if(!first++)
120*5c47fe09SDavid du Colombier 		print("usbdwc: all host channels busy - retrying\n");
121*5c47fe09SDavid du Colombier 	tsleep(&up->sleep, return0, 0, 1);
122*5c47fe09SDavid du Colombier 	goto retry;
1235d9de2d3SDavid du Colombier }
1245d9de2d3SDavid du Colombier 
1255d9de2d3SDavid du Colombier static void
chanrelease(Ep * ep,Hostchan * chan)1265d9de2d3SDavid du Colombier chanrelease(Ep *ep, Hostchan *chan)
1275d9de2d3SDavid du Colombier {
1285d9de2d3SDavid du Colombier 	Ctlr *ctlr;
1295d9de2d3SDavid du Colombier 	int i;
1305d9de2d3SDavid du Colombier 
1315d9de2d3SDavid du Colombier 	ctlr = ep->hp->aux;
1325d9de2d3SDavid du Colombier 	i = chan - ctlr->regs->hchan;
1335d9de2d3SDavid du Colombier 	qlock(&ctlr->chanlock);
1345d9de2d3SDavid du Colombier 	ctlr->chanbusy &= ~(1<<i);
1355d9de2d3SDavid du Colombier 	qunlock(&ctlr->chanlock);
1365d9de2d3SDavid du Colombier }
1375d9de2d3SDavid du Colombier 
1385d9de2d3SDavid du Colombier static void
chansetup(Hostchan * hc,Ep * ep)1395d9de2d3SDavid du Colombier chansetup(Hostchan *hc, Ep *ep)
1405d9de2d3SDavid du Colombier {
1415d9de2d3SDavid du Colombier 	int hcc;
1425d9de2d3SDavid du Colombier 	Ctlr *ctlr = ep->hp->aux;
1435d9de2d3SDavid du Colombier 
1445d9de2d3SDavid du Colombier 	if(ep->debug)
1455d9de2d3SDavid du Colombier 		ctlr->debugchan |= 1 << (hc - ctlr->regs->hchan);
1465d9de2d3SDavid du Colombier 	else
1475d9de2d3SDavid du Colombier 		ctlr->debugchan &= ~(1 << (hc - ctlr->regs->hchan));
1485d9de2d3SDavid du Colombier 	switch(ep->dev->state){
1495d9de2d3SDavid du Colombier 	case Dconfig:
1505d9de2d3SDavid du Colombier 	case Dreset:
1515d9de2d3SDavid du Colombier 		hcc = 0;
1525d9de2d3SDavid du Colombier 		break;
1535d9de2d3SDavid du Colombier 	default:
1545d9de2d3SDavid du Colombier 		hcc = ep->dev->nb<<ODevaddr;
1555d9de2d3SDavid du Colombier 		break;
1565d9de2d3SDavid du Colombier 	}
1575d9de2d3SDavid du Colombier 	hcc |= ep->maxpkt | 1<<OMulticnt | ep->nb<<OEpnum;
1585d9de2d3SDavid du Colombier 	switch(ep->ttype){
1595d9de2d3SDavid du Colombier 	case Tctl:
1605d9de2d3SDavid du Colombier 		hcc |= Epctl;
1615d9de2d3SDavid du Colombier 		break;
1625d9de2d3SDavid du Colombier 	case Tiso:
1635d9de2d3SDavid du Colombier 		hcc |= Episo;
1645d9de2d3SDavid du Colombier 		break;
1655d9de2d3SDavid du Colombier 	case Tbulk:
1665d9de2d3SDavid du Colombier 		hcc |= Epbulk;
1675d9de2d3SDavid du Colombier 		break;
1685d9de2d3SDavid du Colombier 	case Tintr:
1695d9de2d3SDavid du Colombier 		hcc |= Epintr;
1705d9de2d3SDavid du Colombier 		break;
1715d9de2d3SDavid du Colombier 	}
1725d9de2d3SDavid du Colombier 	switch(ep->dev->speed){
1735d9de2d3SDavid du Colombier 	case Lowspeed:
1745d9de2d3SDavid du Colombier 		hcc |= Lspddev;
1755d9de2d3SDavid du Colombier 		/* fall through */
1765d9de2d3SDavid du Colombier 	case Fullspeed:
177fe823997SDavid du Colombier 		if(ep->dev->hub > 1){
1785d9de2d3SDavid du Colombier 			hc->hcsplt = Spltena | POS_ALL | ep->dev->hub<<OHubaddr |
1795d9de2d3SDavid du Colombier 				ep->dev->port;
1805d9de2d3SDavid du Colombier 			break;
181fe823997SDavid du Colombier 		}
182fe823997SDavid du Colombier 		/* fall through */
1835d9de2d3SDavid du Colombier 	default:
1845d9de2d3SDavid du Colombier 		hc->hcsplt = 0;
1855d9de2d3SDavid du Colombier 		break;
1865d9de2d3SDavid du Colombier 	}
1875d9de2d3SDavid du Colombier 	hc->hcchar = hcc;
1885d9de2d3SDavid du Colombier 	hc->hcint = ~0;
1895d9de2d3SDavid du Colombier }
1905d9de2d3SDavid du Colombier 
1915d9de2d3SDavid du Colombier static int
sofdone(void * a)1925d9de2d3SDavid du Colombier sofdone(void *a)
1935d9de2d3SDavid du Colombier {
1945d9de2d3SDavid du Colombier 	Dwcregs *r;
1955d9de2d3SDavid du Colombier 
1965d9de2d3SDavid du Colombier 	r = a;
197*5c47fe09SDavid du Colombier 	return (r->gintmsk & Sofintr) == 0;
1985d9de2d3SDavid du Colombier }
1995d9de2d3SDavid du Colombier 
2005d9de2d3SDavid du Colombier static void
sofwait(Ctlr * ctlr,int n)2015d9de2d3SDavid du Colombier sofwait(Ctlr *ctlr, int n)
2025d9de2d3SDavid du Colombier {
2035d9de2d3SDavid du Colombier 	Dwcregs *r;
2045d9de2d3SDavid du Colombier 
2055d9de2d3SDavid du Colombier 	r = ctlr->regs;
2065d9de2d3SDavid du Colombier 	do{
207*5c47fe09SDavid du Colombier 		filock(ctlr);
2085d9de2d3SDavid du Colombier 		r->gintsts = Sofintr;
2095d9de2d3SDavid du Colombier 		ctlr->sofchan |= 1<<n;
2105d9de2d3SDavid du Colombier 		r->gintmsk |= Sofintr;
211*5c47fe09SDavid du Colombier 		fiunlock(ctlr);
2125d9de2d3SDavid du Colombier 		sleep(&ctlr->chanintr[n], sofdone, r);
2135d9de2d3SDavid du Colombier 	}while((r->hfnum & 7) == 6);
2145d9de2d3SDavid du Colombier }
2155d9de2d3SDavid du Colombier 
2165d9de2d3SDavid du Colombier static int
chandone(void * a)2175d9de2d3SDavid du Colombier chandone(void *a)
2185d9de2d3SDavid du Colombier {
2195d9de2d3SDavid du Colombier 	Hostchan *hc;
2205d9de2d3SDavid du Colombier 
2215d9de2d3SDavid du Colombier 	hc = a;
222fe823997SDavid du Colombier 	if(hc->hcint == (Chhltd|Ack))
223fe823997SDavid du Colombier 		return 0;
2245d9de2d3SDavid du Colombier 	return (hc->hcint & hc->hcintmsk) != 0;
2255d9de2d3SDavid du Colombier }
2265d9de2d3SDavid du Colombier 
2275d9de2d3SDavid du Colombier static int
chanwait(Ep * ep,Ctlr * ctlr,Hostchan * hc,int mask)2285d9de2d3SDavid du Colombier chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
2295d9de2d3SDavid du Colombier {
230*5c47fe09SDavid du Colombier 	int intr, n, ointr;
2315d9de2d3SDavid du Colombier 	ulong start, now;
2325d9de2d3SDavid du Colombier 	Dwcregs *r;
2335d9de2d3SDavid du Colombier 
2345d9de2d3SDavid du Colombier 	r = ctlr->regs;
2355d9de2d3SDavid du Colombier 	n = hc - r->hchan;
2365d9de2d3SDavid du Colombier 	for(;;){
2375d9de2d3SDavid du Colombier restart:
238*5c47fe09SDavid du Colombier 		filock(ctlr);
2395d9de2d3SDavid du Colombier 		r->haintmsk |= 1<<n;
2405d9de2d3SDavid du Colombier 		hc->hcintmsk = mask;
241*5c47fe09SDavid du Colombier 		fiunlock(ctlr);
242*5c47fe09SDavid du Colombier 		tsleep(&ctlr->chanintr[n], chandone, hc, 1000);
243*5c47fe09SDavid du Colombier 		if((intr = hc->hcint) == 0)
244*5c47fe09SDavid du Colombier 			goto restart;
2455d9de2d3SDavid du Colombier 		hc->hcintmsk = 0;
2465d9de2d3SDavid du Colombier 		if(intr & Chhltd)
2475d9de2d3SDavid du Colombier 			return intr;
2485d9de2d3SDavid du Colombier 		start = fastticks(0);
2495d9de2d3SDavid du Colombier 		ointr = intr;
2505d9de2d3SDavid du Colombier 		now = start;
2515d9de2d3SDavid du Colombier 		do{
2525d9de2d3SDavid du Colombier 			intr = hc->hcint;
2535d9de2d3SDavid du Colombier 			if(intr & Chhltd){
2545d9de2d3SDavid du Colombier 				if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
2555d9de2d3SDavid du Colombier 				   intr != (Ack|Chhltd|Xfercomp) ||
2565d9de2d3SDavid du Colombier 				   (now - start) > 60)
257*5c47fe09SDavid du Colombier 					dprint("await %x after %ldµs %x -> %x\n",
2585d9de2d3SDavid du Colombier 						mask, now - start, ointr, intr);
2595d9de2d3SDavid du Colombier 				return intr;
2605d9de2d3SDavid du Colombier 			}
2615d9de2d3SDavid du Colombier 			if((intr & mask) == 0){
262*5c47fe09SDavid du Colombier 				if(intr != Nak)
263*5c47fe09SDavid du Colombier 					dprint("ep%d.%d await %x after %ldµs intr %x -> %x\n",
264*5c47fe09SDavid du Colombier 						ep->dev->nb, ep->nb, mask, now - start, ointr, intr);
2655d9de2d3SDavid du Colombier 				goto restart;
2665d9de2d3SDavid du Colombier 			}
2675d9de2d3SDavid du Colombier 			now = fastticks(0);
2685d9de2d3SDavid du Colombier 		}while(now - start < 100);
2695d9de2d3SDavid du Colombier 		dprint("ep%d.%d halting channel %8.8ux hcchar %8.8ux "
2705d9de2d3SDavid du Colombier 			"grxstsr %8.8ux gnptxsts %8.8ux hptxsts %8.8ux\n",
2715d9de2d3SDavid du Colombier 			ep->dev->nb, ep->nb, intr, hc->hcchar, r->grxstsr,
2725d9de2d3SDavid du Colombier 			r->gnptxsts, r->hptxsts);
2735d9de2d3SDavid du Colombier 		mask = Chhltd;
2745d9de2d3SDavid du Colombier 		hc->hcchar |= Chdis;
2755d9de2d3SDavid du Colombier 		start = m->ticks;
2765d9de2d3SDavid du Colombier 		while(hc->hcchar & Chen){
2775d9de2d3SDavid du Colombier 			if(m->ticks - start >= 100){
2785d9de2d3SDavid du Colombier 				print("ep%d.%d channel won't halt hcchar %8.8ux\n",
2795d9de2d3SDavid du Colombier 					ep->dev->nb, ep->nb, hc->hcchar);
2805d9de2d3SDavid du Colombier 				break;
2815d9de2d3SDavid du Colombier 			}
2825d9de2d3SDavid du Colombier 		}
2835d9de2d3SDavid du Colombier 		logdump(ep);
2845d9de2d3SDavid du Colombier 	}
2855d9de2d3SDavid du Colombier }
2865d9de2d3SDavid du Colombier 
2875d9de2d3SDavid du Colombier static int
chanintr(Ctlr * ctlr,int n)2885d9de2d3SDavid du Colombier chanintr(Ctlr *ctlr, int n)
2895d9de2d3SDavid du Colombier {
2905d9de2d3SDavid du Colombier 	Hostchan *hc;
2915d9de2d3SDavid du Colombier 	int i;
2925d9de2d3SDavid du Colombier 
2935d9de2d3SDavid du Colombier 	hc = &ctlr->regs->hchan[n];
294*5c47fe09SDavid du Colombier 	if((hc->hcint & hc->hcintmsk) == 0)
295*5c47fe09SDavid du Colombier 		return 1;
2965d9de2d3SDavid du Colombier 	if(ctlr->debugchan & (1<<n))
2975d9de2d3SDavid du Colombier 		clog(nil, hc);
2985d9de2d3SDavid du Colombier 	if((hc->hcsplt & Spltena) == 0)
2995d9de2d3SDavid du Colombier 		return 0;
3005d9de2d3SDavid du Colombier 	i = hc->hcint;
3015d9de2d3SDavid du Colombier 	if(i == (Chhltd|Ack)){
3025d9de2d3SDavid du Colombier 		hc->hcsplt |= Compsplt;
3035d9de2d3SDavid du Colombier 		ctlr->splitretry = 0;
3045d9de2d3SDavid du Colombier 	}else if(i == (Chhltd|Nyet)){
3055d9de2d3SDavid du Colombier 		if(++ctlr->splitretry >= 3)
3065d9de2d3SDavid du Colombier 			return 0;
3075d9de2d3SDavid du Colombier 	}else
3085d9de2d3SDavid du Colombier 		return 0;
3095d9de2d3SDavid du Colombier 	if(hc->hcchar & Chen){
3105d9de2d3SDavid du Colombier 		iprint("hcchar %8.8ux hcint %8.8ux", hc->hcchar, hc->hcint);
3115d9de2d3SDavid du Colombier 		hc->hcchar |= Chen | Chdis;
3125d9de2d3SDavid du Colombier 		while(hc->hcchar&Chen)
3135d9de2d3SDavid du Colombier 			;
3145d9de2d3SDavid du Colombier 		iprint(" %8.8ux\n", hc->hcint);
3155d9de2d3SDavid du Colombier 	}
3165d9de2d3SDavid du Colombier 	hc->hcint = i;
3175d9de2d3SDavid du Colombier 	if(ctlr->regs->hfnum & 1)
3185d9de2d3SDavid du Colombier 		hc->hcchar &= ~Oddfrm;
3195d9de2d3SDavid du Colombier 	else
3205d9de2d3SDavid du Colombier 		hc->hcchar |= Oddfrm;
3215d9de2d3SDavid du Colombier 	hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
3225d9de2d3SDavid du Colombier 	return 1;
3235d9de2d3SDavid du Colombier }
3245d9de2d3SDavid du Colombier 
3255d9de2d3SDavid du Colombier static Reg chanlog[32][5];
3265d9de2d3SDavid du Colombier static int nchanlog;
3275d9de2d3SDavid du Colombier 
3285d9de2d3SDavid du Colombier static void
logstart(Ep * ep)3295d9de2d3SDavid du Colombier logstart(Ep *ep)
3305d9de2d3SDavid du Colombier {
3315d9de2d3SDavid du Colombier 	if(ep->debug)
3325d9de2d3SDavid du Colombier 		nchanlog = 0;
3335d9de2d3SDavid du Colombier }
3345d9de2d3SDavid du Colombier 
3355d9de2d3SDavid du Colombier static void
clog(Ep * ep,Hostchan * hc)3365d9de2d3SDavid du Colombier clog(Ep *ep, Hostchan *hc)
3375d9de2d3SDavid du Colombier {
3385d9de2d3SDavid du Colombier 	Reg *p;
3395d9de2d3SDavid du Colombier 
3405d9de2d3SDavid du Colombier 	if(ep != nil && !ep->debug)
3415d9de2d3SDavid du Colombier 		return;
3425d9de2d3SDavid du Colombier 	if(nchanlog == 32)
3435d9de2d3SDavid du Colombier 		nchanlog--;
3445d9de2d3SDavid du Colombier 	p = chanlog[nchanlog];
3455d9de2d3SDavid du Colombier 	p[0] = dwc.regs->hfnum;
3465d9de2d3SDavid du Colombier 	p[1] = hc->hcchar;
3475d9de2d3SDavid du Colombier 	p[2] = hc->hcint;
3485d9de2d3SDavid du Colombier 	p[3] = hc->hctsiz;
3495d9de2d3SDavid du Colombier 	p[4] = hc->hcdma;
3505d9de2d3SDavid du Colombier 	nchanlog++;
3515d9de2d3SDavid du Colombier }
3525d9de2d3SDavid du Colombier 
3535d9de2d3SDavid du Colombier static void
logdump(Ep * ep)3545d9de2d3SDavid du Colombier logdump(Ep *ep)
3555d9de2d3SDavid du Colombier {
3565d9de2d3SDavid du Colombier 	Reg *p;
3575d9de2d3SDavid du Colombier 	int i;
3585d9de2d3SDavid du Colombier 
3595d9de2d3SDavid du Colombier 	if(!ep->debug)
3605d9de2d3SDavid du Colombier 		return;
3615d9de2d3SDavid du Colombier 	p = chanlog[0];
3625d9de2d3SDavid du Colombier 	for(i = 0; i < nchanlog; i++){
3635d9de2d3SDavid du Colombier 		print("%5.5d.%5.5d %8.8ux %8.8ux %8.8ux %8.8ux\n",
3645d9de2d3SDavid du Colombier 			p[0]&0xFFFF, p[0]>>16, p[1], p[2], p[3], p[4]);
3655d9de2d3SDavid du Colombier 		p += 5;
3665d9de2d3SDavid du Colombier 	}
3675d9de2d3SDavid du Colombier 	nchanlog = 0;
3685d9de2d3SDavid du Colombier }
3695d9de2d3SDavid du Colombier 
3705d9de2d3SDavid du Colombier static int
chanio(Ep * ep,Hostchan * hc,int dir,int pid,void * a,int len)3715d9de2d3SDavid du Colombier chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
3725d9de2d3SDavid du Colombier {
3735d9de2d3SDavid du Colombier 	Ctlr *ctlr;
3745d9de2d3SDavid du Colombier 	int nleft, n, nt, i, maxpkt, npkt;
3755d9de2d3SDavid du Colombier 	uint hcdma, hctsiz;
3765d9de2d3SDavid du Colombier 
3775d9de2d3SDavid du Colombier 	ctlr = ep->hp->aux;
3785d9de2d3SDavid du Colombier 	maxpkt = ep->maxpkt;
3795d9de2d3SDavid du Colombier 	npkt = HOWMANY(len, ep->maxpkt);
3805d9de2d3SDavid du Colombier 	if(npkt == 0)
3815d9de2d3SDavid du Colombier 		npkt = 1;
3825d9de2d3SDavid du Colombier 
3835d9de2d3SDavid du Colombier 	hc->hcchar = (hc->hcchar & ~Epdir) | dir;
3845d9de2d3SDavid du Colombier 	if(dir == Epin)
3855d9de2d3SDavid du Colombier 		n = ROUND(len, ep->maxpkt);
3865d9de2d3SDavid du Colombier 	else
3875d9de2d3SDavid du Colombier 		n = len;
3885d9de2d3SDavid du Colombier 	hc->hctsiz = n | npkt<<OPktcnt | pid;
389*5c47fe09SDavid du Colombier 	hc->hcdma  = dmaaddr(a);
3905d9de2d3SDavid du Colombier 
3915d9de2d3SDavid du Colombier 	nleft = len;
3925d9de2d3SDavid du Colombier 	logstart(ep);
3935d9de2d3SDavid du Colombier 	for(;;){
3945d9de2d3SDavid du Colombier 		hcdma = hc->hcdma;
3955d9de2d3SDavid du Colombier 		hctsiz = hc->hctsiz;
3965d9de2d3SDavid du Colombier 		hc->hctsiz = hctsiz & ~Dopng;
3975d9de2d3SDavid du Colombier 		if(hc->hcchar&Chen){
3985d9de2d3SDavid du Colombier 			dprint("ep%d.%d before chanio hcchar=%8.8ux\n",
3995d9de2d3SDavid du Colombier 				ep->dev->nb, ep->nb, hc->hcchar);
4005d9de2d3SDavid du Colombier 			hc->hcchar |= Chen | Chdis;
4015d9de2d3SDavid du Colombier 			while(hc->hcchar&Chen)
4025d9de2d3SDavid du Colombier 				;
4035d9de2d3SDavid du Colombier 			hc->hcint = Chhltd;
4045d9de2d3SDavid du Colombier 		}
4055d9de2d3SDavid du Colombier 		if((i = hc->hcint) != 0){
4065d9de2d3SDavid du Colombier 			dprint("ep%d.%d before chanio hcint=%8.8ux\n",
4075d9de2d3SDavid du Colombier 				ep->dev->nb, ep->nb, i);
4085d9de2d3SDavid du Colombier 			hc->hcint = i;
4095d9de2d3SDavid du Colombier 		}
4105d9de2d3SDavid du Colombier 		if(hc->hcsplt & Spltena){
4115d9de2d3SDavid du Colombier 			qlock(&ctlr->split);
4125d9de2d3SDavid du Colombier 			sofwait(ctlr, hc - ctlr->regs->hchan);
4135d9de2d3SDavid du Colombier 			if((dwc.regs->hfnum & 1) == 0)
4145d9de2d3SDavid du Colombier 				hc->hcchar &= ~Oddfrm;
4155d9de2d3SDavid du Colombier 			else
4165d9de2d3SDavid du Colombier 				hc->hcchar |= Oddfrm;
4175d9de2d3SDavid du Colombier 		}
4185d9de2d3SDavid du Colombier 		hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
4195d9de2d3SDavid du Colombier 		clog(ep, hc);
420*5c47fe09SDavid du Colombier wait:
4215d9de2d3SDavid du Colombier 		if(ep->ttype == Tbulk && dir == Epin)
422*5c47fe09SDavid du Colombier 			i = chanwait(ep, ctlr, hc, Chhltd);
4235d9de2d3SDavid du Colombier 		else if(ep->ttype == Tintr && (hc->hcsplt & Spltena))
4245d9de2d3SDavid du Colombier 			i = chanwait(ep, ctlr, hc, Chhltd);
4255d9de2d3SDavid du Colombier 		else
4265d9de2d3SDavid du Colombier 			i = chanwait(ep, ctlr, hc, Chhltd|Nak);
4275d9de2d3SDavid du Colombier 		clog(ep, hc);
428*5c47fe09SDavid du Colombier 		if(hc->hcint != i){
429*5c47fe09SDavid du Colombier 			dprint("chanwait intr %ux->%ux\n", i, hc->hcint);
430*5c47fe09SDavid du Colombier 			if((i = hc->hcint) == 0)
431*5c47fe09SDavid du Colombier 				goto wait;
432*5c47fe09SDavid du Colombier 		}
4335d9de2d3SDavid du Colombier 		hc->hcint = i;
4345d9de2d3SDavid du Colombier 
4355d9de2d3SDavid du Colombier 		if(hc->hcsplt & Spltena){
4365d9de2d3SDavid du Colombier 			hc->hcsplt &= ~Compsplt;
4375d9de2d3SDavid du Colombier 			qunlock(&ctlr->split);
4385d9de2d3SDavid du Colombier 		}
4395d9de2d3SDavid du Colombier 
4405d9de2d3SDavid du Colombier 		if((i & Xfercomp) == 0 && i != (Chhltd|Ack) && i != Chhltd){
4415d9de2d3SDavid du Colombier 			if(i & Stall)
4425d9de2d3SDavid du Colombier 				error(Estalled);
443fe823997SDavid du Colombier 			if(i & (Nyet|Frmovrun))
4445d9de2d3SDavid du Colombier 				continue;
4455d9de2d3SDavid du Colombier 			if(i & Nak){
4465d9de2d3SDavid du Colombier 				if(ep->ttype == Tintr)
4475d9de2d3SDavid du Colombier 					tsleep(&up->sleep, return0, 0, ep->pollival);
4485d9de2d3SDavid du Colombier 				else
4495d9de2d3SDavid du Colombier 					tsleep(&up->sleep, return0, 0, 1);
4505d9de2d3SDavid du Colombier 				continue;
4515d9de2d3SDavid du Colombier 			}
452fe823997SDavid du Colombier 			logdump(ep);
453*5c47fe09SDavid du Colombier 			print("usbdwc: ep%d.%d error intr %8.8ux\n",
4545d9de2d3SDavid du Colombier 				ep->dev->nb, ep->nb, i);
4555d9de2d3SDavid du Colombier 			if(i & ~(Chhltd|Ack))
4565d9de2d3SDavid du Colombier 				error(Eio);
4575d9de2d3SDavid du Colombier 			if(hc->hcdma != hcdma)
458*5c47fe09SDavid du Colombier 				print("usbdwc: weird hcdma %ux->%ux intr %ux->%ux\n",
4595d9de2d3SDavid du Colombier 					hcdma, hc->hcdma, i, hc->hcint);
4605d9de2d3SDavid du Colombier 		}
4615d9de2d3SDavid du Colombier 		n = hc->hcdma - hcdma;
462b4d1cf41SDavid du Colombier 		if(n == 0){
4635d9de2d3SDavid du Colombier 			if((hc->hctsiz & Pktcnt) != (hctsiz & Pktcnt))
4645d9de2d3SDavid du Colombier 				break;
4655d9de2d3SDavid du Colombier 			else
4665d9de2d3SDavid du Colombier 				continue;
467b4d1cf41SDavid du Colombier 		}
468*5c47fe09SDavid du Colombier 		if(dir == Epin && ep->ttype == Tbulk){
4695d9de2d3SDavid du Colombier 			nt = (hctsiz & Xfersize) - (hc->hctsiz & Xfersize);
470b4d1cf41SDavid du Colombier 			if(nt != n){
471b4d1cf41SDavid du Colombier 				if(n == ROUND(nt, 4))
4725d9de2d3SDavid du Colombier 					n = nt;
4735d9de2d3SDavid du Colombier 				else
474*5c47fe09SDavid du Colombier 					print("usbdwc: intr %8.8ux "
475b4d1cf41SDavid du Colombier 						"dma %8.8ux-%8.8ux "
476b4d1cf41SDavid du Colombier 						"hctsiz %8.8ux-%8.ux\n",
4775d9de2d3SDavid du Colombier 						i, hcdma, hc->hcdma, hctsiz,
4785d9de2d3SDavid du Colombier 						hc->hctsiz);
4795d9de2d3SDavid du Colombier 			}
480b4d1cf41SDavid du Colombier 		}
4815d9de2d3SDavid du Colombier 		if(n > nleft){
482b4d1cf41SDavid du Colombier 			if(n != ROUND(nleft, 4))
4835d9de2d3SDavid du Colombier 				dprint("too much: wanted %d got %d\n",
4845d9de2d3SDavid du Colombier 					len, len - nleft + n);
4855d9de2d3SDavid du Colombier 			n = nleft;
4865d9de2d3SDavid du Colombier 		}
4875d9de2d3SDavid du Colombier 		nleft -= n;
488b4d1cf41SDavid du Colombier 		if(nleft == 0 || (n % maxpkt) != 0)
4895d9de2d3SDavid du Colombier 			break;
4905d9de2d3SDavid du Colombier 		if((i & Xfercomp) && ep->ttype != Tctl)
4915d9de2d3SDavid du Colombier 			break;
4925d9de2d3SDavid du Colombier 		if(dir == Epout)
4935d9de2d3SDavid du Colombier 			dprint("too little: nleft %d hcdma %x->%x hctsiz %x->%x intr %x\n",
4945d9de2d3SDavid du Colombier 				nleft, hcdma, hc->hcdma, hctsiz, hc->hctsiz, i);
4955d9de2d3SDavid du Colombier 	}
4965d9de2d3SDavid du Colombier 	logdump(ep);
4975d9de2d3SDavid du Colombier 	return len - nleft;
4985d9de2d3SDavid du Colombier }
4995d9de2d3SDavid du Colombier 
5005d9de2d3SDavid du Colombier static long
multitrans(Ep * ep,Hostchan * hc,int rw,void * a,long n)501fe823997SDavid du Colombier multitrans(Ep *ep, Hostchan *hc, int rw, void *a, long n)
502fe823997SDavid du Colombier {
503fe823997SDavid du Colombier 	long sofar, m;
504fe823997SDavid du Colombier 
505fe823997SDavid du Colombier 	sofar = 0;
506fe823997SDavid du Colombier 	do{
507fe823997SDavid du Colombier 		m = n - sofar;
508fe823997SDavid du Colombier 		if(m > ep->maxpkt)
509fe823997SDavid du Colombier 			m = ep->maxpkt;
510fe823997SDavid du Colombier 		m = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw],
511fe823997SDavid du Colombier 			(char*)a + sofar, m);
512fe823997SDavid du Colombier 		ep->toggle[rw] = hc->hctsiz & Pid;
513fe823997SDavid du Colombier 		sofar += m;
514fe823997SDavid du Colombier 	}while(sofar < n && m == ep->maxpkt);
515fe823997SDavid du Colombier 	return sofar;
516fe823997SDavid du Colombier }
517fe823997SDavid du Colombier 
518fe823997SDavid du Colombier static long
eptrans(Ep * ep,int rw,void * a,long n)5195d9de2d3SDavid du Colombier eptrans(Ep *ep, int rw, void *a, long n)
5205d9de2d3SDavid du Colombier {
5215d9de2d3SDavid du Colombier 	Hostchan *hc;
5225d9de2d3SDavid du Colombier 
5235d9de2d3SDavid du Colombier 	if(ep->clrhalt){
5245d9de2d3SDavid du Colombier 		ep->clrhalt = 0;
5255d9de2d3SDavid du Colombier 		if(ep->mode != OREAD)
5265d9de2d3SDavid du Colombier 			ep->toggle[Write] = DATA0;
5275d9de2d3SDavid du Colombier 		if(ep->mode != OWRITE)
5285d9de2d3SDavid du Colombier 			ep->toggle[Read] = DATA0;
5295d9de2d3SDavid du Colombier 	}
5305d9de2d3SDavid du Colombier 	hc = chanalloc(ep);
5315d9de2d3SDavid du Colombier 	if(waserror()){
5325d9de2d3SDavid du Colombier 		ep->toggle[rw] = hc->hctsiz & Pid;
5335d9de2d3SDavid du Colombier 		chanrelease(ep, hc);
5345d9de2d3SDavid du Colombier 		if(strcmp(up->errstr, Estalled) == 0)
5355d9de2d3SDavid du Colombier 			return 0;
5365d9de2d3SDavid du Colombier 		nexterror();
5375d9de2d3SDavid du Colombier 	}
5385d9de2d3SDavid du Colombier 	chansetup(hc, ep);
539*5c47fe09SDavid du Colombier 	if(Slowbulkin && rw == Read && ep->ttype == Tbulk)
540fe823997SDavid du Colombier 		n = multitrans(ep, hc, rw, a, n);
541fe823997SDavid du Colombier 	else{
5425d9de2d3SDavid du Colombier 		n = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw],
5435d9de2d3SDavid du Colombier 			a, n);
5445d9de2d3SDavid du Colombier 		ep->toggle[rw] = hc->hctsiz & Pid;
5455d9de2d3SDavid du Colombier 	}
5465d9de2d3SDavid du Colombier 	chanrelease(ep, hc);
5475d9de2d3SDavid du Colombier 	poperror();
5485d9de2d3SDavid du Colombier 	return n;
5495d9de2d3SDavid du Colombier }
5505d9de2d3SDavid du Colombier 
5515d9de2d3SDavid du Colombier static long
ctltrans(Ep * ep,uchar * req,long n)5525d9de2d3SDavid du Colombier ctltrans(Ep *ep, uchar *req, long n)
5535d9de2d3SDavid du Colombier {
5545d9de2d3SDavid du Colombier 	Hostchan *hc;
5555d9de2d3SDavid du Colombier 	Epio *epio;
5565d9de2d3SDavid du Colombier 	Block *b;
5575d9de2d3SDavid du Colombier 	uchar *data;
5585d9de2d3SDavid du Colombier 	int datalen;
5595d9de2d3SDavid du Colombier 
5605d9de2d3SDavid du Colombier 	epio = ep->aux;
5615d9de2d3SDavid du Colombier 	if(epio->cb != nil){
5625d9de2d3SDavid du Colombier 		freeb(epio->cb);
5635d9de2d3SDavid du Colombier 		epio->cb = nil;
5645d9de2d3SDavid du Colombier 	}
5655d9de2d3SDavid du Colombier 	if(n < Rsetuplen)
5665d9de2d3SDavid du Colombier 		error(Ebadlen);
5675d9de2d3SDavid du Colombier 	if(req[Rtype] & Rd2h){
5685d9de2d3SDavid du Colombier 		datalen = GET2(req+Rcount);
5695d9de2d3SDavid du Colombier 		if(datalen <= 0 || datalen > Maxctllen)
5705d9de2d3SDavid du Colombier 			error(Ebadlen);
5715d9de2d3SDavid du Colombier 		/* XXX cache madness */
572*5c47fe09SDavid du Colombier 		epio->cb = b = allocb(ROUND(datalen, ep->maxpkt));
573*5c47fe09SDavid du Colombier 		assert(((uintptr)b->wp & (BLOCKALIGN-1)) == 0);
5745d9de2d3SDavid du Colombier 		memset(b->wp, 0x55, b->lim - b->wp);
5755d9de2d3SDavid du Colombier 		cachedwbinvse(b->wp, b->lim - b->wp);
5765d9de2d3SDavid du Colombier 		data = b->wp;
5775d9de2d3SDavid du Colombier 	}else{
5785d9de2d3SDavid du Colombier 		b = nil;
5795d9de2d3SDavid du Colombier 		datalen = n - Rsetuplen;
5805d9de2d3SDavid du Colombier 		data = req + Rsetuplen;
5815d9de2d3SDavid du Colombier 	}
5825d9de2d3SDavid du Colombier 	hc = chanalloc(ep);
5835d9de2d3SDavid du Colombier 	if(waserror()){
5845d9de2d3SDavid du Colombier 		chanrelease(ep, hc);
5855d9de2d3SDavid du Colombier 		if(strcmp(up->errstr, Estalled) == 0)
5865d9de2d3SDavid du Colombier 			return 0;
5875d9de2d3SDavid du Colombier 		nexterror();
5885d9de2d3SDavid du Colombier 	}
5895d9de2d3SDavid du Colombier 	chansetup(hc, ep);
5905d9de2d3SDavid du Colombier 	chanio(ep, hc, Epout, SETUP, req, Rsetuplen);
5915d9de2d3SDavid du Colombier 	if(req[Rtype] & Rd2h){
592fe823997SDavid du Colombier 		if(ep->dev->hub <= 1){
593fe823997SDavid du Colombier 			ep->toggle[Read] = DATA1;
594fe823997SDavid du Colombier 			b->wp += multitrans(ep, hc, Read, data, datalen);
595fe823997SDavid du Colombier 		}else
5965d9de2d3SDavid du Colombier 			b->wp += chanio(ep, hc, Epin, DATA1, data, datalen);
5975d9de2d3SDavid du Colombier 		chanio(ep, hc, Epout, DATA1, nil, 0);
598*5c47fe09SDavid du Colombier 		cachedinvse(b->rp, BLEN(b));
5995d9de2d3SDavid du Colombier 		n = Rsetuplen;
6005d9de2d3SDavid du Colombier 	}else{
6015d9de2d3SDavid du Colombier 		if(datalen > 0)
6025d9de2d3SDavid du Colombier 			chanio(ep, hc, Epout, DATA1, data, datalen);
6035d9de2d3SDavid du Colombier 		chanio(ep, hc, Epin, DATA1, nil, 0);
6045d9de2d3SDavid du Colombier 		n = Rsetuplen + datalen;
6055d9de2d3SDavid du Colombier 	}
6065d9de2d3SDavid du Colombier 	chanrelease(ep, hc);
6075d9de2d3SDavid du Colombier 	poperror();
6085d9de2d3SDavid du Colombier 	return n;
6095d9de2d3SDavid du Colombier }
6105d9de2d3SDavid du Colombier 
6115d9de2d3SDavid du Colombier static long
ctldata(Ep * ep,void * a,long n)6125d9de2d3SDavid du Colombier ctldata(Ep *ep, void *a, long n)
6135d9de2d3SDavid du Colombier {
6145d9de2d3SDavid du Colombier 	Epio *epio;
6155d9de2d3SDavid du Colombier 	Block *b;
6165d9de2d3SDavid du Colombier 
6175d9de2d3SDavid du Colombier 	epio = ep->aux;
6185d9de2d3SDavid du Colombier 	b = epio->cb;
6195d9de2d3SDavid du Colombier 	if(b == nil)
6205d9de2d3SDavid du Colombier 		return 0;
6215d9de2d3SDavid du Colombier 	if(n > BLEN(b))
6225d9de2d3SDavid du Colombier 		n = BLEN(b);
6235d9de2d3SDavid du Colombier 	memmove(a, b->rp, n);
6245d9de2d3SDavid du Colombier 	b->rp += n;
6255d9de2d3SDavid du Colombier 	if(BLEN(b) == 0){
6265d9de2d3SDavid du Colombier 		freeb(b);
6275d9de2d3SDavid du Colombier 		epio->cb = nil;
6285d9de2d3SDavid du Colombier 	}
6295d9de2d3SDavid du Colombier 	return n;
6305d9de2d3SDavid du Colombier }
6315d9de2d3SDavid du Colombier 
6325d9de2d3SDavid du Colombier static void
greset(Dwcregs * r,int bits)6335d9de2d3SDavid du Colombier greset(Dwcregs *r, int bits)
6345d9de2d3SDavid du Colombier {
6355d9de2d3SDavid du Colombier 	r->grstctl |= bits;
6365d9de2d3SDavid du Colombier 	while(r->grstctl & bits)
6375d9de2d3SDavid du Colombier 		;
6385d9de2d3SDavid du Colombier 	microdelay(10);
6395d9de2d3SDavid du Colombier }
6405d9de2d3SDavid du Colombier 
6415d9de2d3SDavid du Colombier static void
init(Hci * hp)6425d9de2d3SDavid du Colombier init(Hci *hp)
6435d9de2d3SDavid du Colombier {
6445d9de2d3SDavid du Colombier 	Ctlr *ctlr;
6455d9de2d3SDavid du Colombier 	Dwcregs *r;
6465d9de2d3SDavid du Colombier 	uint n, rx, tx, ptx;
6475d9de2d3SDavid du Colombier 
6485d9de2d3SDavid du Colombier 	ctlr = hp->aux;
6495d9de2d3SDavid du Colombier 	r = ctlr->regs;
6505d9de2d3SDavid du Colombier 
6515d9de2d3SDavid du Colombier 	ctlr->nchan = 1 + ((r->ghwcfg2 & Num_host_chan) >> ONum_host_chan);
6525d9de2d3SDavid du Colombier 	ctlr->chanintr = malloc(ctlr->nchan * sizeof(Rendez));
6535d9de2d3SDavid du Colombier 
6545d9de2d3SDavid du Colombier 	r->gahbcfg = 0;
6555d9de2d3SDavid du Colombier 	setpower(PowerUsb, 1);
6565d9de2d3SDavid du Colombier 
6575d9de2d3SDavid du Colombier 	while((r->grstctl&Ahbidle) == 0)
6585d9de2d3SDavid du Colombier 		;
6595d9de2d3SDavid du Colombier 	greset(r, Csftrst);
6605d9de2d3SDavid du Colombier 
6615d9de2d3SDavid du Colombier 	r->gusbcfg |= Force_host_mode;
6625d9de2d3SDavid du Colombier 	tsleep(&up->sleep, return0, 0, 25);
6635d9de2d3SDavid du Colombier 	r->gahbcfg |= Dmaenable;
6645d9de2d3SDavid du Colombier 
6655d9de2d3SDavid du Colombier 	n = (r->ghwcfg3 & Dfifo_depth) >> ODfifo_depth;
6665d9de2d3SDavid du Colombier 	rx = 0x306;
6675d9de2d3SDavid du Colombier 	tx = 0x100;
6685d9de2d3SDavid du Colombier 	ptx = 0x200;
6695d9de2d3SDavid du Colombier 	r->grxfsiz = rx;
6705d9de2d3SDavid du Colombier 	r->gnptxfsiz = rx | tx<<ODepth;
6715d9de2d3SDavid du Colombier 	tsleep(&up->sleep, return0, 0, 1);
6725d9de2d3SDavid du Colombier 	r->hptxfsiz = (rx + tx) | ptx << ODepth;
6735d9de2d3SDavid du Colombier 	greset(r, Rxfflsh);
6745d9de2d3SDavid du Colombier 	r->grstctl = TXF_ALL;
6755d9de2d3SDavid du Colombier 	greset(r, Txfflsh);
676*5c47fe09SDavid du Colombier 	dprint("usbdwc: FIFO depth %d sizes rx/nptx/ptx %8.8ux %8.8ux %8.8ux\n",
6775d9de2d3SDavid du Colombier 		n, r->grxfsiz, r->gnptxfsiz, r->hptxfsiz);
6785d9de2d3SDavid du Colombier 
6795d9de2d3SDavid du Colombier 	r->hport0 = Prtpwr|Prtconndet|Prtenchng|Prtovrcurrchng;
6805d9de2d3SDavid du Colombier 	r->gintsts = ~0;
6815d9de2d3SDavid du Colombier 	r->gintmsk = Hcintr;
6825d9de2d3SDavid du Colombier 	r->gahbcfg |= Glblintrmsk;
6835d9de2d3SDavid du Colombier }
6845d9de2d3SDavid du Colombier 
6855d9de2d3SDavid du Colombier static void
dump(Hci *)6865d9de2d3SDavid du Colombier dump(Hci*)
6875d9de2d3SDavid du Colombier {
6885d9de2d3SDavid du Colombier }
6895d9de2d3SDavid du Colombier 
6905d9de2d3SDavid du Colombier static void
fiqintr(Ureg *,void * a)6915d9de2d3SDavid du Colombier fiqintr(Ureg*, void *a)
6925d9de2d3SDavid du Colombier {
6935d9de2d3SDavid du Colombier 	Hci *hp;
6945d9de2d3SDavid du Colombier 	Ctlr *ctlr;
6955d9de2d3SDavid du Colombier 	Dwcregs *r;
6965d9de2d3SDavid du Colombier 	uint intr, haint, wakechan;
6975d9de2d3SDavid du Colombier 	int i;
6985d9de2d3SDavid du Colombier 
6995d9de2d3SDavid du Colombier 	hp = a;
7005d9de2d3SDavid du Colombier 	ctlr = hp->aux;
7015d9de2d3SDavid du Colombier 	r = ctlr->regs;
7025d9de2d3SDavid du Colombier 	wakechan = 0;
703*5c47fe09SDavid du Colombier 	filock(ctlr);
7045d9de2d3SDavid du Colombier 	intr = r->gintsts;
7055d9de2d3SDavid du Colombier 	if(intr & Hcintr){
7065d9de2d3SDavid du Colombier 		haint = r->haint & r->haintmsk;
7075d9de2d3SDavid du Colombier 		for(i = 0; haint; i++){
708b4d1cf41SDavid du Colombier 			if(haint & 1){
709b4d1cf41SDavid du Colombier 				if(chanintr(ctlr, i) == 0){
7105d9de2d3SDavid du Colombier 					r->haintmsk &= ~(1<<i);
7115d9de2d3SDavid du Colombier 					wakechan |= 1<<i;
7125d9de2d3SDavid du Colombier 				}
713b4d1cf41SDavid du Colombier 			}
7145d9de2d3SDavid du Colombier 			haint >>= 1;
7155d9de2d3SDavid du Colombier 		}
7165d9de2d3SDavid du Colombier 	}
7175d9de2d3SDavid du Colombier 	if(intr & Sofintr){
7185d9de2d3SDavid du Colombier 		r->gintsts = Sofintr;
7195d9de2d3SDavid du Colombier 		if((r->hfnum&7) != 6){
7205d9de2d3SDavid du Colombier 			r->gintmsk &= ~Sofintr;
7215d9de2d3SDavid du Colombier 			wakechan |= ctlr->sofchan;
7225d9de2d3SDavid du Colombier 			ctlr->sofchan = 0;
7235d9de2d3SDavid du Colombier 		}
7245d9de2d3SDavid du Colombier 	}
7255d9de2d3SDavid du Colombier 	if(wakechan){
7265d9de2d3SDavid du Colombier 		ctlr->wakechan |= wakechan;
7275d9de2d3SDavid du Colombier 		armtimerset(1);
7285d9de2d3SDavid du Colombier 	}
729*5c47fe09SDavid du Colombier 	fiunlock(ctlr);
7305d9de2d3SDavid du Colombier }
7315d9de2d3SDavid du Colombier 
7325d9de2d3SDavid du Colombier static void
irqintr(Ureg *,void * a)7335d9de2d3SDavid du Colombier irqintr(Ureg*, void *a)
7345d9de2d3SDavid du Colombier {
7355d9de2d3SDavid du Colombier 	Ctlr *ctlr;
7365d9de2d3SDavid du Colombier 	uint wakechan;
737*5c47fe09SDavid du Colombier 	int i;
7385d9de2d3SDavid du Colombier 
7395d9de2d3SDavid du Colombier 	ctlr = a;
740*5c47fe09SDavid du Colombier 	filock(ctlr);
7415d9de2d3SDavid du Colombier 	armtimerset(0);
7425d9de2d3SDavid du Colombier 	wakechan = ctlr->wakechan;
7435d9de2d3SDavid du Colombier 	ctlr->wakechan = 0;
744*5c47fe09SDavid du Colombier 	fiunlock(ctlr);
7455d9de2d3SDavid du Colombier 	for(i = 0; wakechan; i++){
7465d9de2d3SDavid du Colombier 		if(wakechan & 1)
7475d9de2d3SDavid du Colombier 			wakeup(&ctlr->chanintr[i]);
7485d9de2d3SDavid du Colombier 		wakechan >>= 1;
7495d9de2d3SDavid du Colombier 	}
7505d9de2d3SDavid du Colombier }
7515d9de2d3SDavid du Colombier 
7525d9de2d3SDavid du Colombier static void
epopen(Ep * ep)7535d9de2d3SDavid du Colombier epopen(Ep *ep)
7545d9de2d3SDavid du Colombier {
755*5c47fe09SDavid du Colombier 	ddprint("usbdwc: epopen ep%d.%d ttype %d\n",
7565d9de2d3SDavid du Colombier 		ep->dev->nb, ep->nb, ep->ttype);
7575d9de2d3SDavid du Colombier 	switch(ep->ttype){
7585d9de2d3SDavid du Colombier 	case Tnone:
759a587111cSDavid du Colombier 		error(Enotconf);
7605d9de2d3SDavid du Colombier 	case Tintr:
7615d9de2d3SDavid du Colombier 		assert(ep->pollival > 0);
7625d9de2d3SDavid du Colombier 		/* fall through */
7635d9de2d3SDavid du Colombier 	case Tbulk:
7645d9de2d3SDavid du Colombier 		if(ep->toggle[Read] == 0)
7655d9de2d3SDavid du Colombier 			ep->toggle[Read] = DATA0;
7665d9de2d3SDavid du Colombier 		if(ep->toggle[Write] == 0)
7675d9de2d3SDavid du Colombier 			ep->toggle[Write] = DATA0;
7685d9de2d3SDavid du Colombier 		break;
7695d9de2d3SDavid du Colombier 	}
7705d9de2d3SDavid du Colombier 	ep->aux = malloc(sizeof(Epio));
7715d9de2d3SDavid du Colombier 	if(ep->aux == nil)
7725d9de2d3SDavid du Colombier 		error(Enomem);
7735d9de2d3SDavid du Colombier }
7745d9de2d3SDavid du Colombier 
7755d9de2d3SDavid du Colombier static void
epclose(Ep * ep)7765d9de2d3SDavid du Colombier epclose(Ep *ep)
7775d9de2d3SDavid du Colombier {
778*5c47fe09SDavid du Colombier 	ddprint("usbdwc: epclose ep%d.%d ttype %d\n",
7795d9de2d3SDavid du Colombier 		ep->dev->nb, ep->nb, ep->ttype);
7805d9de2d3SDavid du Colombier 	switch(ep->ttype){
7815d9de2d3SDavid du Colombier 	case Tctl:
7825d9de2d3SDavid du Colombier 		freeb(((Epio*)ep->aux)->cb);
7835d9de2d3SDavid du Colombier 		/* fall through */
7845d9de2d3SDavid du Colombier 	default:
7855d9de2d3SDavid du Colombier 		free(ep->aux);
7865d9de2d3SDavid du Colombier 		break;
7875d9de2d3SDavid du Colombier 	}
7885d9de2d3SDavid du Colombier }
7895d9de2d3SDavid du Colombier 
7905d9de2d3SDavid du Colombier static long
epread(Ep * ep,void * a,long n)7915d9de2d3SDavid du Colombier epread(Ep *ep, void *a, long n)
7925d9de2d3SDavid du Colombier {
7935d9de2d3SDavid du Colombier 	Epio *epio;
794*5c47fe09SDavid du Colombier 	QLock *q;
7955d9de2d3SDavid du Colombier 	Block *b;
7965d9de2d3SDavid du Colombier 	uchar *p;
7975d9de2d3SDavid du Colombier 	ulong elapsed;
7985d9de2d3SDavid du Colombier 	long nr;
7995d9de2d3SDavid du Colombier 
8005d9de2d3SDavid du Colombier 	ddprint("epread ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
8015d9de2d3SDavid du Colombier 	epio = ep->aux;
802*5c47fe09SDavid du Colombier 	q = ep->ttype == Tctl? &epio->ctllock : &epio->rlock;
8035d9de2d3SDavid du Colombier 	b = nil;
804*5c47fe09SDavid du Colombier 	qlock(q);
8055d9de2d3SDavid du Colombier 	if(waserror()){
806*5c47fe09SDavid du Colombier 		qunlock(q);
8075d9de2d3SDavid du Colombier 		if(b)
8085d9de2d3SDavid du Colombier 			freeb(b);
8095d9de2d3SDavid du Colombier 		nexterror();
8105d9de2d3SDavid du Colombier 	}
8115d9de2d3SDavid du Colombier 	switch(ep->ttype){
8125d9de2d3SDavid du Colombier 	default:
8135d9de2d3SDavid du Colombier 		error(Egreg);
8145d9de2d3SDavid du Colombier 	case Tctl:
8155d9de2d3SDavid du Colombier 		nr = ctldata(ep, a, n);
816*5c47fe09SDavid du Colombier 		qunlock(q);
8175d9de2d3SDavid du Colombier 		poperror();
8185d9de2d3SDavid du Colombier 		return nr;
8195d9de2d3SDavid du Colombier 	case Tintr:
8205d9de2d3SDavid du Colombier 		elapsed = TK2MS(m->ticks) - epio->lastpoll;
8215d9de2d3SDavid du Colombier 		if(elapsed < ep->pollival)
8225d9de2d3SDavid du Colombier 			tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
8235d9de2d3SDavid du Colombier 		/* fall through */
8245d9de2d3SDavid du Colombier 	case Tbulk:
8255d9de2d3SDavid du Colombier 		/* XXX cache madness */
826*5c47fe09SDavid du Colombier 		b = allocb(ROUND(n, ep->maxpkt));
827*5c47fe09SDavid du Colombier 		p = b->rp;
828*5c47fe09SDavid du Colombier 		assert(((uintptr)p & (BLOCKALIGN-1)) == 0);
829*5c47fe09SDavid du Colombier 		cachedinvse(p, n);
8305d9de2d3SDavid du Colombier 		nr = eptrans(ep, Read, p, n);
831*5c47fe09SDavid du Colombier 		cachedinvse(p, nr);
8325d9de2d3SDavid du Colombier 		epio->lastpoll = TK2MS(m->ticks);
8335d9de2d3SDavid du Colombier 		memmove(a, p, nr);
834*5c47fe09SDavid du Colombier 		qunlock(q);
8355d9de2d3SDavid du Colombier 		freeb(b);
8365d9de2d3SDavid du Colombier 		poperror();
8375d9de2d3SDavid du Colombier 		return nr;
8385d9de2d3SDavid du Colombier 	}
8395d9de2d3SDavid du Colombier }
8405d9de2d3SDavid du Colombier 
8415d9de2d3SDavid du Colombier static long
epwrite(Ep * ep,void * a,long n)8425d9de2d3SDavid du Colombier epwrite(Ep *ep, void *a, long n)
8435d9de2d3SDavid du Colombier {
8445d9de2d3SDavid du Colombier 	Epio *epio;
845*5c47fe09SDavid du Colombier 	QLock *q;
8465d9de2d3SDavid du Colombier 	Block *b;
8475d9de2d3SDavid du Colombier 	uchar *p;
8485d9de2d3SDavid du Colombier 	ulong elapsed;
8495d9de2d3SDavid du Colombier 
8505d9de2d3SDavid du Colombier 	ddprint("epwrite ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
8515d9de2d3SDavid du Colombier 	epio = ep->aux;
852*5c47fe09SDavid du Colombier 	q = ep->ttype == Tctl? &epio->ctllock : &epio->wlock;
8535d9de2d3SDavid du Colombier 	b = nil;
854*5c47fe09SDavid du Colombier 	qlock(q);
8555d9de2d3SDavid du Colombier 	if(waserror()){
856*5c47fe09SDavid du Colombier 		qunlock(q);
8575d9de2d3SDavid du Colombier 		if(b)
8585d9de2d3SDavid du Colombier 			freeb(b);
8595d9de2d3SDavid du Colombier 		nexterror();
8605d9de2d3SDavid du Colombier 	}
8615d9de2d3SDavid du Colombier 	switch(ep->ttype){
8625d9de2d3SDavid du Colombier 	default:
8635d9de2d3SDavid du Colombier 		error(Egreg);
8645d9de2d3SDavid du Colombier 	case Tintr:
8655d9de2d3SDavid du Colombier 		elapsed = TK2MS(m->ticks) - epio->lastpoll;
8665d9de2d3SDavid du Colombier 		if(elapsed < ep->pollival)
8675d9de2d3SDavid du Colombier 			tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
8685d9de2d3SDavid du Colombier 		/* fall through */
8695d9de2d3SDavid du Colombier 	case Tctl:
8705d9de2d3SDavid du Colombier 	case Tbulk:
8715d9de2d3SDavid du Colombier 		/* XXX cache madness */
872*5c47fe09SDavid du Colombier 		b = allocb(n);
873*5c47fe09SDavid du Colombier 		p = b->wp;
874*5c47fe09SDavid du Colombier 		assert(((uintptr)p & (BLOCKALIGN-1)) == 0);
8755d9de2d3SDavid du Colombier 		memmove(p, a, n);
8765d9de2d3SDavid du Colombier 		cachedwbse(p, n);
8775d9de2d3SDavid du Colombier 		if(ep->ttype == Tctl)
8785d9de2d3SDavid du Colombier 			n = ctltrans(ep, p, n);
8795d9de2d3SDavid du Colombier 		else{
8805d9de2d3SDavid du Colombier 			n = eptrans(ep, Write, p, n);
8815d9de2d3SDavid du Colombier 			epio->lastpoll = TK2MS(m->ticks);
8825d9de2d3SDavid du Colombier 		}
883*5c47fe09SDavid du Colombier 		qunlock(q);
8845d9de2d3SDavid du Colombier 		freeb(b);
8855d9de2d3SDavid du Colombier 		poperror();
8865d9de2d3SDavid du Colombier 		return n;
8875d9de2d3SDavid du Colombier 	}
8885d9de2d3SDavid du Colombier }
8895d9de2d3SDavid du Colombier 
8905d9de2d3SDavid du Colombier static char*
seprintep(char * s,char *,Ep *)8915d9de2d3SDavid du Colombier seprintep(char *s, char*, Ep*)
8925d9de2d3SDavid du Colombier {
8935d9de2d3SDavid du Colombier 	return s;
8945d9de2d3SDavid du Colombier }
8955d9de2d3SDavid du Colombier 
8965d9de2d3SDavid du Colombier static int
portenable(Hci * hp,int port,int on)8975d9de2d3SDavid du Colombier portenable(Hci *hp, int port, int on)
8985d9de2d3SDavid du Colombier {
8995d9de2d3SDavid du Colombier 	Ctlr *ctlr;
9005d9de2d3SDavid du Colombier 	Dwcregs *r;
9015d9de2d3SDavid du Colombier 
9025d9de2d3SDavid du Colombier 	assert(port == 1);
9035d9de2d3SDavid du Colombier 	ctlr = hp->aux;
9045d9de2d3SDavid du Colombier 	r = ctlr->regs;
905*5c47fe09SDavid du Colombier 	dprint("usbdwc enable=%d; sts %#x\n", on, r->hport0);
9065d9de2d3SDavid du Colombier 	if(!on)
9075d9de2d3SDavid du Colombier 		r->hport0 = Prtpwr | Prtena;
9085d9de2d3SDavid du Colombier 	tsleep(&up->sleep, return0, 0, Enabledelay);
909*5c47fe09SDavid du Colombier 	dprint("usbdwc enable=%d; sts %#x\n", on, r->hport0);
9105d9de2d3SDavid du Colombier 	return 0;
9115d9de2d3SDavid du Colombier }
9125d9de2d3SDavid du Colombier 
9135d9de2d3SDavid du Colombier static int
portreset(Hci * hp,int port,int on)9145d9de2d3SDavid du Colombier portreset(Hci *hp, int port, int on)
9155d9de2d3SDavid du Colombier {
9165d9de2d3SDavid du Colombier 	Ctlr *ctlr;
9175d9de2d3SDavid du Colombier 	Dwcregs *r;
9185d9de2d3SDavid du Colombier 	int b, s;
9195d9de2d3SDavid du Colombier 
9205d9de2d3SDavid du Colombier 	assert(port == 1);
9215d9de2d3SDavid du Colombier 	ctlr = hp->aux;
9225d9de2d3SDavid du Colombier 	r = ctlr->regs;
923*5c47fe09SDavid du Colombier 	dprint("usbdwc reset=%d; sts %#x\n", on, r->hport0);
9245d9de2d3SDavid du Colombier 	if(!on)
9255d9de2d3SDavid du Colombier 		return 0;
9265d9de2d3SDavid du Colombier 	r->hport0 = Prtpwr | Prtrst;
9275d9de2d3SDavid du Colombier 	tsleep(&up->sleep, return0, 0, ResetdelayHS);
9285d9de2d3SDavid du Colombier 	r->hport0 = Prtpwr;
9295d9de2d3SDavid du Colombier 	tsleep(&up->sleep, return0, 0, Enabledelay);
9305d9de2d3SDavid du Colombier 	s = r->hport0;
9315d9de2d3SDavid du Colombier 	b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
9325d9de2d3SDavid du Colombier 	if(b != 0)
9335d9de2d3SDavid du Colombier 		r->hport0 = Prtpwr | b;
934*5c47fe09SDavid du Colombier 	dprint("usbdwc reset=%d; sts %#x\n", on, s);
9355d9de2d3SDavid du Colombier 	if((s & Prtena) == 0)
936*5c47fe09SDavid du Colombier 		print("usbdwc: host port not enabled after reset");
9375d9de2d3SDavid du Colombier 	return 0;
9385d9de2d3SDavid du Colombier }
9395d9de2d3SDavid du Colombier 
9405d9de2d3SDavid du Colombier static int
portstatus(Hci * hp,int port)9415d9de2d3SDavid du Colombier portstatus(Hci *hp, int port)
9425d9de2d3SDavid du Colombier {
9435d9de2d3SDavid du Colombier 	Ctlr *ctlr;
9445d9de2d3SDavid du Colombier 	Dwcregs *r;
9455d9de2d3SDavid du Colombier 	int b, s;
9465d9de2d3SDavid du Colombier 
9475d9de2d3SDavid du Colombier 	assert(port == 1);
9485d9de2d3SDavid du Colombier 	ctlr = hp->aux;
9495d9de2d3SDavid du Colombier 	r = ctlr->regs;
9505d9de2d3SDavid du Colombier 	s = r->hport0;
9515d9de2d3SDavid du Colombier 	b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
9525d9de2d3SDavid du Colombier 	if(b != 0)
9535d9de2d3SDavid du Colombier 		r->hport0 = Prtpwr | b;
9545d9de2d3SDavid du Colombier 	b = 0;
9555d9de2d3SDavid du Colombier 	if(s & Prtconnsts)
9565d9de2d3SDavid du Colombier 		b |= HPpresent;
9575d9de2d3SDavid du Colombier 	if(s & Prtconndet)
9585d9de2d3SDavid du Colombier 		b |= HPstatuschg;
9595d9de2d3SDavid du Colombier 	if(s & Prtena)
9605d9de2d3SDavid du Colombier 		b |= HPenable;
9615d9de2d3SDavid du Colombier 	if(s & Prtenchng)
9625d9de2d3SDavid du Colombier 		b |= HPchange;
9635d9de2d3SDavid du Colombier 	if(s & Prtovrcurract)
9645d9de2d3SDavid du Colombier 		 b |= HPovercurrent;
9655d9de2d3SDavid du Colombier 	if(s & Prtsusp)
9665d9de2d3SDavid du Colombier 		b |= HPsuspend;
9675d9de2d3SDavid du Colombier 	if(s & Prtrst)
9685d9de2d3SDavid du Colombier 		b |= HPreset;
9695d9de2d3SDavid du Colombier 	if(s & Prtpwr)
9705d9de2d3SDavid du Colombier 		b |= HPpower;
9715d9de2d3SDavid du Colombier 	switch(s & Prtspd){
9725d9de2d3SDavid du Colombier 	case HIGHSPEED:
9735d9de2d3SDavid du Colombier 		b |= HPhigh;
9745d9de2d3SDavid du Colombier 		break;
9755d9de2d3SDavid du Colombier 	case LOWSPEED:
9765d9de2d3SDavid du Colombier 		b |= HPslow;
9775d9de2d3SDavid du Colombier 		break;
9785d9de2d3SDavid du Colombier 	}
9795d9de2d3SDavid du Colombier 	return b;
9805d9de2d3SDavid du Colombier }
9815d9de2d3SDavid du Colombier 
9825d9de2d3SDavid du Colombier static void
shutdown(Hci *)9835d9de2d3SDavid du Colombier shutdown(Hci*)
9845d9de2d3SDavid du Colombier {
9855d9de2d3SDavid du Colombier }
9865d9de2d3SDavid du Colombier 
9875d9de2d3SDavid du Colombier static void
setdebug(Hci *,int d)9885d9de2d3SDavid du Colombier setdebug(Hci*, int d)
9895d9de2d3SDavid du Colombier {
9905d9de2d3SDavid du Colombier 	debug = d;
9915d9de2d3SDavid du Colombier }
9925d9de2d3SDavid du Colombier 
9935d9de2d3SDavid du Colombier static int
reset(Hci * hp)9945d9de2d3SDavid du Colombier reset(Hci *hp)
9955d9de2d3SDavid du Colombier {
9965d9de2d3SDavid du Colombier 	Ctlr *ctlr;
9975d9de2d3SDavid du Colombier 	uint id;
9985d9de2d3SDavid du Colombier 
9995d9de2d3SDavid du Colombier 	ctlr = &dwc;
10005d9de2d3SDavid du Colombier 	if(ctlr->regs != nil)
10015d9de2d3SDavid du Colombier 		return -1;
10025d9de2d3SDavid du Colombier 	ctlr->regs = (Dwcregs*)USBREGS;
10035d9de2d3SDavid du Colombier 	id = ctlr->regs->gsnpsid;
10045d9de2d3SDavid du Colombier 	if((id>>16) != ('O'<<8 | 'T'))
10055d9de2d3SDavid du Colombier 		return -1;
1006*5c47fe09SDavid du Colombier 	dprint("usbdwc: rev %d.%3.3x\n", (id>>12)&0xF, id&0xFFF);
10075d9de2d3SDavid du Colombier 
10085d9de2d3SDavid du Colombier 	intrenable(IRQtimerArm, irqintr, ctlr, 0, "dwc");
10095d9de2d3SDavid du Colombier 
10105d9de2d3SDavid du Colombier 	hp->aux = ctlr;
10115d9de2d3SDavid du Colombier 	hp->port = 0;
10125d9de2d3SDavid du Colombier 	hp->irq = IRQusb;
10135d9de2d3SDavid du Colombier 	hp->tbdf = 0;
10145d9de2d3SDavid du Colombier 	hp->nports = 1;
10155d9de2d3SDavid du Colombier 	hp->highspeed = 1;
10165d9de2d3SDavid du Colombier 
10175d9de2d3SDavid du Colombier 	hp->init = init;
10185d9de2d3SDavid du Colombier 	hp->dump = dump;
10195d9de2d3SDavid du Colombier 	hp->interrupt = fiqintr;
10205d9de2d3SDavid du Colombier 	hp->epopen = epopen;
10215d9de2d3SDavid du Colombier 	hp->epclose = epclose;
10225d9de2d3SDavid du Colombier 	hp->epread = epread;
10235d9de2d3SDavid du Colombier 	hp->epwrite = epwrite;
10245d9de2d3SDavid du Colombier 	hp->seprintep = seprintep;
10255d9de2d3SDavid du Colombier 	hp->portenable = portenable;
10265d9de2d3SDavid du Colombier 	hp->portreset = portreset;
10275d9de2d3SDavid du Colombier 	hp->portstatus = portstatus;
10285d9de2d3SDavid du Colombier 	hp->shutdown = shutdown;
10295d9de2d3SDavid du Colombier 	hp->debug = setdebug;
10305d9de2d3SDavid du Colombier 	hp->type = "dwcotg";
10315d9de2d3SDavid du Colombier 	return 0;
10325d9de2d3SDavid du Colombier }
10335d9de2d3SDavid du Colombier 
10345d9de2d3SDavid du Colombier void
usbdwclink(void)10355d9de2d3SDavid du Colombier usbdwclink(void)
10365d9de2d3SDavid du Colombier {
10375d9de2d3SDavid du Colombier 	addhcitype("dwcotg", reset);
10385d9de2d3SDavid du Colombier }
1039