xref: /plan9/sys/src/9/bcm/usbdwc.c (revision a587111c8770e522e3667ff2b63cba8a77811dd9)
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"
235d9de2d3SDavid du Colombier #include	"../port/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,
365d9de2d3SDavid du Colombier };
375d9de2d3SDavid du Colombier 
385d9de2d3SDavid du Colombier typedef struct Ctlr Ctlr;
395d9de2d3SDavid du Colombier typedef struct Epio Epio;
405d9de2d3SDavid du Colombier 
415d9de2d3SDavid du Colombier struct Ctlr {
425d9de2d3SDavid du Colombier 	Dwcregs	*regs;		/* controller registers */
435d9de2d3SDavid du Colombier 	int	nchan;		/* number of host channels */
445d9de2d3SDavid du Colombier 	ulong	chanbusy;	/* bitmap of in-use channels */
455d9de2d3SDavid du Colombier 	QLock	chanlock;	/* serialise access to chanbusy */
465d9de2d3SDavid du Colombier 	QLock	split;		/* serialise split transactions */
475d9de2d3SDavid du Colombier 	int	splitretry;	/* count retries of Nyet */
485d9de2d3SDavid du Colombier 	int	sofchan;	/* bitmap of channels waiting for sof */
495d9de2d3SDavid du Colombier 	int	wakechan;	/* bitmap of channels to wakeup after fiq */
505d9de2d3SDavid du Colombier 	int	debugchan;	/* bitmap of channels for interrupt debug */
515d9de2d3SDavid du Colombier 	Rendez	*chanintr;	/* sleep till interrupt on channel N */
525d9de2d3SDavid du Colombier };
535d9de2d3SDavid du Colombier 
545d9de2d3SDavid du Colombier struct Epio {
555d9de2d3SDavid du Colombier 	QLock;
565d9de2d3SDavid du Colombier 	Block	*cb;
575d9de2d3SDavid du Colombier 	ulong	lastpoll;
585d9de2d3SDavid du Colombier };
595d9de2d3SDavid du Colombier 
605d9de2d3SDavid du Colombier static Ctlr dwc;
615d9de2d3SDavid du Colombier static int debug;
625d9de2d3SDavid du Colombier 
635d9de2d3SDavid du Colombier static char Ebadlen[] = "bad usb request length";
645d9de2d3SDavid du Colombier 
655d9de2d3SDavid du Colombier static void clog(Ep *ep, Hostchan *hc);
665d9de2d3SDavid du Colombier static void logdump(Ep *ep);
675d9de2d3SDavid du Colombier 
685d9de2d3SDavid du Colombier static Hostchan*
chanalloc(Ep * ep)695d9de2d3SDavid du Colombier chanalloc(Ep *ep)
705d9de2d3SDavid du Colombier {
715d9de2d3SDavid du Colombier 	Ctlr *ctlr;
725d9de2d3SDavid du Colombier 	int bitmap, i;
735d9de2d3SDavid du Colombier 
745d9de2d3SDavid du Colombier 	ctlr = ep->hp->aux;
755d9de2d3SDavid du Colombier 	qlock(&ctlr->chanlock);
765d9de2d3SDavid du Colombier 	bitmap = ctlr->chanbusy;
775d9de2d3SDavid du Colombier 	for(i = 0; i < ctlr->nchan; i++)
785d9de2d3SDavid du Colombier 		if((bitmap & (1<<i)) == 0){
795d9de2d3SDavid du Colombier 			ctlr->chanbusy = bitmap | 1<<i;
805d9de2d3SDavid du Colombier 			qunlock(&ctlr->chanlock);
815d9de2d3SDavid du Colombier 			return &ctlr->regs->hchan[i];
825d9de2d3SDavid du Colombier 		}
835d9de2d3SDavid du Colombier 	qunlock(&ctlr->chanlock);
845d9de2d3SDavid du Colombier 	panic("miller is a lazy git");
855d9de2d3SDavid du Colombier 	return nil;
865d9de2d3SDavid du Colombier }
875d9de2d3SDavid du Colombier 
885d9de2d3SDavid du Colombier static void
chanrelease(Ep * ep,Hostchan * chan)895d9de2d3SDavid du Colombier chanrelease(Ep *ep, Hostchan *chan)
905d9de2d3SDavid du Colombier {
915d9de2d3SDavid du Colombier 	Ctlr *ctlr;
925d9de2d3SDavid du Colombier 	int i;
935d9de2d3SDavid du Colombier 
945d9de2d3SDavid du Colombier 	ctlr = ep->hp->aux;
955d9de2d3SDavid du Colombier 	i = chan - ctlr->regs->hchan;
965d9de2d3SDavid du Colombier 	qlock(&ctlr->chanlock);
975d9de2d3SDavid du Colombier 	ctlr->chanbusy &= ~(1<<i);
985d9de2d3SDavid du Colombier 	qunlock(&ctlr->chanlock);
995d9de2d3SDavid du Colombier }
1005d9de2d3SDavid du Colombier 
1015d9de2d3SDavid du Colombier static void
chansetup(Hostchan * hc,Ep * ep)1025d9de2d3SDavid du Colombier chansetup(Hostchan *hc, Ep *ep)
1035d9de2d3SDavid du Colombier {
1045d9de2d3SDavid du Colombier 	int hcc;
1055d9de2d3SDavid du Colombier 	Ctlr *ctlr = ep->hp->aux;
1065d9de2d3SDavid du Colombier 
1075d9de2d3SDavid du Colombier 	if(ep->debug)
1085d9de2d3SDavid du Colombier 		ctlr->debugchan |= 1 << (hc - ctlr->regs->hchan);
1095d9de2d3SDavid du Colombier 	else
1105d9de2d3SDavid du Colombier 		ctlr->debugchan &= ~(1 << (hc - ctlr->regs->hchan));
1115d9de2d3SDavid du Colombier 	switch(ep->dev->state){
1125d9de2d3SDavid du Colombier 	case Dconfig:
1135d9de2d3SDavid du Colombier 	case Dreset:
1145d9de2d3SDavid du Colombier 		hcc = 0;
1155d9de2d3SDavid du Colombier 		break;
1165d9de2d3SDavid du Colombier 	default:
1175d9de2d3SDavid du Colombier 		hcc = ep->dev->nb<<ODevaddr;
1185d9de2d3SDavid du Colombier 		break;
1195d9de2d3SDavid du Colombier 	}
1205d9de2d3SDavid du Colombier 	hcc |= ep->maxpkt | 1<<OMulticnt | ep->nb<<OEpnum;
1215d9de2d3SDavid du Colombier 	switch(ep->ttype){
1225d9de2d3SDavid du Colombier 	case Tctl:
1235d9de2d3SDavid du Colombier 		hcc |= Epctl;
1245d9de2d3SDavid du Colombier 		break;
1255d9de2d3SDavid du Colombier 	case Tiso:
1265d9de2d3SDavid du Colombier 		hcc |= Episo;
1275d9de2d3SDavid du Colombier 		break;
1285d9de2d3SDavid du Colombier 	case Tbulk:
1295d9de2d3SDavid du Colombier 		hcc |= Epbulk;
1305d9de2d3SDavid du Colombier 		break;
1315d9de2d3SDavid du Colombier 	case Tintr:
1325d9de2d3SDavid du Colombier 		hcc |= Epintr;
1335d9de2d3SDavid du Colombier 		break;
1345d9de2d3SDavid du Colombier 	}
1355d9de2d3SDavid du Colombier 	switch(ep->dev->speed){
1365d9de2d3SDavid du Colombier 	case Lowspeed:
1375d9de2d3SDavid du Colombier 		hcc |= Lspddev;
1385d9de2d3SDavid du Colombier 		/* fall through */
1395d9de2d3SDavid du Colombier 	case Fullspeed:
140fe823997SDavid du Colombier 		if(ep->dev->hub > 1){
1415d9de2d3SDavid du Colombier 			hc->hcsplt = Spltena | POS_ALL | ep->dev->hub<<OHubaddr |
1425d9de2d3SDavid du Colombier 				ep->dev->port;
1435d9de2d3SDavid du Colombier 			break;
144fe823997SDavid du Colombier 		}
145fe823997SDavid du Colombier 		/* fall through */
1465d9de2d3SDavid du Colombier 	default:
1475d9de2d3SDavid du Colombier 		hc->hcsplt = 0;
1485d9de2d3SDavid du Colombier 		break;
1495d9de2d3SDavid du Colombier 	}
1505d9de2d3SDavid du Colombier 	hc->hcchar = hcc;
1515d9de2d3SDavid du Colombier 	hc->hcint = ~0;
1525d9de2d3SDavid du Colombier }
1535d9de2d3SDavid du Colombier 
1545d9de2d3SDavid du Colombier static int
sofdone(void * a)1555d9de2d3SDavid du Colombier sofdone(void *a)
1565d9de2d3SDavid du Colombier {
1575d9de2d3SDavid du Colombier 	Dwcregs *r;
1585d9de2d3SDavid du Colombier 
1595d9de2d3SDavid du Colombier 	r = a;
1605d9de2d3SDavid du Colombier 	return r->gintsts & Sofintr;
1615d9de2d3SDavid du Colombier }
1625d9de2d3SDavid du Colombier 
1635d9de2d3SDavid du Colombier static void
sofwait(Ctlr * ctlr,int n)1645d9de2d3SDavid du Colombier sofwait(Ctlr *ctlr, int n)
1655d9de2d3SDavid du Colombier {
1665d9de2d3SDavid du Colombier 	Dwcregs *r;
1675d9de2d3SDavid du Colombier 	int x;
1685d9de2d3SDavid du Colombier 
1695d9de2d3SDavid du Colombier 	r = ctlr->regs;
1705d9de2d3SDavid du Colombier 	do{
1715d9de2d3SDavid du Colombier 		r->gintsts = Sofintr;
1725d9de2d3SDavid du Colombier 		x = splfhi();
1735d9de2d3SDavid du Colombier 		ctlr->sofchan |= 1<<n;
1745d9de2d3SDavid du Colombier 		r->gintmsk |= Sofintr;
1755d9de2d3SDavid du Colombier 		sleep(&ctlr->chanintr[n], sofdone, r);
1765d9de2d3SDavid du Colombier 		splx(x);
1775d9de2d3SDavid du Colombier 	}while((r->hfnum & 7) == 6);
1785d9de2d3SDavid du Colombier }
1795d9de2d3SDavid du Colombier 
1805d9de2d3SDavid du Colombier static int
chandone(void * a)1815d9de2d3SDavid du Colombier chandone(void *a)
1825d9de2d3SDavid du Colombier {
1835d9de2d3SDavid du Colombier 	Hostchan *hc;
1845d9de2d3SDavid du Colombier 
1855d9de2d3SDavid du Colombier 	hc = a;
186fe823997SDavid du Colombier 	if(hc->hcint == (Chhltd|Ack))
187fe823997SDavid du Colombier 		return 0;
1885d9de2d3SDavid du Colombier 	return (hc->hcint & hc->hcintmsk) != 0;
1895d9de2d3SDavid du Colombier }
1905d9de2d3SDavid du Colombier 
1915d9de2d3SDavid du Colombier static int
chanwait(Ep * ep,Ctlr * ctlr,Hostchan * hc,int mask)1925d9de2d3SDavid du Colombier chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
1935d9de2d3SDavid du Colombier {
1945d9de2d3SDavid du Colombier 	int intr, n, x, ointr;
1955d9de2d3SDavid du Colombier 	ulong start, now;
1965d9de2d3SDavid du Colombier 	Dwcregs *r;
1975d9de2d3SDavid du Colombier 
1985d9de2d3SDavid du Colombier 	r = ctlr->regs;
1995d9de2d3SDavid du Colombier 	n = hc - r->hchan;
2005d9de2d3SDavid du Colombier 	for(;;){
2015d9de2d3SDavid du Colombier restart:
2025d9de2d3SDavid du Colombier 		x = splfhi();
2035d9de2d3SDavid du Colombier 		r->haintmsk |= 1<<n;
2045d9de2d3SDavid du Colombier 		hc->hcintmsk = mask;
2055d9de2d3SDavid du Colombier 		sleep(&ctlr->chanintr[n], chandone, hc);
2065d9de2d3SDavid du Colombier 		hc->hcintmsk = 0;
2075d9de2d3SDavid du Colombier 		splx(x);
2085d9de2d3SDavid du Colombier 		intr = hc->hcint;
2095d9de2d3SDavid du Colombier 		if(intr & Chhltd)
2105d9de2d3SDavid du Colombier 			return intr;
2115d9de2d3SDavid du Colombier 		start = fastticks(0);
2125d9de2d3SDavid du Colombier 		ointr = intr;
2135d9de2d3SDavid du Colombier 		now = start;
2145d9de2d3SDavid du Colombier 		do{
2155d9de2d3SDavid du Colombier 			intr = hc->hcint;
2165d9de2d3SDavid du Colombier 			if(intr & Chhltd){
2175d9de2d3SDavid du Colombier 				if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
2185d9de2d3SDavid du Colombier 				   intr != (Ack|Chhltd|Xfercomp) ||
2195d9de2d3SDavid du Colombier 				   (now - start) > 60)
2205d9de2d3SDavid du Colombier 					dprint("await %x after %ld %x -> %x\n",
2215d9de2d3SDavid du Colombier 						mask, now - start, ointr, intr);
2225d9de2d3SDavid du Colombier 				return intr;
2235d9de2d3SDavid du Colombier 			}
2245d9de2d3SDavid du Colombier 			if((intr & mask) == 0){
225b4d1cf41SDavid du Colombier 				dprint("ep%d.%d await %x intr %x -> %x\n",
226b4d1cf41SDavid du Colombier 					ep->dev->nb, ep->nb, mask, ointr, intr);
2275d9de2d3SDavid du Colombier 				goto restart;
2285d9de2d3SDavid du Colombier 			}
2295d9de2d3SDavid du Colombier 			now = fastticks(0);
2305d9de2d3SDavid du Colombier 		}while(now - start < 100);
2315d9de2d3SDavid du Colombier 		dprint("ep%d.%d halting channel %8.8ux hcchar %8.8ux "
2325d9de2d3SDavid du Colombier 			"grxstsr %8.8ux gnptxsts %8.8ux hptxsts %8.8ux\n",
2335d9de2d3SDavid du Colombier 			ep->dev->nb, ep->nb, intr, hc->hcchar, r->grxstsr,
2345d9de2d3SDavid du Colombier 			r->gnptxsts, r->hptxsts);
2355d9de2d3SDavid du Colombier 		mask = Chhltd;
2365d9de2d3SDavid du Colombier 		hc->hcchar |= Chdis;
2375d9de2d3SDavid du Colombier 		start = m->ticks;
2385d9de2d3SDavid du Colombier 		while(hc->hcchar & Chen){
2395d9de2d3SDavid du Colombier 			if(m->ticks - start >= 100){
2405d9de2d3SDavid du Colombier 				print("ep%d.%d channel won't halt hcchar %8.8ux\n",
2415d9de2d3SDavid du Colombier 					ep->dev->nb, ep->nb, hc->hcchar);
2425d9de2d3SDavid du Colombier 				break;
2435d9de2d3SDavid du Colombier 			}
2445d9de2d3SDavid du Colombier 		}
2455d9de2d3SDavid du Colombier 		logdump(ep);
2465d9de2d3SDavid du Colombier 	}
2475d9de2d3SDavid du Colombier }
2485d9de2d3SDavid du Colombier 
2495d9de2d3SDavid du Colombier static int
chanintr(Ctlr * ctlr,int n)2505d9de2d3SDavid du Colombier chanintr(Ctlr *ctlr, int n)
2515d9de2d3SDavid du Colombier {
2525d9de2d3SDavid du Colombier 	Hostchan *hc;
2535d9de2d3SDavid du Colombier 	int i;
2545d9de2d3SDavid du Colombier 
2555d9de2d3SDavid du Colombier 	hc = &ctlr->regs->hchan[n];
2565d9de2d3SDavid du Colombier 	if(ctlr->debugchan & (1<<n))
2575d9de2d3SDavid du Colombier 		clog(nil, hc);
2585d9de2d3SDavid du Colombier 	if((hc->hcsplt & Spltena) == 0)
2595d9de2d3SDavid du Colombier 		return 0;
2605d9de2d3SDavid du Colombier 	i = hc->hcint;
2615d9de2d3SDavid du Colombier 	if(i == (Chhltd|Ack)){
2625d9de2d3SDavid du Colombier 		hc->hcsplt |= Compsplt;
2635d9de2d3SDavid du Colombier 		ctlr->splitretry = 0;
2645d9de2d3SDavid du Colombier 	}else if(i == (Chhltd|Nyet)){
2655d9de2d3SDavid du Colombier 		if(++ctlr->splitretry >= 3)
2665d9de2d3SDavid du Colombier 			return 0;
2675d9de2d3SDavid du Colombier 	}else
2685d9de2d3SDavid du Colombier 		return 0;
2695d9de2d3SDavid du Colombier 	if(hc->hcchar & Chen){
2705d9de2d3SDavid du Colombier 		iprint("hcchar %8.8ux hcint %8.8ux", hc->hcchar, hc->hcint);
2715d9de2d3SDavid du Colombier 		hc->hcchar |= Chen | Chdis;
2725d9de2d3SDavid du Colombier 		while(hc->hcchar&Chen)
2735d9de2d3SDavid du Colombier 			;
2745d9de2d3SDavid du Colombier 		iprint(" %8.8ux\n", hc->hcint);
2755d9de2d3SDavid du Colombier 	}
2765d9de2d3SDavid du Colombier 	hc->hcint = i;
2775d9de2d3SDavid du Colombier 	if(ctlr->regs->hfnum & 1)
2785d9de2d3SDavid du Colombier 		hc->hcchar &= ~Oddfrm;
2795d9de2d3SDavid du Colombier 	else
2805d9de2d3SDavid du Colombier 		hc->hcchar |= Oddfrm;
2815d9de2d3SDavid du Colombier 	hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
2825d9de2d3SDavid du Colombier 	return 1;
2835d9de2d3SDavid du Colombier }
2845d9de2d3SDavid du Colombier 
2855d9de2d3SDavid du Colombier static Reg chanlog[32][5];
2865d9de2d3SDavid du Colombier static int nchanlog;
2875d9de2d3SDavid du Colombier 
2885d9de2d3SDavid du Colombier static void
logstart(Ep * ep)2895d9de2d3SDavid du Colombier logstart(Ep *ep)
2905d9de2d3SDavid du Colombier {
2915d9de2d3SDavid du Colombier 	if(ep->debug)
2925d9de2d3SDavid du Colombier 		nchanlog = 0;
2935d9de2d3SDavid du Colombier }
2945d9de2d3SDavid du Colombier 
2955d9de2d3SDavid du Colombier static void
clog(Ep * ep,Hostchan * hc)2965d9de2d3SDavid du Colombier clog(Ep *ep, Hostchan *hc)
2975d9de2d3SDavid du Colombier {
2985d9de2d3SDavid du Colombier 	Reg *p;
2995d9de2d3SDavid du Colombier 
3005d9de2d3SDavid du Colombier 	if(ep != nil && !ep->debug)
3015d9de2d3SDavid du Colombier 		return;
3025d9de2d3SDavid du Colombier 	if(nchanlog == 32)
3035d9de2d3SDavid du Colombier 		nchanlog--;
3045d9de2d3SDavid du Colombier 	p = chanlog[nchanlog];
3055d9de2d3SDavid du Colombier 	p[0] = dwc.regs->hfnum;
3065d9de2d3SDavid du Colombier 	p[1] = hc->hcchar;
3075d9de2d3SDavid du Colombier 	p[2] = hc->hcint;
3085d9de2d3SDavid du Colombier 	p[3] = hc->hctsiz;
3095d9de2d3SDavid du Colombier 	p[4] = hc->hcdma;
3105d9de2d3SDavid du Colombier 	nchanlog++;
3115d9de2d3SDavid du Colombier }
3125d9de2d3SDavid du Colombier 
3135d9de2d3SDavid du Colombier static void
logdump(Ep * ep)3145d9de2d3SDavid du Colombier logdump(Ep *ep)
3155d9de2d3SDavid du Colombier {
3165d9de2d3SDavid du Colombier 	Reg *p;
3175d9de2d3SDavid du Colombier 	int i;
3185d9de2d3SDavid du Colombier 
3195d9de2d3SDavid du Colombier 	if(!ep->debug)
3205d9de2d3SDavid du Colombier 		return;
3215d9de2d3SDavid du Colombier 	p = chanlog[0];
3225d9de2d3SDavid du Colombier 	for(i = 0; i < nchanlog; i++){
3235d9de2d3SDavid du Colombier 		print("%5.5d.%5.5d %8.8ux %8.8ux %8.8ux %8.8ux\n",
3245d9de2d3SDavid du Colombier 			p[0]&0xFFFF, p[0]>>16, p[1], p[2], p[3], p[4]);
3255d9de2d3SDavid du Colombier 		p += 5;
3265d9de2d3SDavid du Colombier 	}
3275d9de2d3SDavid du Colombier 	nchanlog = 0;
3285d9de2d3SDavid du Colombier }
3295d9de2d3SDavid du Colombier 
3305d9de2d3SDavid du Colombier static int
chanio(Ep * ep,Hostchan * hc,int dir,int pid,void * a,int len)3315d9de2d3SDavid du Colombier chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
3325d9de2d3SDavid du Colombier {
3335d9de2d3SDavid du Colombier 	Ctlr *ctlr;
3345d9de2d3SDavid du Colombier 	int nleft, n, nt, i, maxpkt, npkt;
3355d9de2d3SDavid du Colombier 	uint hcdma, hctsiz;
3365d9de2d3SDavid du Colombier 
3375d9de2d3SDavid du Colombier 	ctlr = ep->hp->aux;
3385d9de2d3SDavid du Colombier 	maxpkt = ep->maxpkt;
3395d9de2d3SDavid du Colombier 	npkt = HOWMANY(len, ep->maxpkt);
3405d9de2d3SDavid du Colombier 	if(npkt == 0)
3415d9de2d3SDavid du Colombier 		npkt = 1;
3425d9de2d3SDavid du Colombier 
3435d9de2d3SDavid du Colombier 	hc->hcchar = (hc->hcchar & ~Epdir) | dir;
3445d9de2d3SDavid du Colombier 	if(dir == Epin)
3455d9de2d3SDavid du Colombier 		n = ROUND(len, ep->maxpkt);
3465d9de2d3SDavid du Colombier 	else
3475d9de2d3SDavid du Colombier 		n = len;
3485d9de2d3SDavid du Colombier 	hc->hctsiz = n | npkt<<OPktcnt | pid;
3495d9de2d3SDavid du Colombier 	hc->hcdma  = PADDR(a);
3505d9de2d3SDavid du Colombier 
3515d9de2d3SDavid du Colombier 	nleft = len;
3525d9de2d3SDavid du Colombier 	logstart(ep);
3535d9de2d3SDavid du Colombier 	for(;;){
3545d9de2d3SDavid du Colombier 		hcdma = hc->hcdma;
3555d9de2d3SDavid du Colombier 		hctsiz = hc->hctsiz;
3565d9de2d3SDavid du Colombier 		hc->hctsiz = hctsiz & ~Dopng;
3575d9de2d3SDavid du Colombier 		if(hc->hcchar&Chen){
3585d9de2d3SDavid du Colombier 			dprint("ep%d.%d before chanio hcchar=%8.8ux\n",
3595d9de2d3SDavid du Colombier 				ep->dev->nb, ep->nb, hc->hcchar);
3605d9de2d3SDavid du Colombier 			hc->hcchar |= Chen | Chdis;
3615d9de2d3SDavid du Colombier 			while(hc->hcchar&Chen)
3625d9de2d3SDavid du Colombier 				;
3635d9de2d3SDavid du Colombier 			hc->hcint = Chhltd;
3645d9de2d3SDavid du Colombier 		}
3655d9de2d3SDavid du Colombier 		if((i = hc->hcint) != 0){
3665d9de2d3SDavid du Colombier 			dprint("ep%d.%d before chanio hcint=%8.8ux\n",
3675d9de2d3SDavid du Colombier 				ep->dev->nb, ep->nb, i);
3685d9de2d3SDavid du Colombier 			hc->hcint = i;
3695d9de2d3SDavid du Colombier 		}
3705d9de2d3SDavid du Colombier 		if(hc->hcsplt & Spltena){
3715d9de2d3SDavid du Colombier 			qlock(&ctlr->split);
3725d9de2d3SDavid du Colombier 			sofwait(ctlr, hc - ctlr->regs->hchan);
3735d9de2d3SDavid du Colombier 			if((dwc.regs->hfnum & 1) == 0)
3745d9de2d3SDavid du Colombier 				hc->hcchar &= ~Oddfrm;
3755d9de2d3SDavid du Colombier 			else
3765d9de2d3SDavid du Colombier 				hc->hcchar |= Oddfrm;
3775d9de2d3SDavid du Colombier 		}
3785d9de2d3SDavid du Colombier 		hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
3795d9de2d3SDavid du Colombier 		clog(ep, hc);
3805d9de2d3SDavid du Colombier 		if(ep->ttype == Tbulk && dir == Epin)
3815d9de2d3SDavid du Colombier 			i = chanwait(ep, ctlr, hc, /* Ack| */ Chhltd);
3825d9de2d3SDavid du Colombier 		else if(ep->ttype == Tintr && (hc->hcsplt & Spltena))
3835d9de2d3SDavid du Colombier 			i = chanwait(ep, ctlr, hc, Chhltd);
3845d9de2d3SDavid du Colombier 		else
3855d9de2d3SDavid du Colombier 			i = chanwait(ep, ctlr, hc, Chhltd|Nak);
3865d9de2d3SDavid du Colombier 		clog(ep, hc);
3875d9de2d3SDavid du Colombier 		hc->hcint = i;
3885d9de2d3SDavid du Colombier 
3895d9de2d3SDavid du Colombier 		if(hc->hcsplt & Spltena){
3905d9de2d3SDavid du Colombier 			hc->hcsplt &= ~Compsplt;
3915d9de2d3SDavid du Colombier 			qunlock(&ctlr->split);
3925d9de2d3SDavid du Colombier 		}
3935d9de2d3SDavid du Colombier 
3945d9de2d3SDavid du Colombier 		if((i & Xfercomp) == 0 && i != (Chhltd|Ack) && i != Chhltd){
3955d9de2d3SDavid du Colombier 			if(i & Stall)
3965d9de2d3SDavid du Colombier 				error(Estalled);
397fe823997SDavid du Colombier 			if(i & (Nyet|Frmovrun))
3985d9de2d3SDavid du Colombier 				continue;
3995d9de2d3SDavid du Colombier 			if(i & Nak){
4005d9de2d3SDavid du Colombier 				if(ep->ttype == Tintr)
4015d9de2d3SDavid du Colombier 					tsleep(&up->sleep, return0, 0, ep->pollival);
4025d9de2d3SDavid du Colombier 				else
4035d9de2d3SDavid du Colombier 					tsleep(&up->sleep, return0, 0, 1);
4045d9de2d3SDavid du Colombier 				continue;
4055d9de2d3SDavid du Colombier 			}
406fe823997SDavid du Colombier 			logdump(ep);
4075d9de2d3SDavid du Colombier 			print("usbotg: ep%d.%d error intr %8.8ux\n",
4085d9de2d3SDavid du Colombier 				ep->dev->nb, ep->nb, i);
4095d9de2d3SDavid du Colombier 			if(i & ~(Chhltd|Ack))
4105d9de2d3SDavid du Colombier 				error(Eio);
4115d9de2d3SDavid du Colombier 			if(hc->hcdma != hcdma)
4125d9de2d3SDavid du Colombier 				print("usbotg: weird hcdma %x->%x intr %x->%x\n",
4135d9de2d3SDavid du Colombier 					hcdma, hc->hcdma, i, hc->hcint);
4145d9de2d3SDavid du Colombier 		}
4155d9de2d3SDavid du Colombier 		n = hc->hcdma - hcdma;
416b4d1cf41SDavid du Colombier 		if(n == 0){
4175d9de2d3SDavid du Colombier 			if((hc->hctsiz & Pktcnt) != (hctsiz & Pktcnt))
4185d9de2d3SDavid du Colombier 				break;
4195d9de2d3SDavid du Colombier 			else
4205d9de2d3SDavid du Colombier 				continue;
421b4d1cf41SDavid du Colombier 		}
4225d9de2d3SDavid du Colombier 		if(dir == Epin && ep->ttype == Tbulk && n == nleft){
4235d9de2d3SDavid du Colombier 			nt = (hctsiz & Xfersize) - (hc->hctsiz & Xfersize);
424b4d1cf41SDavid du Colombier 			if(nt != n){
425b4d1cf41SDavid du Colombier 				if(n == ROUND(nt, 4))
4265d9de2d3SDavid du Colombier 					n = nt;
4275d9de2d3SDavid du Colombier 				else
428b4d1cf41SDavid du Colombier 					print("usbotg: intr %8.8ux "
429b4d1cf41SDavid du Colombier 						"dma %8.8ux-%8.8ux "
430b4d1cf41SDavid du Colombier 						"hctsiz %8.8ux-%8.ux\n",
4315d9de2d3SDavid du Colombier 						i, hcdma, hc->hcdma, hctsiz,
4325d9de2d3SDavid du Colombier 						hc->hctsiz);
4335d9de2d3SDavid du Colombier 			}
434b4d1cf41SDavid du Colombier 		}
4355d9de2d3SDavid du Colombier 		if(n > nleft){
436b4d1cf41SDavid du Colombier 			if(n != ROUND(nleft, 4))
4375d9de2d3SDavid du Colombier 				dprint("too much: wanted %d got %d\n",
4385d9de2d3SDavid du Colombier 					len, len - nleft + n);
4395d9de2d3SDavid du Colombier 			n = nleft;
4405d9de2d3SDavid du Colombier 		}
4415d9de2d3SDavid du Colombier 		nleft -= n;
442b4d1cf41SDavid du Colombier 		if(nleft == 0 || (n % maxpkt) != 0)
4435d9de2d3SDavid du Colombier 			break;
4445d9de2d3SDavid du Colombier 		if((i & Xfercomp) && ep->ttype != Tctl)
4455d9de2d3SDavid du Colombier 			break;
4465d9de2d3SDavid du Colombier 		if(dir == Epout)
4475d9de2d3SDavid du Colombier 			dprint("too little: nleft %d hcdma %x->%x hctsiz %x->%x intr %x\n",
4485d9de2d3SDavid du Colombier 				nleft, hcdma, hc->hcdma, hctsiz, hc->hctsiz, i);
4495d9de2d3SDavid du Colombier 	}
4505d9de2d3SDavid du Colombier 	logdump(ep);
4515d9de2d3SDavid du Colombier 	return len - nleft;
4525d9de2d3SDavid du Colombier }
4535d9de2d3SDavid du Colombier 
4545d9de2d3SDavid du Colombier static long
multitrans(Ep * ep,Hostchan * hc,int rw,void * a,long n)455fe823997SDavid du Colombier multitrans(Ep *ep, Hostchan *hc, int rw, void *a, long n)
456fe823997SDavid du Colombier {
457fe823997SDavid du Colombier 	long sofar, m;
458fe823997SDavid du Colombier 
459fe823997SDavid du Colombier 	sofar = 0;
460fe823997SDavid du Colombier 	do{
461fe823997SDavid du Colombier 		m = n - sofar;
462fe823997SDavid du Colombier 		if(m > ep->maxpkt)
463fe823997SDavid du Colombier 			m = ep->maxpkt;
464fe823997SDavid du Colombier 		m = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw],
465fe823997SDavid du Colombier 			(char*)a + sofar, m);
466fe823997SDavid du Colombier 		ep->toggle[rw] = hc->hctsiz & Pid;
467fe823997SDavid du Colombier 		sofar += m;
468fe823997SDavid du Colombier 	}while(sofar < n && m == ep->maxpkt);
469fe823997SDavid du Colombier 	return sofar;
470fe823997SDavid du Colombier }
471fe823997SDavid du Colombier 
472fe823997SDavid du Colombier static long
eptrans(Ep * ep,int rw,void * a,long n)4735d9de2d3SDavid du Colombier eptrans(Ep *ep, int rw, void *a, long n)
4745d9de2d3SDavid du Colombier {
4755d9de2d3SDavid du Colombier 	Hostchan *hc;
4765d9de2d3SDavid du Colombier 
4775d9de2d3SDavid du Colombier 	if(ep->clrhalt){
4785d9de2d3SDavid du Colombier 		ep->clrhalt = 0;
4795d9de2d3SDavid du Colombier 		if(ep->mode != OREAD)
4805d9de2d3SDavid du Colombier 			ep->toggle[Write] = DATA0;
4815d9de2d3SDavid du Colombier 		if(ep->mode != OWRITE)
4825d9de2d3SDavid du Colombier 			ep->toggle[Read] = DATA0;
4835d9de2d3SDavid du Colombier 	}
4845d9de2d3SDavid du Colombier 	hc = chanalloc(ep);
4855d9de2d3SDavid du Colombier 	if(waserror()){
4865d9de2d3SDavid du Colombier 		ep->toggle[rw] = hc->hctsiz & Pid;
4875d9de2d3SDavid du Colombier 		chanrelease(ep, hc);
4885d9de2d3SDavid du Colombier 		if(strcmp(up->errstr, Estalled) == 0)
4895d9de2d3SDavid du Colombier 			return 0;
4905d9de2d3SDavid du Colombier 		nexterror();
4915d9de2d3SDavid du Colombier 	}
4925d9de2d3SDavid du Colombier 	chansetup(hc, ep);
493fe823997SDavid du Colombier 	if(rw == Read && ep->ttype == Tbulk)
494fe823997SDavid du Colombier 		n = multitrans(ep, hc, rw, a, n);
495fe823997SDavid du Colombier 	else{
4965d9de2d3SDavid du Colombier 		n = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw],
4975d9de2d3SDavid du Colombier 			a, n);
4985d9de2d3SDavid du Colombier 		ep->toggle[rw] = hc->hctsiz & Pid;
4995d9de2d3SDavid du Colombier 	}
5005d9de2d3SDavid du Colombier 	chanrelease(ep, hc);
5015d9de2d3SDavid du Colombier 	poperror();
5025d9de2d3SDavid du Colombier 	return n;
5035d9de2d3SDavid du Colombier }
5045d9de2d3SDavid du Colombier 
5055d9de2d3SDavid du Colombier static long
ctltrans(Ep * ep,uchar * req,long n)5065d9de2d3SDavid du Colombier ctltrans(Ep *ep, uchar *req, long n)
5075d9de2d3SDavid du Colombier {
5085d9de2d3SDavid du Colombier 	Hostchan *hc;
5095d9de2d3SDavid du Colombier 	Epio *epio;
5105d9de2d3SDavid du Colombier 	Block *b;
5115d9de2d3SDavid du Colombier 	uchar *data;
5125d9de2d3SDavid du Colombier 	int datalen;
5135d9de2d3SDavid du Colombier 
5145d9de2d3SDavid du Colombier 	epio = ep->aux;
5155d9de2d3SDavid du Colombier 	if(epio->cb != nil){
5165d9de2d3SDavid du Colombier 		freeb(epio->cb);
5175d9de2d3SDavid du Colombier 		epio->cb = nil;
5185d9de2d3SDavid du Colombier 	}
5195d9de2d3SDavid du Colombier 	if(n < Rsetuplen)
5205d9de2d3SDavid du Colombier 		error(Ebadlen);
5215d9de2d3SDavid du Colombier 	if(req[Rtype] & Rd2h){
5225d9de2d3SDavid du Colombier 		datalen = GET2(req+Rcount);
5235d9de2d3SDavid du Colombier 		if(datalen <= 0 || datalen > Maxctllen)
5245d9de2d3SDavid du Colombier 			error(Ebadlen);
5255d9de2d3SDavid du Colombier 		/* XXX cache madness */
5265d9de2d3SDavid du Colombier 		epio->cb = b = allocb(ROUND(datalen, ep->maxpkt) + CACHELINESZ);
5275d9de2d3SDavid du Colombier 		b->wp = (uchar*)ROUND((uintptr)b->wp, CACHELINESZ);
5285d9de2d3SDavid du Colombier 		memset(b->wp, 0x55, b->lim - b->wp);
5295d9de2d3SDavid du Colombier 		cachedwbinvse(b->wp, b->lim - b->wp);
5305d9de2d3SDavid du Colombier 		data = b->wp;
5315d9de2d3SDavid du Colombier 	}else{
5325d9de2d3SDavid du Colombier 		b = nil;
5335d9de2d3SDavid du Colombier 		datalen = n - Rsetuplen;
5345d9de2d3SDavid du Colombier 		data = req + Rsetuplen;
5355d9de2d3SDavid du Colombier 	}
5365d9de2d3SDavid du Colombier 	hc = chanalloc(ep);
5375d9de2d3SDavid du Colombier 	if(waserror()){
5385d9de2d3SDavid du Colombier 		chanrelease(ep, hc);
5395d9de2d3SDavid du Colombier 		if(strcmp(up->errstr, Estalled) == 0)
5405d9de2d3SDavid du Colombier 			return 0;
5415d9de2d3SDavid du Colombier 		nexterror();
5425d9de2d3SDavid du Colombier 	}
5435d9de2d3SDavid du Colombier 	chansetup(hc, ep);
5445d9de2d3SDavid du Colombier 	chanio(ep, hc, Epout, SETUP, req, Rsetuplen);
5455d9de2d3SDavid du Colombier 	if(req[Rtype] & Rd2h){
546fe823997SDavid du Colombier 		if(ep->dev->hub <= 1){
547fe823997SDavid du Colombier 			ep->toggle[Read] = DATA1;
548fe823997SDavid du Colombier 			b->wp += multitrans(ep, hc, Read, data, datalen);
549fe823997SDavid du Colombier 		}else
5505d9de2d3SDavid du Colombier 			b->wp += chanio(ep, hc, Epin, DATA1, data, datalen);
5515d9de2d3SDavid du Colombier 		chanio(ep, hc, Epout, DATA1, nil, 0);
5525d9de2d3SDavid du Colombier 		n = Rsetuplen;
5535d9de2d3SDavid du Colombier 	}else{
5545d9de2d3SDavid du Colombier 		if(datalen > 0)
5555d9de2d3SDavid du Colombier 			chanio(ep, hc, Epout, DATA1, data, datalen);
5565d9de2d3SDavid du Colombier 		chanio(ep, hc, Epin, DATA1, nil, 0);
5575d9de2d3SDavid du Colombier 		n = Rsetuplen + datalen;
5585d9de2d3SDavid du Colombier 	}
5595d9de2d3SDavid du Colombier 	chanrelease(ep, hc);
5605d9de2d3SDavid du Colombier 	poperror();
5615d9de2d3SDavid du Colombier 	return n;
5625d9de2d3SDavid du Colombier }
5635d9de2d3SDavid du Colombier 
5645d9de2d3SDavid du Colombier static long
ctldata(Ep * ep,void * a,long n)5655d9de2d3SDavid du Colombier ctldata(Ep *ep, void *a, long n)
5665d9de2d3SDavid du Colombier {
5675d9de2d3SDavid du Colombier 	Epio *epio;
5685d9de2d3SDavid du Colombier 	Block *b;
5695d9de2d3SDavid du Colombier 
5705d9de2d3SDavid du Colombier 	epio = ep->aux;
5715d9de2d3SDavid du Colombier 	b = epio->cb;
5725d9de2d3SDavid du Colombier 	if(b == nil)
5735d9de2d3SDavid du Colombier 		return 0;
5745d9de2d3SDavid du Colombier 	if(n > BLEN(b))
5755d9de2d3SDavid du Colombier 		n = BLEN(b);
5765d9de2d3SDavid du Colombier 	memmove(a, b->rp, n);
5775d9de2d3SDavid du Colombier 	b->rp += n;
5785d9de2d3SDavid du Colombier 	if(BLEN(b) == 0){
5795d9de2d3SDavid du Colombier 		freeb(b);
5805d9de2d3SDavid du Colombier 		epio->cb = nil;
5815d9de2d3SDavid du Colombier 	}
5825d9de2d3SDavid du Colombier 	return n;
5835d9de2d3SDavid du Colombier }
5845d9de2d3SDavid du Colombier 
5855d9de2d3SDavid du Colombier static void
greset(Dwcregs * r,int bits)5865d9de2d3SDavid du Colombier greset(Dwcregs *r, int bits)
5875d9de2d3SDavid du Colombier {
5885d9de2d3SDavid du Colombier 	r->grstctl |= bits;
5895d9de2d3SDavid du Colombier 	while(r->grstctl & bits)
5905d9de2d3SDavid du Colombier 		;
5915d9de2d3SDavid du Colombier 	microdelay(10);
5925d9de2d3SDavid du Colombier }
5935d9de2d3SDavid du Colombier 
5945d9de2d3SDavid du Colombier static void
init(Hci * hp)5955d9de2d3SDavid du Colombier init(Hci *hp)
5965d9de2d3SDavid du Colombier {
5975d9de2d3SDavid du Colombier 	Ctlr *ctlr;
5985d9de2d3SDavid du Colombier 	Dwcregs *r;
5995d9de2d3SDavid du Colombier 	uint n, rx, tx, ptx;
6005d9de2d3SDavid du Colombier 
6015d9de2d3SDavid du Colombier 	ctlr = hp->aux;
6025d9de2d3SDavid du Colombier 	r = ctlr->regs;
6035d9de2d3SDavid du Colombier 
6045d9de2d3SDavid du Colombier 	ctlr->nchan = 1 + ((r->ghwcfg2 & Num_host_chan) >> ONum_host_chan);
6055d9de2d3SDavid du Colombier 	ctlr->chanintr = malloc(ctlr->nchan * sizeof(Rendez));
6065d9de2d3SDavid du Colombier 
6075d9de2d3SDavid du Colombier 	r->gahbcfg = 0;
6085d9de2d3SDavid du Colombier 	setpower(PowerUsb, 1);
6095d9de2d3SDavid du Colombier 
6105d9de2d3SDavid du Colombier 	while((r->grstctl&Ahbidle) == 0)
6115d9de2d3SDavid du Colombier 		;
6125d9de2d3SDavid du Colombier 	greset(r, Csftrst);
6135d9de2d3SDavid du Colombier 
6145d9de2d3SDavid du Colombier 	r->gusbcfg |= Force_host_mode;
6155d9de2d3SDavid du Colombier 	tsleep(&up->sleep, return0, 0, 25);
6165d9de2d3SDavid du Colombier 	r->gahbcfg |= Dmaenable;
6175d9de2d3SDavid du Colombier 
6185d9de2d3SDavid du Colombier 	n = (r->ghwcfg3 & Dfifo_depth) >> ODfifo_depth;
6195d9de2d3SDavid du Colombier 	rx = 0x306;
6205d9de2d3SDavid du Colombier 	tx = 0x100;
6215d9de2d3SDavid du Colombier 	ptx = 0x200;
6225d9de2d3SDavid du Colombier 	r->grxfsiz = rx;
6235d9de2d3SDavid du Colombier 	r->gnptxfsiz = rx | tx<<ODepth;
6245d9de2d3SDavid du Colombier 	tsleep(&up->sleep, return0, 0, 1);
6255d9de2d3SDavid du Colombier 	r->hptxfsiz = (rx + tx) | ptx << ODepth;
6265d9de2d3SDavid du Colombier 	greset(r, Rxfflsh);
6275d9de2d3SDavid du Colombier 	r->grstctl = TXF_ALL;
6285d9de2d3SDavid du Colombier 	greset(r, Txfflsh);
6295d9de2d3SDavid du Colombier 	dprint("usbotg: FIFO depth %d sizes rx/nptx/ptx %8.8ux %8.8ux %8.8ux\n",
6305d9de2d3SDavid du Colombier 		n, r->grxfsiz, r->gnptxfsiz, r->hptxfsiz);
6315d9de2d3SDavid du Colombier 
6325d9de2d3SDavid du Colombier 	r->hport0 = Prtpwr|Prtconndet|Prtenchng|Prtovrcurrchng;
6335d9de2d3SDavid du Colombier 	r->gintsts = ~0;
6345d9de2d3SDavid du Colombier 	r->gintmsk = Hcintr;
6355d9de2d3SDavid du Colombier 	r->gahbcfg |= Glblintrmsk;
6365d9de2d3SDavid du Colombier }
6375d9de2d3SDavid du Colombier 
6385d9de2d3SDavid du Colombier static void
dump(Hci *)6395d9de2d3SDavid du Colombier dump(Hci*)
6405d9de2d3SDavid du Colombier {
6415d9de2d3SDavid du Colombier }
6425d9de2d3SDavid du Colombier 
6435d9de2d3SDavid du Colombier static void
fiqintr(Ureg *,void * a)6445d9de2d3SDavid du Colombier fiqintr(Ureg*, void *a)
6455d9de2d3SDavid du Colombier {
6465d9de2d3SDavid du Colombier 	Hci *hp;
6475d9de2d3SDavid du Colombier 	Ctlr *ctlr;
6485d9de2d3SDavid du Colombier 	Dwcregs *r;
6495d9de2d3SDavid du Colombier 	uint intr, haint, wakechan;
6505d9de2d3SDavid du Colombier 	int i;
6515d9de2d3SDavid du Colombier 
6525d9de2d3SDavid du Colombier 	hp = a;
6535d9de2d3SDavid du Colombier 	ctlr = hp->aux;
6545d9de2d3SDavid du Colombier 	r = ctlr->regs;
6555d9de2d3SDavid du Colombier 	wakechan = 0;
6565d9de2d3SDavid du Colombier 	intr = r->gintsts;
6575d9de2d3SDavid du Colombier 	if(intr & Hcintr){
6585d9de2d3SDavid du Colombier 		haint = r->haint & r->haintmsk;
6595d9de2d3SDavid du Colombier 		for(i = 0; haint; i++){
660b4d1cf41SDavid du Colombier 			if(haint & 1){
661b4d1cf41SDavid du Colombier 				if(chanintr(ctlr, i) == 0){
6625d9de2d3SDavid du Colombier 					r->haintmsk &= ~(1<<i);
6635d9de2d3SDavid du Colombier 					wakechan |= 1<<i;
6645d9de2d3SDavid du Colombier 				}
665b4d1cf41SDavid du Colombier 			}
6665d9de2d3SDavid du Colombier 			haint >>= 1;
6675d9de2d3SDavid du Colombier 		}
6685d9de2d3SDavid du Colombier 	}
6695d9de2d3SDavid du Colombier 	if(intr & Sofintr){
6705d9de2d3SDavid du Colombier 		r->gintsts = Sofintr;
6715d9de2d3SDavid du Colombier 		if((r->hfnum&7) != 6){
6725d9de2d3SDavid du Colombier 			r->gintmsk &= ~Sofintr;
6735d9de2d3SDavid du Colombier 			wakechan |= ctlr->sofchan;
6745d9de2d3SDavid du Colombier 			ctlr->sofchan = 0;
6755d9de2d3SDavid du Colombier 		}
6765d9de2d3SDavid du Colombier 	}
6775d9de2d3SDavid du Colombier 	if(wakechan){
6785d9de2d3SDavid du Colombier 		ctlr->wakechan |= wakechan;
6795d9de2d3SDavid du Colombier 		armtimerset(1);
6805d9de2d3SDavid du Colombier 	}
6815d9de2d3SDavid du Colombier }
6825d9de2d3SDavid du Colombier 
6835d9de2d3SDavid du Colombier static void
irqintr(Ureg *,void * a)6845d9de2d3SDavid du Colombier irqintr(Ureg*, void *a)
6855d9de2d3SDavid du Colombier {
6865d9de2d3SDavid du Colombier 	Ctlr *ctlr;
6875d9de2d3SDavid du Colombier 	uint wakechan;
6885d9de2d3SDavid du Colombier 	int i, x;
6895d9de2d3SDavid du Colombier 
6905d9de2d3SDavid du Colombier 	ctlr = a;
6915d9de2d3SDavid du Colombier 	x = splfhi();
6925d9de2d3SDavid du Colombier 	armtimerset(0);
6935d9de2d3SDavid du Colombier 	wakechan = ctlr->wakechan;
6945d9de2d3SDavid du Colombier 	ctlr->wakechan = 0;
6955d9de2d3SDavid du Colombier 	splx(x);
6965d9de2d3SDavid du Colombier 	for(i = 0; wakechan; i++){
6975d9de2d3SDavid du Colombier 		if(wakechan & 1)
6985d9de2d3SDavid du Colombier 			wakeup(&ctlr->chanintr[i]);
6995d9de2d3SDavid du Colombier 		wakechan >>= 1;
7005d9de2d3SDavid du Colombier 	}
7015d9de2d3SDavid du Colombier }
7025d9de2d3SDavid du Colombier 
7035d9de2d3SDavid du Colombier static void
epopen(Ep * ep)7045d9de2d3SDavid du Colombier epopen(Ep *ep)
7055d9de2d3SDavid du Colombier {
7065d9de2d3SDavid du Colombier 	ddprint("usbotg: epopen ep%d.%d ttype %d\n",
7075d9de2d3SDavid du Colombier 		ep->dev->nb, ep->nb, ep->ttype);
7085d9de2d3SDavid du Colombier 	switch(ep->ttype){
7095d9de2d3SDavid du Colombier 	case Tnone:
710*a587111cSDavid du Colombier 		error(Enotconf);
7115d9de2d3SDavid du Colombier 	case Tintr:
7125d9de2d3SDavid du Colombier 		assert(ep->pollival > 0);
7135d9de2d3SDavid du Colombier 		/* fall through */
7145d9de2d3SDavid du Colombier 	case Tbulk:
7155d9de2d3SDavid du Colombier 		if(ep->toggle[Read] == 0)
7165d9de2d3SDavid du Colombier 			ep->toggle[Read] = DATA0;
7175d9de2d3SDavid du Colombier 		if(ep->toggle[Write] == 0)
7185d9de2d3SDavid du Colombier 			ep->toggle[Write] = DATA0;
7195d9de2d3SDavid du Colombier 		break;
7205d9de2d3SDavid du Colombier 	}
7215d9de2d3SDavid du Colombier 	ep->aux = malloc(sizeof(Epio));
7225d9de2d3SDavid du Colombier 	if(ep->aux == nil)
7235d9de2d3SDavid du Colombier 		error(Enomem);
7245d9de2d3SDavid du Colombier }
7255d9de2d3SDavid du Colombier 
7265d9de2d3SDavid du Colombier static void
epclose(Ep * ep)7275d9de2d3SDavid du Colombier epclose(Ep *ep)
7285d9de2d3SDavid du Colombier {
7295d9de2d3SDavid du Colombier 	ddprint("usbotg: epclose ep%d.%d ttype %d\n",
7305d9de2d3SDavid du Colombier 		ep->dev->nb, ep->nb, ep->ttype);
7315d9de2d3SDavid du Colombier 	switch(ep->ttype){
7325d9de2d3SDavid du Colombier 	case Tctl:
7335d9de2d3SDavid du Colombier 		freeb(((Epio*)ep->aux)->cb);
7345d9de2d3SDavid du Colombier 		/* fall through */
7355d9de2d3SDavid du Colombier 	default:
7365d9de2d3SDavid du Colombier 		free(ep->aux);
7375d9de2d3SDavid du Colombier 		break;
7385d9de2d3SDavid du Colombier 	}
7395d9de2d3SDavid du Colombier }
7405d9de2d3SDavid du Colombier 
7415d9de2d3SDavid du Colombier static long
epread(Ep * ep,void * a,long n)7425d9de2d3SDavid du Colombier epread(Ep *ep, void *a, long n)
7435d9de2d3SDavid du Colombier {
7445d9de2d3SDavid du Colombier 	Epio *epio;
7455d9de2d3SDavid du Colombier 	Block *b;
7465d9de2d3SDavid du Colombier 	uchar *p;
7475d9de2d3SDavid du Colombier 	ulong elapsed;
7485d9de2d3SDavid du Colombier 	long nr;
7495d9de2d3SDavid du Colombier 
7505d9de2d3SDavid du Colombier 	ddprint("epread ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
7515d9de2d3SDavid du Colombier 	epio = ep->aux;
7525d9de2d3SDavid du Colombier 	b = nil;
7535d9de2d3SDavid du Colombier 	qlock(epio);
7545d9de2d3SDavid du Colombier 	if(waserror()){
7555d9de2d3SDavid du Colombier 		qunlock(epio);
7565d9de2d3SDavid du Colombier 		if(b)
7575d9de2d3SDavid du Colombier 			freeb(b);
7585d9de2d3SDavid du Colombier 		nexterror();
7595d9de2d3SDavid du Colombier 	}
7605d9de2d3SDavid du Colombier 	switch(ep->ttype){
7615d9de2d3SDavid du Colombier 	default:
7625d9de2d3SDavid du Colombier 		error(Egreg);
7635d9de2d3SDavid du Colombier 	case Tctl:
7645d9de2d3SDavid du Colombier 		nr = ctldata(ep, a, n);
7655d9de2d3SDavid du Colombier 		qunlock(epio);
7665d9de2d3SDavid du Colombier 		poperror();
7675d9de2d3SDavid du Colombier 		return nr;
7685d9de2d3SDavid du Colombier 	case Tintr:
7695d9de2d3SDavid du Colombier 		elapsed = TK2MS(m->ticks) - epio->lastpoll;
7705d9de2d3SDavid du Colombier 		if(elapsed < ep->pollival)
7715d9de2d3SDavid du Colombier 			tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
7725d9de2d3SDavid du Colombier 		/* fall through */
7735d9de2d3SDavid du Colombier 	case Tbulk:
7745d9de2d3SDavid du Colombier 		/* XXX cache madness */
7755d9de2d3SDavid du Colombier 		b = allocb(ROUND(n, ep->maxpkt) + CACHELINESZ);
7765d9de2d3SDavid du Colombier 		p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
7775d9de2d3SDavid du Colombier 		cachedwbinvse(p, n);
7785d9de2d3SDavid du Colombier 		nr = eptrans(ep, Read, p, n);
7795d9de2d3SDavid du Colombier 		epio->lastpoll = TK2MS(m->ticks);
7805d9de2d3SDavid du Colombier 		memmove(a, p, nr);
7815d9de2d3SDavid du Colombier 		qunlock(epio);
7825d9de2d3SDavid du Colombier 		freeb(b);
7835d9de2d3SDavid du Colombier 		poperror();
7845d9de2d3SDavid du Colombier 		return nr;
7855d9de2d3SDavid du Colombier 	}
7865d9de2d3SDavid du Colombier }
7875d9de2d3SDavid du Colombier 
7885d9de2d3SDavid du Colombier static long
epwrite(Ep * ep,void * a,long n)7895d9de2d3SDavid du Colombier epwrite(Ep *ep, void *a, long n)
7905d9de2d3SDavid du Colombier {
7915d9de2d3SDavid du Colombier 	Epio *epio;
7925d9de2d3SDavid du Colombier 	Block *b;
7935d9de2d3SDavid du Colombier 	uchar *p;
7945d9de2d3SDavid du Colombier 	ulong elapsed;
7955d9de2d3SDavid du Colombier 
7965d9de2d3SDavid du Colombier 	ddprint("epwrite ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
7975d9de2d3SDavid du Colombier 	epio = ep->aux;
7985d9de2d3SDavid du Colombier 	b = nil;
7995d9de2d3SDavid du Colombier 	qlock(epio);
8005d9de2d3SDavid du Colombier 	if(waserror()){
8015d9de2d3SDavid du Colombier 		qunlock(epio);
8025d9de2d3SDavid du Colombier 		if(b)
8035d9de2d3SDavid du Colombier 			freeb(b);
8045d9de2d3SDavid du Colombier 		nexterror();
8055d9de2d3SDavid du Colombier 	}
8065d9de2d3SDavid du Colombier 	switch(ep->ttype){
8075d9de2d3SDavid du Colombier 	default:
8085d9de2d3SDavid du Colombier 		error(Egreg);
8095d9de2d3SDavid du Colombier 	case Tintr:
8105d9de2d3SDavid du Colombier 		elapsed = TK2MS(m->ticks) - epio->lastpoll;
8115d9de2d3SDavid du Colombier 		if(elapsed < ep->pollival)
8125d9de2d3SDavid du Colombier 			tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
8135d9de2d3SDavid du Colombier 		/* fall through */
8145d9de2d3SDavid du Colombier 	case Tctl:
8155d9de2d3SDavid du Colombier 	case Tbulk:
8165d9de2d3SDavid du Colombier 		/* XXX cache madness */
8175d9de2d3SDavid du Colombier 		b = allocb(n + CACHELINESZ);
8185d9de2d3SDavid du Colombier 		p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
8195d9de2d3SDavid du Colombier 		memmove(p, a, n);
8205d9de2d3SDavid du Colombier 		cachedwbse(p, n);
8215d9de2d3SDavid du Colombier 		if(ep->ttype == Tctl)
8225d9de2d3SDavid du Colombier 			n = ctltrans(ep, p, n);
8235d9de2d3SDavid du Colombier 		else{
8245d9de2d3SDavid du Colombier 			n = eptrans(ep, Write, p, n);
8255d9de2d3SDavid du Colombier 			epio->lastpoll = TK2MS(m->ticks);
8265d9de2d3SDavid du Colombier 		}
8275d9de2d3SDavid du Colombier 		qunlock(epio);
8285d9de2d3SDavid du Colombier 		freeb(b);
8295d9de2d3SDavid du Colombier 		poperror();
8305d9de2d3SDavid du Colombier 		return n;
8315d9de2d3SDavid du Colombier 	}
8325d9de2d3SDavid du Colombier }
8335d9de2d3SDavid du Colombier 
8345d9de2d3SDavid du Colombier static char*
seprintep(char * s,char *,Ep *)8355d9de2d3SDavid du Colombier seprintep(char *s, char*, Ep*)
8365d9de2d3SDavid du Colombier {
8375d9de2d3SDavid du Colombier 	return s;
8385d9de2d3SDavid du Colombier }
8395d9de2d3SDavid du Colombier 
8405d9de2d3SDavid du Colombier static int
portenable(Hci * hp,int port,int on)8415d9de2d3SDavid du Colombier portenable(Hci *hp, int port, int on)
8425d9de2d3SDavid du Colombier {
8435d9de2d3SDavid du Colombier 	Ctlr *ctlr;
8445d9de2d3SDavid du Colombier 	Dwcregs *r;
8455d9de2d3SDavid du Colombier 
8465d9de2d3SDavid du Colombier 	assert(port == 1);
8475d9de2d3SDavid du Colombier 	ctlr = hp->aux;
8485d9de2d3SDavid du Colombier 	r = ctlr->regs;
8495d9de2d3SDavid du Colombier 	dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
8505d9de2d3SDavid du Colombier 	if(!on)
8515d9de2d3SDavid du Colombier 		r->hport0 = Prtpwr | Prtena;
8525d9de2d3SDavid du Colombier 	tsleep(&up->sleep, return0, 0, Enabledelay);
8535d9de2d3SDavid du Colombier 	dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
8545d9de2d3SDavid du Colombier 	return 0;
8555d9de2d3SDavid du Colombier }
8565d9de2d3SDavid du Colombier 
8575d9de2d3SDavid du Colombier static int
portreset(Hci * hp,int port,int on)8585d9de2d3SDavid du Colombier portreset(Hci *hp, int port, int on)
8595d9de2d3SDavid du Colombier {
8605d9de2d3SDavid du Colombier 	Ctlr *ctlr;
8615d9de2d3SDavid du Colombier 	Dwcregs *r;
8625d9de2d3SDavid du Colombier 	int b, s;
8635d9de2d3SDavid du Colombier 
8645d9de2d3SDavid du Colombier 	assert(port == 1);
8655d9de2d3SDavid du Colombier 	ctlr = hp->aux;
8665d9de2d3SDavid du Colombier 	r = ctlr->regs;
8675d9de2d3SDavid du Colombier 	dprint("usbotg reset=%d; sts %#x\n", on, r->hport0);
8685d9de2d3SDavid du Colombier 	if(!on)
8695d9de2d3SDavid du Colombier 		return 0;
8705d9de2d3SDavid du Colombier 	r->hport0 = Prtpwr | Prtrst;
8715d9de2d3SDavid du Colombier 	tsleep(&up->sleep, return0, 0, ResetdelayHS);
8725d9de2d3SDavid du Colombier 	r->hport0 = Prtpwr;
8735d9de2d3SDavid du Colombier 	tsleep(&up->sleep, return0, 0, Enabledelay);
8745d9de2d3SDavid du Colombier 	s = r->hport0;
8755d9de2d3SDavid du Colombier 	b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
8765d9de2d3SDavid du Colombier 	if(b != 0)
8775d9de2d3SDavid du Colombier 		r->hport0 = Prtpwr | b;
8785d9de2d3SDavid du Colombier 	dprint("usbotg reset=%d; sts %#x\n", on, s);
8795d9de2d3SDavid du Colombier 	if((s & Prtena) == 0)
8805d9de2d3SDavid du Colombier 		print("usbotg: host port not enabled after reset");
8815d9de2d3SDavid du Colombier 	return 0;
8825d9de2d3SDavid du Colombier }
8835d9de2d3SDavid du Colombier 
8845d9de2d3SDavid du Colombier static int
portstatus(Hci * hp,int port)8855d9de2d3SDavid du Colombier portstatus(Hci *hp, int port)
8865d9de2d3SDavid du Colombier {
8875d9de2d3SDavid du Colombier 	Ctlr *ctlr;
8885d9de2d3SDavid du Colombier 	Dwcregs *r;
8895d9de2d3SDavid du Colombier 	int b, s;
8905d9de2d3SDavid du Colombier 
8915d9de2d3SDavid du Colombier 	assert(port == 1);
8925d9de2d3SDavid du Colombier 	ctlr = hp->aux;
8935d9de2d3SDavid du Colombier 	r = ctlr->regs;
8945d9de2d3SDavid du Colombier 	s = r->hport0;
8955d9de2d3SDavid du Colombier 	b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
8965d9de2d3SDavid du Colombier 	if(b != 0)
8975d9de2d3SDavid du Colombier 		r->hport0 = Prtpwr | b;
8985d9de2d3SDavid du Colombier 	b = 0;
8995d9de2d3SDavid du Colombier 	if(s & Prtconnsts)
9005d9de2d3SDavid du Colombier 		b |= HPpresent;
9015d9de2d3SDavid du Colombier 	if(s & Prtconndet)
9025d9de2d3SDavid du Colombier 		b |= HPstatuschg;
9035d9de2d3SDavid du Colombier 	if(s & Prtena)
9045d9de2d3SDavid du Colombier 		b |= HPenable;
9055d9de2d3SDavid du Colombier 	if(s & Prtenchng)
9065d9de2d3SDavid du Colombier 		b |= HPchange;
9075d9de2d3SDavid du Colombier 	if(s & Prtovrcurract)
9085d9de2d3SDavid du Colombier 		 b |= HPovercurrent;
9095d9de2d3SDavid du Colombier 	if(s & Prtsusp)
9105d9de2d3SDavid du Colombier 		b |= HPsuspend;
9115d9de2d3SDavid du Colombier 	if(s & Prtrst)
9125d9de2d3SDavid du Colombier 		b |= HPreset;
9135d9de2d3SDavid du Colombier 	if(s & Prtpwr)
9145d9de2d3SDavid du Colombier 		b |= HPpower;
9155d9de2d3SDavid du Colombier 	switch(s & Prtspd){
9165d9de2d3SDavid du Colombier 	case HIGHSPEED:
9175d9de2d3SDavid du Colombier 		b |= HPhigh;
9185d9de2d3SDavid du Colombier 		break;
9195d9de2d3SDavid du Colombier 	case LOWSPEED:
9205d9de2d3SDavid du Colombier 		b |= HPslow;
9215d9de2d3SDavid du Colombier 		break;
9225d9de2d3SDavid du Colombier 	}
9235d9de2d3SDavid du Colombier 	return b;
9245d9de2d3SDavid du Colombier }
9255d9de2d3SDavid du Colombier 
9265d9de2d3SDavid du Colombier static void
shutdown(Hci *)9275d9de2d3SDavid du Colombier shutdown(Hci*)
9285d9de2d3SDavid du Colombier {
9295d9de2d3SDavid du Colombier }
9305d9de2d3SDavid du Colombier 
9315d9de2d3SDavid du Colombier static void
setdebug(Hci *,int d)9325d9de2d3SDavid du Colombier setdebug(Hci*, int d)
9335d9de2d3SDavid du Colombier {
9345d9de2d3SDavid du Colombier 	debug = d;
9355d9de2d3SDavid du Colombier }
9365d9de2d3SDavid du Colombier 
9375d9de2d3SDavid du Colombier static int
reset(Hci * hp)9385d9de2d3SDavid du Colombier reset(Hci *hp)
9395d9de2d3SDavid du Colombier {
9405d9de2d3SDavid du Colombier 	Ctlr *ctlr;
9415d9de2d3SDavid du Colombier 	uint id;
9425d9de2d3SDavid du Colombier 
9435d9de2d3SDavid du Colombier 	ctlr = &dwc;
9445d9de2d3SDavid du Colombier 	if(ctlr->regs != nil)
9455d9de2d3SDavid du Colombier 		return -1;
9465d9de2d3SDavid du Colombier 	ctlr->regs = (Dwcregs*)USBREGS;
9475d9de2d3SDavid du Colombier 	id = ctlr->regs->gsnpsid;
9485d9de2d3SDavid du Colombier 	if((id>>16) != ('O'<<8 | 'T'))
9495d9de2d3SDavid du Colombier 		return -1;
9505d9de2d3SDavid du Colombier 	dprint("usbotg: rev %d.%3.3x\n", (id>>12)&0xF, id&0xFFF);
9515d9de2d3SDavid du Colombier 
9525d9de2d3SDavid du Colombier 	intrenable(IRQtimerArm, irqintr, ctlr, 0, "dwc");
9535d9de2d3SDavid du Colombier 
9545d9de2d3SDavid du Colombier 	hp->aux = ctlr;
9555d9de2d3SDavid du Colombier 	hp->port = 0;
9565d9de2d3SDavid du Colombier 	hp->irq = IRQusb;
9575d9de2d3SDavid du Colombier 	hp->tbdf = 0;
9585d9de2d3SDavid du Colombier 	hp->nports = 1;
9595d9de2d3SDavid du Colombier 	hp->highspeed = 1;
9605d9de2d3SDavid du Colombier 
9615d9de2d3SDavid du Colombier 	hp->init = init;
9625d9de2d3SDavid du Colombier 	hp->dump = dump;
9635d9de2d3SDavid du Colombier 	hp->interrupt = fiqintr;
9645d9de2d3SDavid du Colombier 	hp->epopen = epopen;
9655d9de2d3SDavid du Colombier 	hp->epclose = epclose;
9665d9de2d3SDavid du Colombier 	hp->epread = epread;
9675d9de2d3SDavid du Colombier 	hp->epwrite = epwrite;
9685d9de2d3SDavid du Colombier 	hp->seprintep = seprintep;
9695d9de2d3SDavid du Colombier 	hp->portenable = portenable;
9705d9de2d3SDavid du Colombier 	hp->portreset = portreset;
9715d9de2d3SDavid du Colombier 	hp->portstatus = portstatus;
9725d9de2d3SDavid du Colombier 	hp->shutdown = shutdown;
9735d9de2d3SDavid du Colombier 	hp->debug = setdebug;
9745d9de2d3SDavid du Colombier 	hp->type = "dwcotg";
9755d9de2d3SDavid du Colombier 	return 0;
9765d9de2d3SDavid du Colombier }
9775d9de2d3SDavid du Colombier 
9785d9de2d3SDavid du Colombier void
usbdwclink(void)9795d9de2d3SDavid du Colombier usbdwclink(void)
9805d9de2d3SDavid du Colombier {
9815d9de2d3SDavid du Colombier 	addhcitype("dwcotg", reset);
9825d9de2d3SDavid du Colombier }
983