xref: /plan9-contrib/sys/src/9/bcm/usbxhci.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
1*5c47fe09SDavid du Colombier /*
2*5c47fe09SDavid du Colombier  * from 9front ../port/usbxhci.c
3*5c47fe09SDavid du Colombier  */
4*5c47fe09SDavid du Colombier 
5*5c47fe09SDavid du Colombier #include	"u.h"
6*5c47fe09SDavid du Colombier #include	"../port/lib.h"
7*5c47fe09SDavid du Colombier #include	"mem.h"
8*5c47fe09SDavid du Colombier #include	"dat.h"
9*5c47fe09SDavid du Colombier #include	"fns.h"
10*5c47fe09SDavid du Colombier #include	"io.h"
11*5c47fe09SDavid du Colombier #include	"pci.h"
12*5c47fe09SDavid du Colombier #include	"../port/error.h"
13*5c47fe09SDavid du Colombier #include	"usb.h"
14*5c47fe09SDavid du Colombier 
15*5c47fe09SDavid du Colombier extern void dmaflush(int, void*, ulong);
16*5c47fe09SDavid du Colombier 
17*5c47fe09SDavid du Colombier enum {
18*5c47fe09SDavid du Colombier 	/* Capability Registers */
19*5c47fe09SDavid du Colombier 	CAPLENGTH	= 0x00/4,	// 1
20*5c47fe09SDavid du Colombier 	HCIVERSION	= 0x02/4,	// 2
21*5c47fe09SDavid du Colombier 	HCSPARAMS1	= 0x04/4,
22*5c47fe09SDavid du Colombier 	HCSPARAMS2	= 0x08/4,
23*5c47fe09SDavid du Colombier 	HCSPARAMS3	= 0x0C/4,
24*5c47fe09SDavid du Colombier 
25*5c47fe09SDavid du Colombier 	HCCPARAMS	= 0x10/4,
26*5c47fe09SDavid du Colombier 		AC64	= 1<<0,
27*5c47fe09SDavid du Colombier 		BNC	= 1<<1,
28*5c47fe09SDavid du Colombier 		CSZ	= 1<<2,
29*5c47fe09SDavid du Colombier 		PPC	= 1<<3,
30*5c47fe09SDavid du Colombier 		PIND	= 1<<4,
31*5c47fe09SDavid du Colombier 		LHRC	= 1<<5,
32*5c47fe09SDavid du Colombier 		LTC	= 1<<6,
33*5c47fe09SDavid du Colombier 		NSS	= 1<<7,
34*5c47fe09SDavid du Colombier 
35*5c47fe09SDavid du Colombier 	DBOFF		= 0x14/4,
36*5c47fe09SDavid du Colombier 	RTSOFF		= 0x18/4,
37*5c47fe09SDavid du Colombier 
38*5c47fe09SDavid du Colombier 	HCCPARAMS2	= 0x1C/4,
39*5c47fe09SDavid du Colombier 
40*5c47fe09SDavid du Colombier 	/* Operational Registers */
41*5c47fe09SDavid du Colombier 	USBCMD		= 0x00/4,	/* USB Command Register */
42*5c47fe09SDavid du Colombier 		RUNSTOP	= 1<<0,		/* Run/Stop - RW */
43*5c47fe09SDavid du Colombier 		HCRST	= 1<<1,		/* Host Controller Reset - RW */
44*5c47fe09SDavid du Colombier 		INTE	= 1<<2,		/* Interrupter Enable - RW */
45*5c47fe09SDavid du Colombier 		HSEE	= 1<<3,		/* Host System Error Enable - RW */
46*5c47fe09SDavid du Colombier 		LHCRST	= 1<<7,		/* Light Host Controller Reset - RO/RW */
47*5c47fe09SDavid du Colombier 		CSS	= 1<<8,		/* Controller Save State - RW */
48*5c47fe09SDavid du Colombier 		CRS	= 1<<9,		/* Controller Restore State - RW */
49*5c47fe09SDavid du Colombier 		EWE	= 1<<10,	/* Enable Wrap Event - RW */
50*5c47fe09SDavid du Colombier 		EU3S	= 1<<11,	/* Enable U3 MFINDEX Stop - RW */
51*5c47fe09SDavid du Colombier 
52*5c47fe09SDavid du Colombier 	USBSTS		= 0x04/4,	/* USB Status Register */
53*5c47fe09SDavid du Colombier 		HCH	= 1<<0,		/* HCHalted - RO */
54*5c47fe09SDavid du Colombier 		HSE	= 1<<2,		/* Host System Error - RW1C */
55*5c47fe09SDavid du Colombier 		EINT	= 1<<3,		/* Event Interrupt - RW1C */
56*5c47fe09SDavid du Colombier 		PCD	= 1<<4,		/* Port Change Detect - RW1C */
57*5c47fe09SDavid du Colombier 		SSS	= 1<<8,		/* Save State Status - RO */
58*5c47fe09SDavid du Colombier 		RSS	= 1<<9,		/* Restore State Status - RO */
59*5c47fe09SDavid du Colombier 		SRE	= 1<<10,	/* Save/Restore Error - RW1C */
60*5c47fe09SDavid du Colombier 		CNR	= 1<<11,	/* Controller Not Ready - RO */
61*5c47fe09SDavid du Colombier 		HCE	= 1<<12,	/* Host Controller Error - RO */
62*5c47fe09SDavid du Colombier 
63*5c47fe09SDavid du Colombier 	PAGESIZE	= 0x08/4,	/* Page Size - RO */
64*5c47fe09SDavid du Colombier 
65*5c47fe09SDavid du Colombier 	DNCTRL		= 0x14/4,	/* Device Notification Control Register - RW */
66*5c47fe09SDavid du Colombier 
67*5c47fe09SDavid du Colombier 	CRCR		= 0x18/4,	/* Command Ring Control Register - RW */
68*5c47fe09SDavid du Colombier 		RCS	= 1<<0,		/* Ring Cycle State - RW */
69*5c47fe09SDavid du Colombier 		CS	= 1<<1,		/* Command Stop - RW1S */
70*5c47fe09SDavid du Colombier 		CA	= 1<<2,		/* Command Abort - RW1S */
71*5c47fe09SDavid du Colombier 		CRR	= 1<<3,		/* Command Ring Running - RO */
72*5c47fe09SDavid du Colombier 
73*5c47fe09SDavid du Colombier 	DCBAAP		= 0x30/4,	// 8
74*5c47fe09SDavid du Colombier 
75*5c47fe09SDavid du Colombier 	CONFIG		= 0x38/4,	/* Configure Register (MaxSlotEn[7:0]) */
76*5c47fe09SDavid du Colombier 
77*5c47fe09SDavid du Colombier 	/* Port Register Set */
78*5c47fe09SDavid du Colombier 	PORTSC		= 0x00/4,	/* Port status and Control Register */
79*5c47fe09SDavid du Colombier 		CCS	= 1<<0,		/* Current Connect Status - ROS */
80*5c47fe09SDavid du Colombier 		PED	= 1<<1,		/* Port Enable/Disabled - RW1CS */
81*5c47fe09SDavid du Colombier 		OCA	= 1<<3,		/* Over-current Active - RO */
82*5c47fe09SDavid du Colombier 		PR	= 1<<4,		/* Port Reset - RW1S */
83*5c47fe09SDavid du Colombier 		PLS	= 15<<5,	/* Port Link State - RWS */
84*5c47fe09SDavid du Colombier 		PP	= 1<<9,		/* Port Power - RWS */
85*5c47fe09SDavid du Colombier 		PS	= 15<<10,	/* Port Speed - ROS */
86*5c47fe09SDavid du Colombier 		PIC	= 3<<14,	/* Port Indicator Control - RWS */
87*5c47fe09SDavid du Colombier 		LWS	= 1<<16,	/* Port Link Write Strobe - RW */
88*5c47fe09SDavid du Colombier 		CSC	= 1<<17,	/* Connect Status Change - RW1CS */
89*5c47fe09SDavid du Colombier 		PEC	= 1<<18,	/* Port Enabled/Disabled Change - RW1CS */
90*5c47fe09SDavid du Colombier 		WRC	= 1<<19,	/* Warm Port Reset Change - RW1CS */
91*5c47fe09SDavid du Colombier 		OCC	= 1<<20,	/* Over-current Change - RW1CS */
92*5c47fe09SDavid du Colombier 		PRC	= 1<<21,	/* Port Reset Change - RW1CS */
93*5c47fe09SDavid du Colombier 		PLC	= 1<<22,	/* Port Link State Change - RW1CS */
94*5c47fe09SDavid du Colombier 		CEC	= 1<<23,	/* Port Config Error Change - RW1CS */
95*5c47fe09SDavid du Colombier 		CAS	= 1<<24,	/* Cold Attach Status - RO */
96*5c47fe09SDavid du Colombier 		WCE	= 1<<25,	/* Wake on Connect Enable - RWS */
97*5c47fe09SDavid du Colombier 		WDE	= 1<<26,	/* Wake on Disconnect Enable - RWS */
98*5c47fe09SDavid du Colombier 		WOE	= 1<<27,	/* Wake on Over-current Enable - RWS */
99*5c47fe09SDavid du Colombier 		DR	= 1<<30,	/* Device Removable - RO */
100*5c47fe09SDavid du Colombier 		WPR	= 1<<31,	/* Warm Port Reset - RW1S */
101*5c47fe09SDavid du Colombier 
102*5c47fe09SDavid du Colombier 	PORTPMSC	= 0x04/4,
103*5c47fe09SDavid du Colombier 	PORTLI		= 0x08/4,
104*5c47fe09SDavid du Colombier 
105*5c47fe09SDavid du Colombier 	/* Host Controller Runtime Register */
106*5c47fe09SDavid du Colombier 	MFINDEX		= 0x0000/4,	/* Microframe Index */
107*5c47fe09SDavid du Colombier 	IR0		= 0x0020/4,	/* Interrupt Register Set 0 */
108*5c47fe09SDavid du Colombier 
109*5c47fe09SDavid du Colombier 	/* Interrupter Registers */
110*5c47fe09SDavid du Colombier 	IMAN		= 0x00/4,	/* Interrupter Management */
111*5c47fe09SDavid du Colombier 	IMOD		= 0x04/4,	/* Interrupter Moderation */
112*5c47fe09SDavid du Colombier 	ERSTSZ		= 0x08/4,	/* Event Ring Segment Table Size */
113*5c47fe09SDavid du Colombier 	ERSTBA		= 0x10/4,	/* Event Ring Segment Table Base Address */
114*5c47fe09SDavid du Colombier 	ERDP		= 0x18/4,	/* Event Ring Dequeue Pointer */
115*5c47fe09SDavid du Colombier 
116*5c47fe09SDavid du Colombier 	/* TRB flags */
117*5c47fe09SDavid du Colombier 	TR_ENT		= 1<<1,
118*5c47fe09SDavid du Colombier 	TR_ISP		= 1<<2,
119*5c47fe09SDavid du Colombier 	TR_NS		= 1<<3,
120*5c47fe09SDavid du Colombier 	TR_CH		= 1<<4,
121*5c47fe09SDavid du Colombier 	TR_IOC		= 1<<5,
122*5c47fe09SDavid du Colombier 	TR_IDT		= 1<<6,
123*5c47fe09SDavid du Colombier 	TR_BEI		= 1<<9,
124*5c47fe09SDavid du Colombier 
125*5c47fe09SDavid du Colombier 	/* TRB types */
126*5c47fe09SDavid du Colombier 	TR_RESERVED	= 0<<10,
127*5c47fe09SDavid du Colombier 	TR_NORMAL	= 1<<10,
128*5c47fe09SDavid du Colombier 	TR_SETUPSTAGE	= 2<<10,
129*5c47fe09SDavid du Colombier 	TR_DATASTAGE	= 3<<10,
130*5c47fe09SDavid du Colombier 	TR_STATUSSTAGE	= 4<<10,
131*5c47fe09SDavid du Colombier 	TR_ISOCH	= 5<<10,
132*5c47fe09SDavid du Colombier 	TR_LINK		= 6<<10,
133*5c47fe09SDavid du Colombier 	TR_EVENTDATA	= 7<<10,
134*5c47fe09SDavid du Colombier 	TR_NOOP		= 8<<10,
135*5c47fe09SDavid du Colombier 
136*5c47fe09SDavid du Colombier 	CR_ENABLESLOT	= 9<<10,
137*5c47fe09SDavid du Colombier 	CR_DISABLESLOT	= 10<<10,
138*5c47fe09SDavid du Colombier 	CR_ADDRESSDEV	= 11<<10,
139*5c47fe09SDavid du Colombier 	CR_CONFIGEP	= 12<<10,
140*5c47fe09SDavid du Colombier 	CR_EVALCTX	= 13<<10,
141*5c47fe09SDavid du Colombier 	CR_RESETEP	= 14<<10,
142*5c47fe09SDavid du Colombier 	CR_STOPEP	= 15<<10,
143*5c47fe09SDavid du Colombier 	CR_SETTRDQP	= 16<<10,
144*5c47fe09SDavid du Colombier 	CR_RESETDEV	= 17<<10,
145*5c47fe09SDavid du Colombier 	CR_FORCECMD	= 18<<10,
146*5c47fe09SDavid du Colombier 	CR_NEGBW	= 19<<10,
147*5c47fe09SDavid du Colombier 	CR_SETLAT	= 20<<10,
148*5c47fe09SDavid du Colombier 	CR_GETPORTBW	= 21<<10,
149*5c47fe09SDavid du Colombier 	CR_FORCEHDR	= 22<<10,
150*5c47fe09SDavid du Colombier 	CR_NOOP		= 23<<10,
151*5c47fe09SDavid du Colombier 
152*5c47fe09SDavid du Colombier 	ER_TRANSFER	= 32<<10,
153*5c47fe09SDavid du Colombier 	ER_CMDCOMPL	= 33<<10,
154*5c47fe09SDavid du Colombier 	ER_PORTSC	= 34<<10,
155*5c47fe09SDavid du Colombier 	ER_BWREQ	= 35<<10,
156*5c47fe09SDavid du Colombier 	ER_DOORBELL	= 36<<10,
157*5c47fe09SDavid du Colombier 	ER_HCE		= 37<<10,
158*5c47fe09SDavid du Colombier 	ER_DEVNOTE	= 38<<10,
159*5c47fe09SDavid du Colombier 	ER_MFINDEXWRAP	= 39<<10,
160*5c47fe09SDavid du Colombier };
161*5c47fe09SDavid du Colombier 
162*5c47fe09SDavid du Colombier typedef struct Ctlr Ctlr;
163*5c47fe09SDavid du Colombier typedef struct Wait Wait;
164*5c47fe09SDavid du Colombier typedef struct Ring Ring;
165*5c47fe09SDavid du Colombier typedef struct Slot Slot;
166*5c47fe09SDavid du Colombier typedef struct Epio Epio;
167*5c47fe09SDavid du Colombier typedef struct Port Port;
168*5c47fe09SDavid du Colombier 
169*5c47fe09SDavid du Colombier struct Wait
170*5c47fe09SDavid du Colombier {
171*5c47fe09SDavid du Colombier 	Wait	*next;
172*5c47fe09SDavid du Colombier 	Ring	*ring;
173*5c47fe09SDavid du Colombier 	u32int	*td;
174*5c47fe09SDavid du Colombier 	u32int	er[4];
175*5c47fe09SDavid du Colombier 	Rendez	*z;
176*5c47fe09SDavid du Colombier };
177*5c47fe09SDavid du Colombier 
178*5c47fe09SDavid du Colombier struct Ring
179*5c47fe09SDavid du Colombier {
180*5c47fe09SDavid du Colombier 	int	id;
181*5c47fe09SDavid du Colombier 
182*5c47fe09SDavid du Colombier 	Slot	*slot;
183*5c47fe09SDavid du Colombier 
184*5c47fe09SDavid du Colombier 	u32int	*base;
185*5c47fe09SDavid du Colombier 
186*5c47fe09SDavid du Colombier 	u32int	mask;
187*5c47fe09SDavid du Colombier 	u32int	shift;
188*5c47fe09SDavid du Colombier 
189*5c47fe09SDavid du Colombier 	u32int	rp;
190*5c47fe09SDavid du Colombier 	u32int	wp;
191*5c47fe09SDavid du Colombier 
192*5c47fe09SDavid du Colombier 	u32int	*ctx;
193*5c47fe09SDavid du Colombier 	u32int	*doorbell;
194*5c47fe09SDavid du Colombier 
195*5c47fe09SDavid du Colombier 	int	stopped;
196*5c47fe09SDavid du Colombier 
197*5c47fe09SDavid du Colombier 	Wait	*pending;
198*5c47fe09SDavid du Colombier 	Lock;
199*5c47fe09SDavid du Colombier };
200*5c47fe09SDavid du Colombier 
201*5c47fe09SDavid du Colombier struct Slot
202*5c47fe09SDavid du Colombier {
203*5c47fe09SDavid du Colombier 	int	id;
204*5c47fe09SDavid du Colombier 
205*5c47fe09SDavid du Colombier 	int	confval; // bConfigurationValue of SET_CONFIGURATION
206*5c47fe09SDavid du Colombier 	int	iface;	// bInterfaceNumber of SET_INTERFACE
207*5c47fe09SDavid du Colombier 	int	altc;	// bAlternateSetting of SET_INTERFACE
208*5c47fe09SDavid du Colombier 
209*5c47fe09SDavid du Colombier 	Ctlr	*ctlr;
210*5c47fe09SDavid du Colombier 	Udev	*dev;
211*5c47fe09SDavid du Colombier 
212*5c47fe09SDavid du Colombier 	u32int	*ibase;
213*5c47fe09SDavid du Colombier 	u32int	*obase;
214*5c47fe09SDavid du Colombier 
215*5c47fe09SDavid du Colombier 	/* endpoint rings */
216*5c47fe09SDavid du Colombier 	int	nep;
217*5c47fe09SDavid du Colombier 	Ring	epr[32];
218*5c47fe09SDavid du Colombier };
219*5c47fe09SDavid du Colombier 
220*5c47fe09SDavid du Colombier struct Port
221*5c47fe09SDavid du Colombier {
222*5c47fe09SDavid du Colombier 	char	spec[4];
223*5c47fe09SDavid du Colombier 	int	proto;
224*5c47fe09SDavid du Colombier 
225*5c47fe09SDavid du Colombier 	u32int	*reg;
226*5c47fe09SDavid du Colombier };
227*5c47fe09SDavid du Colombier 
228*5c47fe09SDavid du Colombier struct Ctlr
229*5c47fe09SDavid du Colombier {
230*5c47fe09SDavid du Colombier 	Pcidev	*pcidev;
231*5c47fe09SDavid du Colombier 
232*5c47fe09SDavid du Colombier 	u32int	*mmio;
233*5c47fe09SDavid du Colombier 
234*5c47fe09SDavid du Colombier 	u32int	*opr;	/* operational registers */
235*5c47fe09SDavid du Colombier 	u32int	*rts;	/* runtime registers */
236*5c47fe09SDavid du Colombier 	u32int	*dba;	/* doorbell array */
237*5c47fe09SDavid du Colombier 
238*5c47fe09SDavid du Colombier 	u64int	*dcba;	/* device context base array */
239*5c47fe09SDavid du Colombier 
240*5c47fe09SDavid du Colombier 	u64int	*sba;	/* scratchpad buffer array */
241*5c47fe09SDavid du Colombier 	void	*sbp;	/* scratchpad buffer pages */
242*5c47fe09SDavid du Colombier 
243*5c47fe09SDavid du Colombier 	u32int	*erst[1];	/* event ring segment table */
244*5c47fe09SDavid du Colombier 	Ring	er[1];		/* event ring segment */
245*5c47fe09SDavid du Colombier 	Ring	cr[1];		/* command ring segment */
246*5c47fe09SDavid du Colombier 	QLock	cmdlock;
247*5c47fe09SDavid du Colombier 
248*5c47fe09SDavid du Colombier 	u32int	µframe;
249*5c47fe09SDavid du Colombier 
250*5c47fe09SDavid du Colombier 	QLock	slotlock;
251*5c47fe09SDavid du Colombier 	Slot	**slot;		/* slots by slot id */
252*5c47fe09SDavid du Colombier 	Port	*port;
253*5c47fe09SDavid du Colombier 
254*5c47fe09SDavid du Colombier 	u32int	hccparams;
255*5c47fe09SDavid du Colombier 
256*5c47fe09SDavid du Colombier 	int	csz;
257*5c47fe09SDavid du Colombier 	int	pagesize;
258*5c47fe09SDavid du Colombier 	int	nscratch;
259*5c47fe09SDavid du Colombier 	int	nintrs;
260*5c47fe09SDavid du Colombier 	int	nslots;
261*5c47fe09SDavid du Colombier 
262*5c47fe09SDavid du Colombier 	Rendez	recover;
263*5c47fe09SDavid du Colombier 	void	*active;
264*5c47fe09SDavid du Colombier 	uintptr	base;
265*5c47fe09SDavid du Colombier };
266*5c47fe09SDavid du Colombier 
267*5c47fe09SDavid du Colombier struct Epio
268*5c47fe09SDavid du Colombier {
269*5c47fe09SDavid du Colombier 	QLock;
270*5c47fe09SDavid du Colombier 
271*5c47fe09SDavid du Colombier 	Ring	*ring;
272*5c47fe09SDavid du Colombier 	Block	*b;
273*5c47fe09SDavid du Colombier 
274*5c47fe09SDavid du Colombier 	/* iso */
275*5c47fe09SDavid du Colombier 	u32int	frame;
276*5c47fe09SDavid du Colombier 	u32int	period;
277*5c47fe09SDavid du Colombier 	u32int	incr;
278*5c47fe09SDavid du Colombier 	u32int	tdsz;
279*5c47fe09SDavid du Colombier 
280*5c47fe09SDavid du Colombier 	int	nleft;
281*5c47fe09SDavid du Colombier };
282*5c47fe09SDavid du Colombier 
283*5c47fe09SDavid du Colombier static char Ebadlen[] = "bad usb request length";
284*5c47fe09SDavid du Colombier static char Enotconfig[] = "usb endpoint not configured";
285*5c47fe09SDavid du Colombier static char Erecover[] = "xhci controller needs reset";
286*5c47fe09SDavid du Colombier 
287*5c47fe09SDavid du Colombier static char*
288*5c47fe09SDavid du Colombier ctlrcmd(Ctlr *ctlr, u32int c, u32int s, u64int p, u32int *er);
289*5c47fe09SDavid du Colombier 
290*5c47fe09SDavid du Colombier static void
setrptr(u32int * reg,u64int pa)291*5c47fe09SDavid du Colombier setrptr(u32int *reg, u64int pa)
292*5c47fe09SDavid du Colombier {
293*5c47fe09SDavid du Colombier 	coherence();
294*5c47fe09SDavid du Colombier 	reg[0] = pa;
295*5c47fe09SDavid du Colombier 	reg[1] = pa>>32;
296*5c47fe09SDavid du Colombier }
297*5c47fe09SDavid du Colombier 
298*5c47fe09SDavid du Colombier static u32int
frame(Ctlr * ctlr)299*5c47fe09SDavid du Colombier µframe(Ctlr *ctlr)
300*5c47fe09SDavid du Colombier {
301*5c47fe09SDavid du Colombier 	u32int µ;
302*5c47fe09SDavid du Colombier 	do {
303*5c47fe09SDavid du Colombier 		µ = (ctlr->rts[MFINDEX] & (1<<14)-1) |
304*5c47fe09SDavid du Colombier 			(ctlr->µframe & ~((1<<14)-1));
305*5c47fe09SDavid du Colombier 	} while((int)(µ - ctlr->µframe) < 0);
306*5c47fe09SDavid du Colombier 	return µ;
307*5c47fe09SDavid du Colombier }
308*5c47fe09SDavid du Colombier 
309*5c47fe09SDavid du Colombier static void
freering(Ring * r)310*5c47fe09SDavid du Colombier freering(Ring *r)
311*5c47fe09SDavid du Colombier {
312*5c47fe09SDavid du Colombier 	if(r == nil)
313*5c47fe09SDavid du Colombier 		return;
314*5c47fe09SDavid du Colombier 	if(r->base != nil){
315*5c47fe09SDavid du Colombier 		dmaflush(0, r->base, 4*4<<r->shift);
316*5c47fe09SDavid du Colombier 		free(r->base);
317*5c47fe09SDavid du Colombier 	}
318*5c47fe09SDavid du Colombier 	memset(r, 0, sizeof(*r));
319*5c47fe09SDavid du Colombier }
320*5c47fe09SDavid du Colombier 
321*5c47fe09SDavid du Colombier static Ring*
initring(Ring * r,int shift)322*5c47fe09SDavid du Colombier initring(Ring *r, int shift)
323*5c47fe09SDavid du Colombier {
324*5c47fe09SDavid du Colombier 	r->id = 0;
325*5c47fe09SDavid du Colombier 	r->ctx = nil;
326*5c47fe09SDavid du Colombier 	r->slot = nil;
327*5c47fe09SDavid du Colombier 	r->doorbell = nil;
328*5c47fe09SDavid du Colombier 	r->pending = nil;
329*5c47fe09SDavid du Colombier 	r->stopped = 0;
330*5c47fe09SDavid du Colombier 	r->shift = shift;
331*5c47fe09SDavid du Colombier 	r->mask = (1<<shift)-1;
332*5c47fe09SDavid du Colombier 	r->rp = r->wp = 0;
333*5c47fe09SDavid du Colombier 	r->base = mallocalign(4*4<<shift, 64, 0, 64*1024);
334*5c47fe09SDavid du Colombier 	if(r->base == nil){
335*5c47fe09SDavid du Colombier 		freering(r);
336*5c47fe09SDavid du Colombier 		error(Enomem);
337*5c47fe09SDavid du Colombier 	}
338*5c47fe09SDavid du Colombier 	dmaflush(1, r->base, 4*4<<shift);
339*5c47fe09SDavid du Colombier 	return r;
340*5c47fe09SDavid du Colombier }
341*5c47fe09SDavid du Colombier 
342*5c47fe09SDavid du Colombier static void
flushring(Ring * r)343*5c47fe09SDavid du Colombier flushring(Ring *r)
344*5c47fe09SDavid du Colombier {
345*5c47fe09SDavid du Colombier 	Rendez *z;
346*5c47fe09SDavid du Colombier 	Wait *w;
347*5c47fe09SDavid du Colombier 
348*5c47fe09SDavid du Colombier 	while((w = r->pending) != nil){
349*5c47fe09SDavid du Colombier 		r->pending = w->next;
350*5c47fe09SDavid du Colombier 		w->next = nil;
351*5c47fe09SDavid du Colombier 		if((z = w->z) != nil){
352*5c47fe09SDavid du Colombier 			w->z = nil;
353*5c47fe09SDavid du Colombier 			wakeup(z);
354*5c47fe09SDavid du Colombier 		}
355*5c47fe09SDavid du Colombier 	}
356*5c47fe09SDavid du Colombier }
357*5c47fe09SDavid du Colombier 
358*5c47fe09SDavid du Colombier static u64int
resetring(Ring * r)359*5c47fe09SDavid du Colombier resetring(Ring *r)
360*5c47fe09SDavid du Colombier {
361*5c47fe09SDavid du Colombier 	u64int pa;
362*5c47fe09SDavid du Colombier 
363*5c47fe09SDavid du Colombier 	ilock(r);
364*5c47fe09SDavid du Colombier 	flushring(r);
365*5c47fe09SDavid du Colombier 	r->rp = r->wp;
366*5c47fe09SDavid du Colombier 	pa = PCIWADDR(&r->base[4*(r->wp & r->mask)]) | ((~r->wp>>r->shift) & 1);
367*5c47fe09SDavid du Colombier 	iunlock(r);
368*5c47fe09SDavid du Colombier 
369*5c47fe09SDavid du Colombier 	return pa;
370*5c47fe09SDavid du Colombier }
371*5c47fe09SDavid du Colombier 
372*5c47fe09SDavid du Colombier static u32int*
xecp(Ctlr * ctlr,uchar id,u32int * p)373*5c47fe09SDavid du Colombier xecp(Ctlr *ctlr, uchar id, u32int *p)
374*5c47fe09SDavid du Colombier {
375*5c47fe09SDavid du Colombier 	u32int x, *e;
376*5c47fe09SDavid du Colombier 
377*5c47fe09SDavid du Colombier 	e = &ctlr->mmio[ctlr->pcidev->mem[0].size/4];
378*5c47fe09SDavid du Colombier 	if(p == nil){
379*5c47fe09SDavid du Colombier 		p = ctlr->mmio;
380*5c47fe09SDavid du Colombier 		x = ctlr->hccparams>>16;
381*5c47fe09SDavid du Colombier 	} else {
382*5c47fe09SDavid du Colombier 		assert(p < e);
383*5c47fe09SDavid du Colombier 		x = (*p>>8) & 255;
384*5c47fe09SDavid du Colombier 	}
385*5c47fe09SDavid du Colombier 	while(x != 0){
386*5c47fe09SDavid du Colombier 		p += x;
387*5c47fe09SDavid du Colombier 		if(p >= e)
388*5c47fe09SDavid du Colombier 			break;
389*5c47fe09SDavid du Colombier 		x = *p;
390*5c47fe09SDavid du Colombier 		if((x & 255) == id)
391*5c47fe09SDavid du Colombier 			return p;
392*5c47fe09SDavid du Colombier 		x >>= 8;
393*5c47fe09SDavid du Colombier 		x &= 255;
394*5c47fe09SDavid du Colombier 	}
395*5c47fe09SDavid du Colombier 	return nil;
396*5c47fe09SDavid du Colombier }
397*5c47fe09SDavid du Colombier 
398*5c47fe09SDavid du Colombier static void
handoff(Ctlr * ctlr)399*5c47fe09SDavid du Colombier handoff(Ctlr *ctlr)
400*5c47fe09SDavid du Colombier {
401*5c47fe09SDavid du Colombier 	u32int *r;
402*5c47fe09SDavid du Colombier 	int i;
403*5c47fe09SDavid du Colombier 
404*5c47fe09SDavid du Colombier 	if((r = xecp(ctlr, 1, nil)) == nil)
405*5c47fe09SDavid du Colombier 		return;
406*5c47fe09SDavid du Colombier 	if(getconf("*noxhcihandoff") == nil){
407*5c47fe09SDavid du Colombier 		r[0] |= 1<<24;		/* request ownership */
408*5c47fe09SDavid du Colombier 		for(i = 0; (r[0] & (1<<16)) != 0 && i<100; i++)
409*5c47fe09SDavid du Colombier 			tsleep(&up->sleep, return0, nil, 10);
410*5c47fe09SDavid du Colombier 	}
411*5c47fe09SDavid du Colombier 	/* disable SMI interrupts */
412*5c47fe09SDavid du Colombier 	r[1] &= 7<<1 | 255<<5 | 7<<17 | 7<<29;
413*5c47fe09SDavid du Colombier 
414*5c47fe09SDavid du Colombier 	/* clear BIOS ownership in case of timeout */
415*5c47fe09SDavid du Colombier 	r[0] &= ~(1<<16);
416*5c47fe09SDavid du Colombier }
417*5c47fe09SDavid du Colombier 
418*5c47fe09SDavid du Colombier static void
shutdown(Hci * hp)419*5c47fe09SDavid du Colombier shutdown(Hci *hp)
420*5c47fe09SDavid du Colombier {
421*5c47fe09SDavid du Colombier 	Ctlr *ctlr = hp->aux;
422*5c47fe09SDavid du Colombier 	int i;
423*5c47fe09SDavid du Colombier 
424*5c47fe09SDavid du Colombier 	ctlr->opr[USBCMD] = 0;
425*5c47fe09SDavid du Colombier 	for(i=0; (ctlr->opr[USBSTS] & HCH) == 0 && i < 10; i++)
426*5c47fe09SDavid du Colombier 		delay(10);
427*5c47fe09SDavid du Colombier 	pciintrdisable(ctlr->pcidev->tbdf, hp->interrupt, hp);
428*5c47fe09SDavid du Colombier 	pcidisable(ctlr->pcidev);
429*5c47fe09SDavid du Colombier }
430*5c47fe09SDavid du Colombier 
431*5c47fe09SDavid du Colombier static void
release(Ctlr * ctlr)432*5c47fe09SDavid du Colombier release(Ctlr *ctlr)
433*5c47fe09SDavid du Colombier {
434*5c47fe09SDavid du Colombier 	int i;
435*5c47fe09SDavid du Colombier 
436*5c47fe09SDavid du Colombier 	freering(ctlr->cr);
437*5c47fe09SDavid du Colombier 	for(i=0; i<nelem(ctlr->er); i++){
438*5c47fe09SDavid du Colombier 		freering(&ctlr->er[i]);
439*5c47fe09SDavid du Colombier 		free(ctlr->erst[i]);
440*5c47fe09SDavid du Colombier 		ctlr->erst[i] = nil;
441*5c47fe09SDavid du Colombier 	}
442*5c47fe09SDavid du Colombier 	free(ctlr->port), ctlr->port = nil;
443*5c47fe09SDavid du Colombier 	free(ctlr->slot), ctlr->slot = nil;
444*5c47fe09SDavid du Colombier 	free(ctlr->dcba), ctlr->dcba = nil;
445*5c47fe09SDavid du Colombier 	free(ctlr->sba), ctlr->sba = nil;
446*5c47fe09SDavid du Colombier 	if(ctlr->sbp != nil){
447*5c47fe09SDavid du Colombier 		dmaflush(0, ctlr->sbp, ctlr->nscratch*ctlr->pagesize);
448*5c47fe09SDavid du Colombier 		free(ctlr->sbp);
449*5c47fe09SDavid du Colombier 		ctlr->sbp = nil;
450*5c47fe09SDavid du Colombier 	}
451*5c47fe09SDavid du Colombier }
452*5c47fe09SDavid du Colombier 
453*5c47fe09SDavid du Colombier static void recover(void *arg);
454*5c47fe09SDavid du Colombier 
455*5c47fe09SDavid du Colombier static void
init(Hci * hp)456*5c47fe09SDavid du Colombier init(Hci *hp)
457*5c47fe09SDavid du Colombier {
458*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
459*5c47fe09SDavid du Colombier 	Port *pp;
460*5c47fe09SDavid du Colombier 	u32int *x;
461*5c47fe09SDavid du Colombier 	uchar *p;
462*5c47fe09SDavid du Colombier 	int i, j;
463*5c47fe09SDavid du Colombier 
464*5c47fe09SDavid du Colombier 	ctlr = hp->aux;
465*5c47fe09SDavid du Colombier 	pcienable(ctlr->pcidev);
466*5c47fe09SDavid du Colombier 	if(ctlr->mmio[CAPLENGTH] == -1){
467*5c47fe09SDavid du Colombier 		pcidisable(ctlr->pcidev);
468*5c47fe09SDavid du Colombier 		error("controller vanished");
469*5c47fe09SDavid du Colombier 	}
470*5c47fe09SDavid du Colombier 
471*5c47fe09SDavid du Colombier 	ctlr->opr = &ctlr->mmio[(ctlr->mmio[CAPLENGTH]&0xFF)/4];
472*5c47fe09SDavid du Colombier 	ctlr->dba = &ctlr->mmio[ctlr->mmio[DBOFF]/4];
473*5c47fe09SDavid du Colombier 	ctlr->rts = &ctlr->mmio[ctlr->mmio[RTSOFF]/4];
474*5c47fe09SDavid du Colombier 
475*5c47fe09SDavid du Colombier 	ctlr->hccparams = ctlr->mmio[HCCPARAMS];
476*5c47fe09SDavid du Colombier 	handoff(ctlr);
477*5c47fe09SDavid du Colombier 	xhcireset(BUSBNO(hp->tbdf)<<20 | BUSDNO(hp->tbdf)<<15 | BUSFNO(hp->tbdf)<<12);
478*5c47fe09SDavid du Colombier 
479*5c47fe09SDavid du Colombier 	for(i=0; (ctlr->opr[USBSTS] & CNR) != 0 && i<100; i++)
480*5c47fe09SDavid du Colombier 		tsleep(&up->sleep, return0, nil, 10);
481*5c47fe09SDavid du Colombier 	if(i == 100)
482*5c47fe09SDavid du Colombier 		print("%s: controller not ready, status %ux\n", hp->type, ctlr->opr[USBSTS]);
483*5c47fe09SDavid du Colombier 
484*5c47fe09SDavid du Colombier 	ctlr->opr[USBCMD] = HCRST;
485*5c47fe09SDavid du Colombier 	delay(1);
486*5c47fe09SDavid du Colombier 	for(i=0; (ctlr->opr[USBSTS] & (CNR|HCH)) != HCH && i<100; i++)
487*5c47fe09SDavid du Colombier 		tsleep(&up->sleep, return0, nil, 10);
488*5c47fe09SDavid du Colombier 	if(i == 100)
489*5c47fe09SDavid du Colombier 		print("%s: controller not halted, status %ux\n", hp->type, ctlr->opr[USBSTS]);
490*5c47fe09SDavid du Colombier 
491*5c47fe09SDavid du Colombier 	pcisetbme(ctlr->pcidev);
492*5c47fe09SDavid du Colombier 	pciintrenable(ctlr->pcidev->tbdf, hp->interrupt, hp);
493*5c47fe09SDavid du Colombier 
494*5c47fe09SDavid du Colombier 	if(waserror()){
495*5c47fe09SDavid du Colombier 		shutdown(hp);
496*5c47fe09SDavid du Colombier 		release(ctlr);
497*5c47fe09SDavid du Colombier 		nexterror();
498*5c47fe09SDavid du Colombier 	}
499*5c47fe09SDavid du Colombier 
500*5c47fe09SDavid du Colombier 	ctlr->csz = (ctlr->hccparams & CSZ) != 0;
501*5c47fe09SDavid du Colombier 	ctlr->pagesize = (ctlr->opr[PAGESIZE] & 0xFFFF) << 12;
502*5c47fe09SDavid du Colombier 
503*5c47fe09SDavid du Colombier 	ctlr->nscratch = (ctlr->mmio[HCSPARAMS2] >> 27) & 0x1F | (ctlr->mmio[HCSPARAMS2] >> 16) & 0x3E0;
504*5c47fe09SDavid du Colombier 	ctlr->nintrs = (ctlr->mmio[HCSPARAMS1] >> 8) & 0x7FF;
505*5c47fe09SDavid du Colombier 	ctlr->nslots = (ctlr->mmio[HCSPARAMS1] >> 0) & 0xFF;
506*5c47fe09SDavid du Colombier 
507*5c47fe09SDavid du Colombier 	hp->highspeed = 1;
508*5c47fe09SDavid du Colombier 	hp->superspeed = 0;
509*5c47fe09SDavid du Colombier 	hp->nports = (ctlr->mmio[HCSPARAMS1] >> 24) & 0xFF;
510*5c47fe09SDavid du Colombier 	ctlr->port = malloc(hp->nports * sizeof(Port));
511*5c47fe09SDavid du Colombier 	if(ctlr->port == nil)
512*5c47fe09SDavid du Colombier 		error(Enomem);
513*5c47fe09SDavid du Colombier 	for(i=0; i<hp->nports; i++)
514*5c47fe09SDavid du Colombier 		ctlr->port[i].reg = &ctlr->opr[0x400/4 + i*4];
515*5c47fe09SDavid du Colombier 
516*5c47fe09SDavid du Colombier 	x = nil;
517*5c47fe09SDavid du Colombier 	while((x = xecp(ctlr, 2, x)) != nil){
518*5c47fe09SDavid du Colombier 		i = x[2]&255;
519*5c47fe09SDavid du Colombier 		j = (x[2]>>8)&255;
520*5c47fe09SDavid du Colombier 		while(j--){
521*5c47fe09SDavid du Colombier 			if(i < 1 || i > hp->nports)
522*5c47fe09SDavid du Colombier 				break;
523*5c47fe09SDavid du Colombier 			pp = &ctlr->port[i-1];
524*5c47fe09SDavid du Colombier 			pp->proto = x[0]>>16;
525*5c47fe09SDavid du Colombier 			memmove(pp->spec, &x[1], 4);
526*5c47fe09SDavid du Colombier 			if(memcmp(pp->spec, "USB ", 4) == 0 && pp->proto >= 0x0300)
527*5c47fe09SDavid du Colombier 				hp->superspeed |= 1<<(i-1);
528*5c47fe09SDavid du Colombier 			i++;
529*5c47fe09SDavid du Colombier 		}
530*5c47fe09SDavid du Colombier 	}
531*5c47fe09SDavid du Colombier 
532*5c47fe09SDavid du Colombier 	ctlr->slot = malloc((1+ctlr->nslots)*sizeof(ctlr->slot[0]));
533*5c47fe09SDavid du Colombier 	ctlr->dcba = mallocalign((1+ctlr->nslots)*sizeof(ctlr->dcba[0]), 64, 0, ctlr->pagesize);
534*5c47fe09SDavid du Colombier 	if(ctlr->slot == nil || ctlr->dcba == nil)
535*5c47fe09SDavid du Colombier 		error(Enomem);
536*5c47fe09SDavid du Colombier 	if(ctlr->nscratch != 0){
537*5c47fe09SDavid du Colombier 		ctlr->sba = mallocalign(ctlr->nscratch*8, 64, 0, ctlr->pagesize);
538*5c47fe09SDavid du Colombier 		ctlr->sbp = mallocalign(ctlr->nscratch*ctlr->pagesize, ctlr->pagesize, 0, 0);
539*5c47fe09SDavid du Colombier 		if(ctlr->sba == nil || ctlr->sbp == nil)
540*5c47fe09SDavid du Colombier 			error(Enomem);
541*5c47fe09SDavid du Colombier 		for(i=0, p = ctlr->sbp; i<ctlr->nscratch; i++, p += ctlr->pagesize){
542*5c47fe09SDavid du Colombier 			memset(p, 0, ctlr->pagesize);
543*5c47fe09SDavid du Colombier 			ctlr->sba[i] = PCIWADDR(p);
544*5c47fe09SDavid du Colombier 		}
545*5c47fe09SDavid du Colombier 		dmaflush(1, ctlr->sbp, ctlr->nscratch*ctlr->pagesize);
546*5c47fe09SDavid du Colombier 		dmaflush(1, ctlr->sba, ctlr->nscratch*8);
547*5c47fe09SDavid du Colombier 		ctlr->dcba[0] = PCIWADDR(ctlr->sba);
548*5c47fe09SDavid du Colombier 	} else {
549*5c47fe09SDavid du Colombier 		ctlr->dcba[0] = 0;
550*5c47fe09SDavid du Colombier 	}
551*5c47fe09SDavid du Colombier 	for(i=1; i<=ctlr->nslots; i++)
552*5c47fe09SDavid du Colombier 		ctlr->dcba[i] = 0;
553*5c47fe09SDavid du Colombier 
554*5c47fe09SDavid du Colombier 	ctlr->opr[CONFIG] = (ctlr->opr[CONFIG] & 0xFFFFFC00) | ctlr->nslots;	/* MaxSlotsEn */
555*5c47fe09SDavid du Colombier 
556*5c47fe09SDavid du Colombier 	dmaflush(1, ctlr->dcba, (1+ctlr->nslots)*sizeof(ctlr->dcba[0]));
557*5c47fe09SDavid du Colombier 	setrptr(&ctlr->opr[DCBAAP], PCIWADDR(ctlr->dcba));
558*5c47fe09SDavid du Colombier 
559*5c47fe09SDavid du Colombier 	initring(ctlr->cr, 8);		/* 256 entries */
560*5c47fe09SDavid du Colombier 	ctlr->cr->id = 0;
561*5c47fe09SDavid du Colombier 	ctlr->cr->doorbell = &ctlr->dba[0];
562*5c47fe09SDavid du Colombier 	setrptr(&ctlr->opr[CRCR], resetring(ctlr->cr));
563*5c47fe09SDavid du Colombier 
564*5c47fe09SDavid du Colombier 	for(i=0; i<ctlr->nintrs; i++){
565*5c47fe09SDavid du Colombier 		u32int *irs = &ctlr->rts[IR0 + i*8];
566*5c47fe09SDavid du Colombier 
567*5c47fe09SDavid du Colombier 		if(i >= nelem(ctlr->er)){
568*5c47fe09SDavid du Colombier 			irs[ERSTSZ] = 0;	/* disable ring */
569*5c47fe09SDavid du Colombier 			irs[IMAN] = 1;
570*5c47fe09SDavid du Colombier 			irs[IMOD] = 0;
571*5c47fe09SDavid du Colombier 			setrptr(&irs[ERSTBA], 0);
572*5c47fe09SDavid du Colombier 			setrptr(&irs[ERDP], 0);
573*5c47fe09SDavid du Colombier 			continue;
574*5c47fe09SDavid du Colombier 		}
575*5c47fe09SDavid du Colombier 
576*5c47fe09SDavid du Colombier 		/* allocate and link into event ring segment table */
577*5c47fe09SDavid du Colombier 		initring(&ctlr->er[i], 8);	/* 256 entries */
578*5c47fe09SDavid du Colombier 		ctlr->erst[i] = mallocalign(4*4, 64, 0, 0);
579*5c47fe09SDavid du Colombier 		if(ctlr->erst[i] == nil)
580*5c47fe09SDavid du Colombier 			error(Enomem);
581*5c47fe09SDavid du Colombier 		*((u64int*)ctlr->erst[i]) = PCIWADDR(ctlr->er[i].base);
582*5c47fe09SDavid du Colombier 		ctlr->erst[i][2] = ctlr->er[i].mask+1;
583*5c47fe09SDavid du Colombier 		ctlr->erst[i][3] = 0;
584*5c47fe09SDavid du Colombier 		dmaflush(1, ctlr->erst[i], 4*4);
585*5c47fe09SDavid du Colombier 
586*5c47fe09SDavid du Colombier 		irs[ERSTSZ] = 1;	/* just one segment */
587*5c47fe09SDavid du Colombier 		irs[IMAN] = 3;
588*5c47fe09SDavid du Colombier 		irs[IMOD] = 0;
589*5c47fe09SDavid du Colombier 		setrptr(&irs[ERSTBA], PCIWADDR(ctlr->erst[i]));
590*5c47fe09SDavid du Colombier 		setrptr(&irs[ERDP], PCIWADDR(ctlr->er[i].base) | (1<<3));
591*5c47fe09SDavid du Colombier 	}
592*5c47fe09SDavid du Colombier 	poperror();
593*5c47fe09SDavid du Colombier 
594*5c47fe09SDavid du Colombier 	ctlr->µframe = 0;
595*5c47fe09SDavid du Colombier 	ctlr->opr[USBSTS] = ctlr->opr[USBSTS] & (HSE|EINT|PCD|SRE);
596*5c47fe09SDavid du Colombier 	coherence();
597*5c47fe09SDavid du Colombier 
598*5c47fe09SDavid du Colombier 	ctlr->opr[USBCMD] = RUNSTOP|INTE|HSEE|EWE;
599*5c47fe09SDavid du Colombier 	for(i=0; (ctlr->opr[USBSTS] & (CNR|HCH)) != 0 && i<100; i++)
600*5c47fe09SDavid du Colombier 		tsleep(&up->sleep, return0, nil, 10);
601*5c47fe09SDavid du Colombier 
602*5c47fe09SDavid du Colombier 	kproc("xhcirecover", recover, hp);
603*5c47fe09SDavid du Colombier }
604*5c47fe09SDavid du Colombier 
605*5c47fe09SDavid du Colombier static int
needrecover(void * arg)606*5c47fe09SDavid du Colombier needrecover(void *arg)
607*5c47fe09SDavid du Colombier {
608*5c47fe09SDavid du Colombier 	Ctlr *ctlr = arg;
609*5c47fe09SDavid du Colombier 	return 	ctlr->er->stopped ||
610*5c47fe09SDavid du Colombier 		(ctlr->opr[USBSTS] & (HCH|HCE|HSE)) != 0;
611*5c47fe09SDavid du Colombier }
612*5c47fe09SDavid du Colombier 
613*5c47fe09SDavid du Colombier static void
recover(void * arg)614*5c47fe09SDavid du Colombier recover(void *arg)
615*5c47fe09SDavid du Colombier {
616*5c47fe09SDavid du Colombier 	Hci *hp = arg;
617*5c47fe09SDavid du Colombier 	Ctlr *ctlr = hp->aux;
618*5c47fe09SDavid du Colombier 
619*5c47fe09SDavid du Colombier 	while(waserror())
620*5c47fe09SDavid du Colombier 		;
621*5c47fe09SDavid du Colombier 	while(!needrecover(ctlr))
622*5c47fe09SDavid du Colombier 		tsleep(&ctlr->recover, needrecover, ctlr, 10);
623*5c47fe09SDavid du Colombier 	shutdown(hp);
624*5c47fe09SDavid du Colombier 
625*5c47fe09SDavid du Colombier 	/*
626*5c47fe09SDavid du Colombier 	 * flush all transactions and wait until all devices have
627*5c47fe09SDavid du Colombier 	 * been detached by usbd.
628*5c47fe09SDavid du Colombier 	 */
629*5c47fe09SDavid du Colombier 	for(;;){
630*5c47fe09SDavid du Colombier 		int i, j, active;
631*5c47fe09SDavid du Colombier 
632*5c47fe09SDavid du Colombier 		ilock(ctlr->cr);
633*5c47fe09SDavid du Colombier 		ctlr->cr->stopped = 1;
634*5c47fe09SDavid du Colombier 		flushring(ctlr->cr);
635*5c47fe09SDavid du Colombier 		iunlock(ctlr->cr);
636*5c47fe09SDavid du Colombier 
637*5c47fe09SDavid du Colombier 		active = 0;
638*5c47fe09SDavid du Colombier 		qlock(&ctlr->slotlock);
639*5c47fe09SDavid du Colombier 		for(i=1; i<=ctlr->nslots; i++){
640*5c47fe09SDavid du Colombier 			Slot *slot = ctlr->slot[i];
641*5c47fe09SDavid du Colombier 			if(slot == nil)
642*5c47fe09SDavid du Colombier 				continue;
643*5c47fe09SDavid du Colombier 			active++;
644*5c47fe09SDavid du Colombier 			for(j=0; j < slot->nep; j++){
645*5c47fe09SDavid du Colombier 				Ring *ring = &slot->epr[j];
646*5c47fe09SDavid du Colombier 				if(ring->base == nil)
647*5c47fe09SDavid du Colombier 					continue;
648*5c47fe09SDavid du Colombier 				ilock(ring);
649*5c47fe09SDavid du Colombier 				ring->stopped = 1;
650*5c47fe09SDavid du Colombier 				flushring(ring);
651*5c47fe09SDavid du Colombier 				iunlock(ring);
652*5c47fe09SDavid du Colombier 			}
653*5c47fe09SDavid du Colombier 		}
654*5c47fe09SDavid du Colombier 		qunlock(&ctlr->slotlock);
655*5c47fe09SDavid du Colombier 		if(active == 0)
656*5c47fe09SDavid du Colombier 			break;
657*5c47fe09SDavid du Colombier 
658*5c47fe09SDavid du Colombier 		tsleep(&up->sleep, return0, nil, 100);
659*5c47fe09SDavid du Colombier 	}
660*5c47fe09SDavid du Colombier 
661*5c47fe09SDavid du Colombier 	qlock(&ctlr->slotlock);
662*5c47fe09SDavid du Colombier 	qlock(&ctlr->cmdlock);
663*5c47fe09SDavid du Colombier 
664*5c47fe09SDavid du Colombier 	release(ctlr);
665*5c47fe09SDavid du Colombier 	if(waserror()) {
666*5c47fe09SDavid du Colombier 		print("xhci recovery failed: %s\n", up->errstr);
667*5c47fe09SDavid du Colombier 	} else {
668*5c47fe09SDavid du Colombier 		init(hp);
669*5c47fe09SDavid du Colombier 		poperror();
670*5c47fe09SDavid du Colombier 	}
671*5c47fe09SDavid du Colombier 
672*5c47fe09SDavid du Colombier 	qunlock(&ctlr->cmdlock);
673*5c47fe09SDavid du Colombier 	qunlock(&ctlr->slotlock);
674*5c47fe09SDavid du Colombier 
675*5c47fe09SDavid du Colombier 	pexit("", 1);
676*5c47fe09SDavid du Colombier }
677*5c47fe09SDavid du Colombier 
678*5c47fe09SDavid du Colombier static void
dump(Hci *)679*5c47fe09SDavid du Colombier dump(Hci *)
680*5c47fe09SDavid du Colombier {
681*5c47fe09SDavid du Colombier }
682*5c47fe09SDavid du Colombier 
683*5c47fe09SDavid du Colombier static void
queuetd(Ring * r,u32int c,u32int s,u64int p,Wait * w)684*5c47fe09SDavid du Colombier queuetd(Ring *r, u32int c, u32int s, u64int p, Wait *w)
685*5c47fe09SDavid du Colombier {
686*5c47fe09SDavid du Colombier 	u32int *td, x;
687*5c47fe09SDavid du Colombier 
688*5c47fe09SDavid du Colombier 	x = r->wp++;
689*5c47fe09SDavid du Colombier 	if((x & r->mask) == r->mask){
690*5c47fe09SDavid du Colombier 		td = r->base + 4*(x & r->mask);
691*5c47fe09SDavid du Colombier 		*(u64int*)td = PCIWADDR(r->base);
692*5c47fe09SDavid du Colombier 		td[2] = 0;
693*5c47fe09SDavid du Colombier 		td[3] = ((~x>>r->shift)&1) | (1<<1) | TR_LINK;
694*5c47fe09SDavid du Colombier 		dmaflush(1, td, 4*4);
695*5c47fe09SDavid du Colombier 		x = r->wp++;
696*5c47fe09SDavid du Colombier 	}
697*5c47fe09SDavid du Colombier 	td = r->base + 4*(x & r->mask);
698*5c47fe09SDavid du Colombier 	if(w != nil){
699*5c47fe09SDavid du Colombier 		w->er[0] = w->er[1] = w->er[2] = w->er[3] = 0;
700*5c47fe09SDavid du Colombier 		w->ring = r;
701*5c47fe09SDavid du Colombier 		w->td = td;
702*5c47fe09SDavid du Colombier 		w->z = &up->sleep;
703*5c47fe09SDavid du Colombier 
704*5c47fe09SDavid du Colombier 		ilock(r);
705*5c47fe09SDavid du Colombier 		w->next = r->pending;
706*5c47fe09SDavid du Colombier 		r->pending = w;
707*5c47fe09SDavid du Colombier 		iunlock(r);
708*5c47fe09SDavid du Colombier 	}
709*5c47fe09SDavid du Colombier 	coherence();
710*5c47fe09SDavid du Colombier 	*(u64int*)td = p;
711*5c47fe09SDavid du Colombier 	td[2] = s;
712*5c47fe09SDavid du Colombier 	td[3] = ((~x>>r->shift)&1) | c;
713*5c47fe09SDavid du Colombier 	dmaflush(1, td, 4*4);
714*5c47fe09SDavid du Colombier }
715*5c47fe09SDavid du Colombier 
716*5c47fe09SDavid du Colombier static char *ccerrtab[] = {
717*5c47fe09SDavid du Colombier [2]	"Data Buffer Error",
718*5c47fe09SDavid du Colombier [3]	"Babble Detected Error",
719*5c47fe09SDavid du Colombier [4]	"USB Transaction Error",
720*5c47fe09SDavid du Colombier [5]	"TRB Error",
721*5c47fe09SDavid du Colombier [6]	"Stall Error",
722*5c47fe09SDavid du Colombier [7]	"Resume Error",
723*5c47fe09SDavid du Colombier [8]	"Bandwidth Error",
724*5c47fe09SDavid du Colombier [9]	"No Slots Available",
725*5c47fe09SDavid du Colombier [10]	"Invalid Stream Type",
726*5c47fe09SDavid du Colombier [11]	"Slot Not Enabled",
727*5c47fe09SDavid du Colombier [12]	"Endpoint Not Enabled",
728*5c47fe09SDavid du Colombier [13]	"Short Packet",
729*5c47fe09SDavid du Colombier [14]	"Ring Underrun",
730*5c47fe09SDavid du Colombier [15]	"Ring Overrun",
731*5c47fe09SDavid du Colombier [16]	"VF Event Ring Full",
732*5c47fe09SDavid du Colombier [17]	"Parameter Error",
733*5c47fe09SDavid du Colombier [18]	"Bandwidth Overrun Error",
734*5c47fe09SDavid du Colombier [19]	"Context State Error",
735*5c47fe09SDavid du Colombier [20]	"No Ping Response",
736*5c47fe09SDavid du Colombier [21]	"Event Ring Full",
737*5c47fe09SDavid du Colombier [22]	"Incompatible Device",
738*5c47fe09SDavid du Colombier [23]	"Missed Service Error",
739*5c47fe09SDavid du Colombier [24]	"Command Ring Stopped",
740*5c47fe09SDavid du Colombier [25]	"Command Aborted",
741*5c47fe09SDavid du Colombier [26]	"Stopped",
742*5c47fe09SDavid du Colombier [27]	"Stopped - Length Invalid",
743*5c47fe09SDavid du Colombier [29]	"Max Exit Latency Too Large",
744*5c47fe09SDavid du Colombier [31]	"Isoch Buffer Overrun",
745*5c47fe09SDavid du Colombier [32]	"Event Lost Error",
746*5c47fe09SDavid du Colombier [33]	"Undefined Error",
747*5c47fe09SDavid du Colombier [34]	"Invalid Stream ID",
748*5c47fe09SDavid du Colombier [35]	"Secondary Bandwidth Error",
749*5c47fe09SDavid du Colombier [36]	"Split Transaction Error",
750*5c47fe09SDavid du Colombier };
751*5c47fe09SDavid du Colombier 
752*5c47fe09SDavid du Colombier static char*
ccerrstr(u32int cc)753*5c47fe09SDavid du Colombier ccerrstr(u32int cc)
754*5c47fe09SDavid du Colombier {
755*5c47fe09SDavid du Colombier 	char *s;
756*5c47fe09SDavid du Colombier 
757*5c47fe09SDavid du Colombier 	if(cc == 1 || cc == 13)
758*5c47fe09SDavid du Colombier 		return nil;
759*5c47fe09SDavid du Colombier 	if(cc < nelem(ccerrtab) && ccerrtab[cc] != nil)
760*5c47fe09SDavid du Colombier 		s = ccerrtab[cc];
761*5c47fe09SDavid du Colombier 	else
762*5c47fe09SDavid du Colombier 		s = "???";
763*5c47fe09SDavid du Colombier 	return s;
764*5c47fe09SDavid du Colombier }
765*5c47fe09SDavid du Colombier 
766*5c47fe09SDavid du Colombier static int
waitdone(void * a)767*5c47fe09SDavid du Colombier waitdone(void *a)
768*5c47fe09SDavid du Colombier {
769*5c47fe09SDavid du Colombier 	return ((Wait*)a)->z == nil;
770*5c47fe09SDavid du Colombier }
771*5c47fe09SDavid du Colombier 
772*5c47fe09SDavid du Colombier static char*
waittd(Ctlr * ctlr,Wait * w,int tmout)773*5c47fe09SDavid du Colombier waittd(Ctlr *ctlr, Wait *w, int tmout)
774*5c47fe09SDavid du Colombier {
775*5c47fe09SDavid du Colombier 	Ring *r = w->ring;
776*5c47fe09SDavid du Colombier 
777*5c47fe09SDavid du Colombier 	coherence();
778*5c47fe09SDavid du Colombier 	*r->doorbell = r->id;
779*5c47fe09SDavid du Colombier 
780*5c47fe09SDavid du Colombier 	while(waserror()){
781*5c47fe09SDavid du Colombier 		if(r->stopped) {
782*5c47fe09SDavid du Colombier 			ctlr->er->stopped = 1;
783*5c47fe09SDavid du Colombier 			wakeup(&ctlr->recover);
784*5c47fe09SDavid du Colombier 
785*5c47fe09SDavid du Colombier 			/* wait for rescue */
786*5c47fe09SDavid du Colombier 			tmout = 0;
787*5c47fe09SDavid du Colombier 			continue;
788*5c47fe09SDavid du Colombier 		}
789*5c47fe09SDavid du Colombier 
790*5c47fe09SDavid du Colombier 		if(r == ctlr->cr)
791*5c47fe09SDavid du Colombier 			ctlr->opr[CRCR] |= CA;
792*5c47fe09SDavid du Colombier 		else
793*5c47fe09SDavid du Colombier 			ctlrcmd(ctlr, CR_STOPEP | (r->id<<16) | (r->slot->id<<24), 0, 0, nil);
794*5c47fe09SDavid du Colombier 		r->stopped = 1;
795*5c47fe09SDavid du Colombier 
796*5c47fe09SDavid du Colombier 		/* time to abort the transaction */
797*5c47fe09SDavid du Colombier 		tmout = 5000;
798*5c47fe09SDavid du Colombier 	}
799*5c47fe09SDavid du Colombier 	if(tmout > 0){
800*5c47fe09SDavid du Colombier 		tsleep(&up->sleep, waitdone, w, tmout);
801*5c47fe09SDavid du Colombier 		if(!waitdone(w))
802*5c47fe09SDavid du Colombier 			error("timed out");
803*5c47fe09SDavid du Colombier 	} else {
804*5c47fe09SDavid du Colombier 		while(!waitdone(w))
805*5c47fe09SDavid du Colombier 			sleep(&up->sleep, waitdone, w);
806*5c47fe09SDavid du Colombier 	}
807*5c47fe09SDavid du Colombier 	poperror();
808*5c47fe09SDavid du Colombier 	return ccerrstr(w->er[2]>>24);
809*5c47fe09SDavid du Colombier }
810*5c47fe09SDavid du Colombier 
811*5c47fe09SDavid du Colombier static char*
ctlrcmd(Ctlr * ctlr,u32int c,u32int s,u64int p,u32int * er)812*5c47fe09SDavid du Colombier ctlrcmd(Ctlr *ctlr, u32int c, u32int s, u64int p, u32int *er)
813*5c47fe09SDavid du Colombier {
814*5c47fe09SDavid du Colombier 	Wait w[1];
815*5c47fe09SDavid du Colombier 	char *err;
816*5c47fe09SDavid du Colombier 
817*5c47fe09SDavid du Colombier 	qlock(&ctlr->cmdlock);
818*5c47fe09SDavid du Colombier 	if(needrecover(ctlr)){
819*5c47fe09SDavid du Colombier 		qunlock(&ctlr->cmdlock);
820*5c47fe09SDavid du Colombier 		return Erecover;
821*5c47fe09SDavid du Colombier 	}
822*5c47fe09SDavid du Colombier 	ctlr->cr->stopped = 0;
823*5c47fe09SDavid du Colombier 	queuetd(ctlr->cr, c, s, p, w);
824*5c47fe09SDavid du Colombier 	err = waittd(ctlr, w, 5000);
825*5c47fe09SDavid du Colombier 	qunlock(&ctlr->cmdlock);
826*5c47fe09SDavid du Colombier 
827*5c47fe09SDavid du Colombier 	if(er != nil)
828*5c47fe09SDavid du Colombier 		memmove(er, w->er, 4*4);
829*5c47fe09SDavid du Colombier 
830*5c47fe09SDavid du Colombier 	return err;
831*5c47fe09SDavid du Colombier }
832*5c47fe09SDavid du Colombier 
833*5c47fe09SDavid du Colombier static void
completering(Ring * r,u32int * er)834*5c47fe09SDavid du Colombier completering(Ring *r, u32int *er)
835*5c47fe09SDavid du Colombier {
836*5c47fe09SDavid du Colombier 	Wait *w, **wp;
837*5c47fe09SDavid du Colombier 	u32int *td, x;
838*5c47fe09SDavid du Colombier 	u64int pa;
839*5c47fe09SDavid du Colombier 
840*5c47fe09SDavid du Colombier 	pa = (*(u64int*)er) & ~15ULL;
841*5c47fe09SDavid du Colombier 	ilock(r);
842*5c47fe09SDavid du Colombier 
843*5c47fe09SDavid du Colombier 	for(x = r->rp; (int)(r->wp - x) > 0;){
844*5c47fe09SDavid du Colombier 		td = &r->base[4*(x++ & r->mask)];
845*5c47fe09SDavid du Colombier 		if((u64int)PCIWADDR(td) == pa){
846*5c47fe09SDavid du Colombier 			r->rp = x;
847*5c47fe09SDavid du Colombier 			break;
848*5c47fe09SDavid du Colombier 		}
849*5c47fe09SDavid du Colombier 	}
850*5c47fe09SDavid du Colombier 
851*5c47fe09SDavid du Colombier 	wp = &r->pending;
852*5c47fe09SDavid du Colombier 	while(w = *wp){
853*5c47fe09SDavid du Colombier 		if((u64int)PCIWADDR(w->td) == pa){
854*5c47fe09SDavid du Colombier 			Rendez *z = w->z;
855*5c47fe09SDavid du Colombier 
856*5c47fe09SDavid du Colombier 			memmove(w->er, er, 4*4);
857*5c47fe09SDavid du Colombier 			*wp = w->next;
858*5c47fe09SDavid du Colombier 			w->next = nil;
859*5c47fe09SDavid du Colombier 
860*5c47fe09SDavid du Colombier 			if(z != nil){
861*5c47fe09SDavid du Colombier 				w->z = nil;
862*5c47fe09SDavid du Colombier 				wakeup(z);
863*5c47fe09SDavid du Colombier 			}
864*5c47fe09SDavid du Colombier 			break;
865*5c47fe09SDavid du Colombier 		} else {
866*5c47fe09SDavid du Colombier 			wp = &w->next;
867*5c47fe09SDavid du Colombier 		}
868*5c47fe09SDavid du Colombier 	}
869*5c47fe09SDavid du Colombier 
870*5c47fe09SDavid du Colombier 	iunlock(r);
871*5c47fe09SDavid du Colombier }
872*5c47fe09SDavid du Colombier 
873*5c47fe09SDavid du Colombier static void
interrupt(Ureg *,void * arg)874*5c47fe09SDavid du Colombier interrupt(Ureg*, void *arg)
875*5c47fe09SDavid du Colombier {
876*5c47fe09SDavid du Colombier 	Hci *hp = arg;
877*5c47fe09SDavid du Colombier 	Ctlr *ctlr = hp->aux;
878*5c47fe09SDavid du Colombier 	Ring *ring = ctlr->er;
879*5c47fe09SDavid du Colombier 	Slot *slot;
880*5c47fe09SDavid du Colombier 	u32int *irs, *td, x;
881*5c47fe09SDavid du Colombier 
882*5c47fe09SDavid du Colombier 	if(ring->base == nil)
883*5c47fe09SDavid du Colombier 		return;
884*5c47fe09SDavid du Colombier 
885*5c47fe09SDavid du Colombier 	irs = &ctlr->rts[IR0];
886*5c47fe09SDavid du Colombier 	x = irs[IMAN];
887*5c47fe09SDavid du Colombier 	if(x & 1) irs[IMAN] = x & 3;
888*5c47fe09SDavid du Colombier 
889*5c47fe09SDavid du Colombier 	for(x = ring->rp;; x=++ring->rp){
890*5c47fe09SDavid du Colombier 		td = ring->base + 4*(x & ring->mask);
891*5c47fe09SDavid du Colombier 		dmaflush(0, td, 4*4);
892*5c47fe09SDavid du Colombier 
893*5c47fe09SDavid du Colombier 		if((((x>>ring->shift)^td[3])&1) == 0)
894*5c47fe09SDavid du Colombier 			break;
895*5c47fe09SDavid du Colombier 
896*5c47fe09SDavid du Colombier 		switch(td[3] & 0xFC00){
897*5c47fe09SDavid du Colombier 		case ER_CMDCOMPL:
898*5c47fe09SDavid du Colombier 			completering(ctlr->cr, td);
899*5c47fe09SDavid du Colombier 			break;
900*5c47fe09SDavid du Colombier 		case ER_TRANSFER:
901*5c47fe09SDavid du Colombier 			x = td[3]>>24;
902*5c47fe09SDavid du Colombier 			if(x == 0 || x > ctlr->nslots)
903*5c47fe09SDavid du Colombier 				break;
904*5c47fe09SDavid du Colombier 			slot = ctlr->slot[x];
905*5c47fe09SDavid du Colombier 			if(slot == nil)
906*5c47fe09SDavid du Colombier 				break;
907*5c47fe09SDavid du Colombier 			completering(&slot->epr[(td[3]>>16)-1&31], td);
908*5c47fe09SDavid du Colombier 			break;
909*5c47fe09SDavid du Colombier 		case ER_MFINDEXWRAP:
910*5c47fe09SDavid du Colombier 			ctlr->µframe = (ctlr->rts[MFINDEX] & (1<<14)-1) |
911*5c47fe09SDavid du Colombier 				(ctlr->µframe+(1<<14) & ~((1<<14)-1));
912*5c47fe09SDavid du Colombier 			break;
913*5c47fe09SDavid du Colombier 		case ER_HCE:
914*5c47fe09SDavid du Colombier 			iprint("xhci: host controller error: %ux %ux %ux %ux\n",
915*5c47fe09SDavid du Colombier 				td[0], td[1], td[2], td[3]);
916*5c47fe09SDavid du Colombier 			ctlr->er->stopped = 1;
917*5c47fe09SDavid du Colombier 			wakeup(&ctlr->recover);
918*5c47fe09SDavid du Colombier 			return;
919*5c47fe09SDavid du Colombier 		case ER_PORTSC:
920*5c47fe09SDavid du Colombier 			break;
921*5c47fe09SDavid du Colombier 		case ER_BWREQ:
922*5c47fe09SDavid du Colombier 		case ER_DOORBELL:
923*5c47fe09SDavid du Colombier 		case ER_DEVNOTE:
924*5c47fe09SDavid du Colombier 		default:
925*5c47fe09SDavid du Colombier 			iprint("xhci: event %ud: %ux %ux %ux %ux\n",
926*5c47fe09SDavid du Colombier 				x, td[0], td[1], td[2], td[3]);
927*5c47fe09SDavid du Colombier 		}
928*5c47fe09SDavid du Colombier 	}
929*5c47fe09SDavid du Colombier 
930*5c47fe09SDavid du Colombier 	setrptr(&irs[ERDP], PCIWADDR(td) | (1<<3));
931*5c47fe09SDavid du Colombier }
932*5c47fe09SDavid du Colombier 
933*5c47fe09SDavid du Colombier static void
freeslot(void * arg)934*5c47fe09SDavid du Colombier freeslot(void *arg)
935*5c47fe09SDavid du Colombier {
936*5c47fe09SDavid du Colombier 	Slot *slot;
937*5c47fe09SDavid du Colombier 
938*5c47fe09SDavid du Colombier 	if(arg == nil)
939*5c47fe09SDavid du Colombier 		return;
940*5c47fe09SDavid du Colombier 	slot = arg;
941*5c47fe09SDavid du Colombier 	if(slot->id != 0){
942*5c47fe09SDavid du Colombier 		Ctlr *ctlr = slot->ctlr;
943*5c47fe09SDavid du Colombier 		qlock(&ctlr->slotlock);
944*5c47fe09SDavid du Colombier 		if(ctlr->slot != nil && ctlr->slot[slot->id] == slot){
945*5c47fe09SDavid du Colombier 			ctlrcmd(ctlr, CR_DISABLESLOT | (slot->id<<24), 0, 0, nil);
946*5c47fe09SDavid du Colombier 			dmaflush(0, slot->obase, 32*32 << ctlr->csz);
947*5c47fe09SDavid du Colombier 			ctlr->dcba[slot->id] = 0;
948*5c47fe09SDavid du Colombier 			dmaflush(1, &ctlr->dcba[slot->id], sizeof(ctlr->dcba[0]));
949*5c47fe09SDavid du Colombier 			ctlr->slot[slot->id] = nil;
950*5c47fe09SDavid du Colombier 		}
951*5c47fe09SDavid du Colombier 		qunlock(&ctlr->slotlock);
952*5c47fe09SDavid du Colombier 	}
953*5c47fe09SDavid du Colombier 	freering(&slot->epr[0]);
954*5c47fe09SDavid du Colombier 	free(slot->ibase);
955*5c47fe09SDavid du Colombier 	free(slot->obase);
956*5c47fe09SDavid du Colombier 	free(slot);
957*5c47fe09SDavid du Colombier }
958*5c47fe09SDavid du Colombier 
959*5c47fe09SDavid du Colombier static Slot*
allocslot(Ctlr * ctlr,Udev * dev)960*5c47fe09SDavid du Colombier allocslot(Ctlr *ctlr, Udev *dev)
961*5c47fe09SDavid du Colombier {
962*5c47fe09SDavid du Colombier 	u32int r[4];
963*5c47fe09SDavid du Colombier 	Slot *slot;
964*5c47fe09SDavid du Colombier 	char *err;
965*5c47fe09SDavid du Colombier 
966*5c47fe09SDavid du Colombier 	slot = malloc(sizeof(Slot));
967*5c47fe09SDavid du Colombier 	if(slot == nil)
968*5c47fe09SDavid du Colombier 		error(Enomem);
969*5c47fe09SDavid du Colombier 
970*5c47fe09SDavid du Colombier 	slot->ctlr = ctlr;
971*5c47fe09SDavid du Colombier 	slot->dev = dev;
972*5c47fe09SDavid du Colombier 	slot->nep = 0;
973*5c47fe09SDavid du Colombier 	slot->id = 0;
974*5c47fe09SDavid du Colombier 
975*5c47fe09SDavid du Colombier 	slot->confval = 0;
976*5c47fe09SDavid du Colombier 	slot->iface = 0;
977*5c47fe09SDavid du Colombier 	slot->altc = 0;
978*5c47fe09SDavid du Colombier 
979*5c47fe09SDavid du Colombier 	qlock(&ctlr->slotlock);
980*5c47fe09SDavid du Colombier 	if(waserror()){
981*5c47fe09SDavid du Colombier 		qunlock(&ctlr->slotlock);
982*5c47fe09SDavid du Colombier 		freeslot(slot);
983*5c47fe09SDavid du Colombier 		nexterror();
984*5c47fe09SDavid du Colombier 	}
985*5c47fe09SDavid du Colombier 	if(ctlr->slot == nil)
986*5c47fe09SDavid du Colombier 		error(Erecover);
987*5c47fe09SDavid du Colombier 	slot->ibase = mallocalign(32*33 << ctlr->csz, 64, 0, ctlr->pagesize);
988*5c47fe09SDavid du Colombier 	slot->obase = mallocalign(32*32 << ctlr->csz, 64, 0, ctlr->pagesize);
989*5c47fe09SDavid du Colombier 	if(slot->ibase == nil || slot->obase == nil)
990*5c47fe09SDavid du Colombier 		error(Enomem);
991*5c47fe09SDavid du Colombier 
992*5c47fe09SDavid du Colombier 	if((err = ctlrcmd(ctlr, CR_ENABLESLOT, 0, 0, r)) != nil)
993*5c47fe09SDavid du Colombier 		error(err);
994*5c47fe09SDavid du Colombier 	slot->id = r[3]>>24;
995*5c47fe09SDavid du Colombier 	if(slot->id <= 0 || slot->id > ctlr->nslots || ctlr->slot[slot->id] != nil){
996*5c47fe09SDavid du Colombier 		slot->id = 0;
997*5c47fe09SDavid du Colombier 		error("bad slot id from controller");
998*5c47fe09SDavid du Colombier 	}
999*5c47fe09SDavid du Colombier 	poperror();
1000*5c47fe09SDavid du Colombier 
1001*5c47fe09SDavid du Colombier 	dmaflush(1, slot->obase, 32*32 << ctlr->csz);
1002*5c47fe09SDavid du Colombier 	ctlr->dcba[slot->id] = PCIWADDR(slot->obase);
1003*5c47fe09SDavid du Colombier 	dmaflush(1, &ctlr->dcba[slot->id], sizeof(ctlr->dcba[0]));
1004*5c47fe09SDavid du Colombier 
1005*5c47fe09SDavid du Colombier 	ctlr->slot[slot->id] = slot;
1006*5c47fe09SDavid du Colombier 
1007*5c47fe09SDavid du Colombier 	qunlock(&ctlr->slotlock);
1008*5c47fe09SDavid du Colombier 
1009*5c47fe09SDavid du Colombier 	return slot;
1010*5c47fe09SDavid du Colombier }
1011*5c47fe09SDavid du Colombier 
1012*5c47fe09SDavid du Colombier static void
setdebug(Hci *,int)1013*5c47fe09SDavid du Colombier setdebug(Hci *, int)
1014*5c47fe09SDavid du Colombier {
1015*5c47fe09SDavid du Colombier }
1016*5c47fe09SDavid du Colombier 
1017*5c47fe09SDavid du Colombier static void
epclose(Ep * ep)1018*5c47fe09SDavid du Colombier epclose(Ep *ep)
1019*5c47fe09SDavid du Colombier {
1020*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
1021*5c47fe09SDavid du Colombier 	Slot *slot;
1022*5c47fe09SDavid du Colombier 	Ring *ring;
1023*5c47fe09SDavid du Colombier 	Epio *io;
1024*5c47fe09SDavid du Colombier 
1025*5c47fe09SDavid du Colombier 	if(ep->dev->isroot)
1026*5c47fe09SDavid du Colombier 		return;
1027*5c47fe09SDavid du Colombier 
1028*5c47fe09SDavid du Colombier 	io = ep->aux;
1029*5c47fe09SDavid du Colombier 	if(io == nil)
1030*5c47fe09SDavid du Colombier 		return;
1031*5c47fe09SDavid du Colombier 	ep->aux = nil;
1032*5c47fe09SDavid du Colombier 
1033*5c47fe09SDavid du Colombier 	ctlr = ep->hp->aux;
1034*5c47fe09SDavid du Colombier 	slot = ep->dev->aux;
1035*5c47fe09SDavid du Colombier 
1036*5c47fe09SDavid du Colombier 	if(ep->nb > 0 && (io[OREAD].ring != nil || io[OWRITE].ring != nil)){
1037*5c47fe09SDavid du Colombier 		u32int *w;
1038*5c47fe09SDavid du Colombier 
1039*5c47fe09SDavid du Colombier 		/* input control context */
1040*5c47fe09SDavid du Colombier 		w = slot->ibase;
1041*5c47fe09SDavid du Colombier 		memset(w, 0, 32<<ctlr->csz);
1042*5c47fe09SDavid du Colombier 		w[1] = 1;
1043*5c47fe09SDavid du Colombier 		if((ring = io[OREAD].ring) != nil){
1044*5c47fe09SDavid du Colombier 			w[0] |= 1 << ring->id;
1045*5c47fe09SDavid du Colombier 			if(ring->id == slot->nep)
1046*5c47fe09SDavid du Colombier 				slot->nep--;
1047*5c47fe09SDavid du Colombier 			ctlrcmd(ctlr, CR_STOPEP | (ring->id<<16) | (slot->id<<24), 0, 0, nil);
1048*5c47fe09SDavid du Colombier 		}
1049*5c47fe09SDavid du Colombier 		if((ring = io[OWRITE].ring) != nil){
1050*5c47fe09SDavid du Colombier 			w[0] |= 1 << ring->id;
1051*5c47fe09SDavid du Colombier 			if(ring->id == slot->nep)
1052*5c47fe09SDavid du Colombier 				slot->nep--;
1053*5c47fe09SDavid du Colombier 			ctlrcmd(ctlr, CR_STOPEP | (ring->id<<16) | (slot->id<<24), 0, 0, nil);
1054*5c47fe09SDavid du Colombier 		}
1055*5c47fe09SDavid du Colombier 
1056*5c47fe09SDavid du Colombier 		/* (input) slot context */
1057*5c47fe09SDavid du Colombier 		w += 8<<ctlr->csz;
1058*5c47fe09SDavid du Colombier 		w[0] = (w[0] & ~(0x1F<<27)) | slot->nep<<27;
1059*5c47fe09SDavid du Colombier 
1060*5c47fe09SDavid du Colombier 		/* (input) ep context */
1061*5c47fe09SDavid du Colombier 		w += ep->nb*2*8<<ctlr->csz;
1062*5c47fe09SDavid du Colombier 		memset(w, 0, 2*32<<ctlr->csz);
1063*5c47fe09SDavid du Colombier 
1064*5c47fe09SDavid du Colombier 		dmaflush(1, slot->ibase, 32*33 << ctlr->csz);
1065*5c47fe09SDavid du Colombier 		ctlrcmd(ctlr, CR_CONFIGEP | (slot->id<<24), 0, PCIWADDR(slot->ibase), nil);
1066*5c47fe09SDavid du Colombier 		dmaflush(0, slot->obase, 32*32 << ctlr->csz);
1067*5c47fe09SDavid du Colombier 
1068*5c47fe09SDavid du Colombier 		freering(io[OREAD].ring);
1069*5c47fe09SDavid du Colombier 		freering(io[OWRITE].ring);
1070*5c47fe09SDavid du Colombier 	}
1071*5c47fe09SDavid du Colombier 	freeb(io[OREAD].b);
1072*5c47fe09SDavid du Colombier 	freeb(io[OWRITE].b);
1073*5c47fe09SDavid du Colombier 	free(io);
1074*5c47fe09SDavid du Colombier }
1075*5c47fe09SDavid du Colombier 
1076*5c47fe09SDavid du Colombier static void
initepctx(u32int * w,Ring * r,Ep * ep)1077*5c47fe09SDavid du Colombier initepctx(u32int *w, Ring *r, Ep *ep)
1078*5c47fe09SDavid du Colombier {
1079*5c47fe09SDavid du Colombier 	int ival;
1080*5c47fe09SDavid du Colombier 
1081*5c47fe09SDavid du Colombier 	if(ep->dev->speed == Lowspeed || ep->dev->speed == Fullspeed){
1082*5c47fe09SDavid du Colombier 		for(ival=3; ival < 11 && (1<<ival) < ep->pollival; ival++)
1083*5c47fe09SDavid du Colombier 			;
1084*5c47fe09SDavid du Colombier 	} else {
1085*5c47fe09SDavid du Colombier 		for(ival=0; ival < 15 && (1<<ival) < ep->pollival; ival++)
1086*5c47fe09SDavid du Colombier 			;
1087*5c47fe09SDavid du Colombier 	}
1088*5c47fe09SDavid du Colombier 	w[0] = ival<<16;
1089*5c47fe09SDavid du Colombier 	w[1] = ((ep->ttype-Tctl) | (r->id&1)<<2)<<3 | (ep->ntds-1)<<8 | ep->maxpkt<<16;
1090*5c47fe09SDavid du Colombier 	if(ep->ttype != Tiso)
1091*5c47fe09SDavid du Colombier 		w[1] |= 3<<1;
1092*5c47fe09SDavid du Colombier 	*((u64int*)&w[2]) = PCIWADDR(r->base) | 1;
1093*5c47fe09SDavid du Colombier 	w[4] = 2*ep->maxpkt;
1094*5c47fe09SDavid du Colombier 	if(ep->ttype == Tintr || ep->ttype == Tiso)
1095*5c47fe09SDavid du Colombier 		w[4] |= (ep->maxpkt*ep->ntds)<<16;
1096*5c47fe09SDavid du Colombier }
1097*5c47fe09SDavid du Colombier 
1098*5c47fe09SDavid du Colombier static void
initisoio(Epio * io,Ep * ep)1099*5c47fe09SDavid du Colombier initisoio(Epio *io, Ep *ep)
1100*5c47fe09SDavid du Colombier {
1101*5c47fe09SDavid du Colombier 	if(io->ring == nil)
1102*5c47fe09SDavid du Colombier 		return;
1103*5c47fe09SDavid du Colombier 	io->frame = 0;
1104*5c47fe09SDavid du Colombier 	io->period = ep->pollival<<3*(ep->dev->speed == Fullspeed);
1105*5c47fe09SDavid du Colombier 	io->incr = (ep->hz*io->period<<8)/8000;
1106*5c47fe09SDavid du Colombier 	io->tdsz = (io->incr+255>>8)*ep->samplesz;
1107*5c47fe09SDavid du Colombier 	io->b = allocb((io->ring->mask+1)*io->tdsz);
1108*5c47fe09SDavid du Colombier }
1109*5c47fe09SDavid du Colombier 
1110*5c47fe09SDavid du Colombier static void
initep(Ep * ep)1111*5c47fe09SDavid du Colombier initep(Ep *ep)
1112*5c47fe09SDavid du Colombier {
1113*5c47fe09SDavid du Colombier 	Epio *io;
1114*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
1115*5c47fe09SDavid du Colombier 	Slot *slot;
1116*5c47fe09SDavid du Colombier 	Ring *ring;
1117*5c47fe09SDavid du Colombier 	u32int *w;
1118*5c47fe09SDavid du Colombier 	char *err;
1119*5c47fe09SDavid du Colombier 
1120*5c47fe09SDavid du Colombier 	io = ep->aux;
1121*5c47fe09SDavid du Colombier 	ctlr = ep->hp->aux;
1122*5c47fe09SDavid du Colombier 	slot = ep->dev->aux;
1123*5c47fe09SDavid du Colombier 
1124*5c47fe09SDavid du Colombier 	io[OREAD].ring = io[OWRITE].ring = nil;
1125*5c47fe09SDavid du Colombier 	if(ep->nb == 0){
1126*5c47fe09SDavid du Colombier 		io[OWRITE].ring = &slot->epr[0];
1127*5c47fe09SDavid du Colombier 		return;
1128*5c47fe09SDavid du Colombier 	}
1129*5c47fe09SDavid du Colombier 
1130*5c47fe09SDavid du Colombier 	/* (input) control context */
1131*5c47fe09SDavid du Colombier 	w = slot->ibase;
1132*5c47fe09SDavid du Colombier 	memset(w, 0, 32<<ctlr->csz);
1133*5c47fe09SDavid du Colombier 	w[1] = 1;
1134*5c47fe09SDavid du Colombier 	w[31] = slot->altc<<16 | slot->iface<<8 | slot->confval;
1135*5c47fe09SDavid du Colombier 
1136*5c47fe09SDavid du Colombier 	if(waserror()){
1137*5c47fe09SDavid du Colombier 		freering(io[OWRITE].ring), io[OWRITE].ring = nil;
1138*5c47fe09SDavid du Colombier 		freering(io[OREAD].ring), io[OREAD].ring = nil;
1139*5c47fe09SDavid du Colombier 		nexterror();
1140*5c47fe09SDavid du Colombier 	}
1141*5c47fe09SDavid du Colombier 	if(ep->mode != OREAD){
1142*5c47fe09SDavid du Colombier 		ring = initring(io[OWRITE].ring = &slot->epr[ep->nb*2-1], 8);
1143*5c47fe09SDavid du Colombier 		ring->id = ep->nb*2;
1144*5c47fe09SDavid du Colombier 		if(ring->id > slot->nep)
1145*5c47fe09SDavid du Colombier 			slot->nep = ring->id;
1146*5c47fe09SDavid du Colombier 		ring->slot = slot;
1147*5c47fe09SDavid du Colombier 		ring->doorbell = &ctlr->dba[slot->id];
1148*5c47fe09SDavid du Colombier 		ring->ctx = &slot->obase[ring->id*8<<ctlr->csz];
1149*5c47fe09SDavid du Colombier 		w[1] |= 1 << ring->id;
1150*5c47fe09SDavid du Colombier 	}
1151*5c47fe09SDavid du Colombier 	if(ep->mode != OWRITE){
1152*5c47fe09SDavid du Colombier 		ring = initring(io[OREAD].ring = &slot->epr[ep->nb*2], 8);
1153*5c47fe09SDavid du Colombier 		ring->id = ep->nb*2+1;
1154*5c47fe09SDavid du Colombier 		if(ring->id > slot->nep)
1155*5c47fe09SDavid du Colombier 			slot->nep = ring->id;
1156*5c47fe09SDavid du Colombier 		ring->slot = slot;
1157*5c47fe09SDavid du Colombier 		ring->doorbell = &ctlr->dba[slot->id];
1158*5c47fe09SDavid du Colombier 		ring->ctx = &slot->obase[ring->id*8<<ctlr->csz];
1159*5c47fe09SDavid du Colombier 		w[1] |= 1 << ring->id;
1160*5c47fe09SDavid du Colombier 	}
1161*5c47fe09SDavid du Colombier 
1162*5c47fe09SDavid du Colombier 	/* (input) slot context */
1163*5c47fe09SDavid du Colombier 	w += 8<<ctlr->csz;
1164*5c47fe09SDavid du Colombier 	w[0] = (w[0] & ~(0x1F<<27)) | slot->nep<<27;
1165*5c47fe09SDavid du Colombier 	if(!ep->dev->ishub)
1166*5c47fe09SDavid du Colombier 		w[0] &= ~(1<<25);	// MTT
1167*5c47fe09SDavid du Colombier 
1168*5c47fe09SDavid du Colombier 	/* (input) ep context */
1169*5c47fe09SDavid du Colombier 	w += ep->nb*2*8<<ctlr->csz;
1170*5c47fe09SDavid du Colombier 	if(io[OWRITE].ring != nil){
1171*5c47fe09SDavid du Colombier 		memset(w, 0, 5*4);
1172*5c47fe09SDavid du Colombier 		initepctx(w, io[OWRITE].ring, ep);
1173*5c47fe09SDavid du Colombier 	}
1174*5c47fe09SDavid du Colombier 
1175*5c47fe09SDavid du Colombier 	w += 8<<ctlr->csz;
1176*5c47fe09SDavid du Colombier 	if(io[OREAD].ring != nil){
1177*5c47fe09SDavid du Colombier 		memset(w, 0, 5*4);
1178*5c47fe09SDavid du Colombier 		initepctx(w, io[OREAD].ring, ep);
1179*5c47fe09SDavid du Colombier 	}
1180*5c47fe09SDavid du Colombier 
1181*5c47fe09SDavid du Colombier 	dmaflush(1, slot->ibase, 32*33 << ctlr->csz);
1182*5c47fe09SDavid du Colombier 	err = ctlrcmd(ctlr, CR_CONFIGEP | (slot->id<<24), 0, PCIWADDR(slot->ibase), nil);
1183*5c47fe09SDavid du Colombier 	dmaflush(0, slot->obase, 32*32 << ctlr->csz);
1184*5c47fe09SDavid du Colombier 	if(err != nil)
1185*5c47fe09SDavid du Colombier 		error(err);
1186*5c47fe09SDavid du Colombier 
1187*5c47fe09SDavid du Colombier 	if(ep->ttype == Tiso){
1188*5c47fe09SDavid du Colombier 		initisoio(io+OWRITE, ep);
1189*5c47fe09SDavid du Colombier 		initisoio(io+OREAD, ep);
1190*5c47fe09SDavid du Colombier 	}
1191*5c47fe09SDavid du Colombier 	poperror();
1192*5c47fe09SDavid du Colombier }
1193*5c47fe09SDavid du Colombier 
1194*5c47fe09SDavid du Colombier static int
speedid(int speed)1195*5c47fe09SDavid du Colombier speedid(int speed)
1196*5c47fe09SDavid du Colombier {
1197*5c47fe09SDavid du Colombier 	switch(speed){
1198*5c47fe09SDavid du Colombier 	case Fullspeed:		return 1;
1199*5c47fe09SDavid du Colombier 	case Lowspeed:		return 2;
1200*5c47fe09SDavid du Colombier 	case Highspeed:		return 3;
1201*5c47fe09SDavid du Colombier 	case Superspeed:	return 4;
1202*5c47fe09SDavid du Colombier 	}
1203*5c47fe09SDavid du Colombier 	return 0;
1204*5c47fe09SDavid du Colombier }
1205*5c47fe09SDavid du Colombier 
1206*5c47fe09SDavid du Colombier static void
epopen(Ep * ep)1207*5c47fe09SDavid du Colombier epopen(Ep *ep)
1208*5c47fe09SDavid du Colombier {
1209*5c47fe09SDavid du Colombier 	Ctlr *ctlr = ep->hp->aux;
1210*5c47fe09SDavid du Colombier 	Slot *slot, *hub;
1211*5c47fe09SDavid du Colombier 	Ring *ring;
1212*5c47fe09SDavid du Colombier 	Epio *io;
1213*5c47fe09SDavid du Colombier 	Udev *dev;
1214*5c47fe09SDavid du Colombier 	char *err;
1215*5c47fe09SDavid du Colombier 	u32int *w;
1216*5c47fe09SDavid du Colombier 	int i;
1217*5c47fe09SDavid du Colombier 
1218*5c47fe09SDavid du Colombier 	if(ep->dev->isroot)
1219*5c47fe09SDavid du Colombier 		return;
1220*5c47fe09SDavid du Colombier 	if(needrecover(ctlr))
1221*5c47fe09SDavid du Colombier 		error(Erecover);
1222*5c47fe09SDavid du Colombier 	io = malloc(sizeof(Epio)*2);
1223*5c47fe09SDavid du Colombier 	if(io == nil)
1224*5c47fe09SDavid du Colombier 		error(Enomem);
1225*5c47fe09SDavid du Colombier 	ep->aux = io;
1226*5c47fe09SDavid du Colombier 	if(waserror()){
1227*5c47fe09SDavid du Colombier 		epclose(ep);
1228*5c47fe09SDavid du Colombier 		nexterror();
1229*5c47fe09SDavid du Colombier 	}
1230*5c47fe09SDavid du Colombier 	dev = ep->dev;
1231*5c47fe09SDavid du Colombier 	slot = dev->aux;
1232*5c47fe09SDavid du Colombier 	if(slot != nil && slot->dev == dev){
1233*5c47fe09SDavid du Colombier 		initep(ep);
1234*5c47fe09SDavid du Colombier 		poperror();
1235*5c47fe09SDavid du Colombier 		return;
1236*5c47fe09SDavid du Colombier 	}
1237*5c47fe09SDavid du Colombier 
1238*5c47fe09SDavid du Colombier 	/* first open has to be control endpoint */
1239*5c47fe09SDavid du Colombier 	if(ep->nb != 0)
1240*5c47fe09SDavid du Colombier 		error(Egreg);
1241*5c47fe09SDavid du Colombier 
1242*5c47fe09SDavid du Colombier 	slot = allocslot(ctlr, dev);
1243*5c47fe09SDavid du Colombier 	if(waserror()){
1244*5c47fe09SDavid du Colombier 		freeslot(slot);
1245*5c47fe09SDavid du Colombier 		nexterror();
1246*5c47fe09SDavid du Colombier 	}
1247*5c47fe09SDavid du Colombier 
1248*5c47fe09SDavid du Colombier 	/* allocate control ep 0 ring */
1249*5c47fe09SDavid du Colombier 	ring = initring(io[OWRITE].ring = &slot->epr[0], 4);
1250*5c47fe09SDavid du Colombier 	ring->id = 1;
1251*5c47fe09SDavid du Colombier 	slot->nep = 1;
1252*5c47fe09SDavid du Colombier 	ring->slot = slot;
1253*5c47fe09SDavid du Colombier 	ring->doorbell = &ctlr->dba[slot->id];
1254*5c47fe09SDavid du Colombier 	ring->ctx = &slot->obase[8];
1255*5c47fe09SDavid du Colombier 
1256*5c47fe09SDavid du Colombier 	/* (input) control context */
1257*5c47fe09SDavid du Colombier 	w = slot->ibase;
1258*5c47fe09SDavid du Colombier 	memset(w, 0, 3*32<<ctlr->csz);
1259*5c47fe09SDavid du Colombier 	w[1] = 3;	/* A0, A1 */
1260*5c47fe09SDavid du Colombier 
1261*5c47fe09SDavid du Colombier 	/* (input) slot context */
1262*5c47fe09SDavid du Colombier 	w += 8<<ctlr->csz;
1263*5c47fe09SDavid du Colombier 	w[2] = w[3] = 0;
1264*5c47fe09SDavid du Colombier 	w[0] = dev->routestr | speedid(dev->speed)<<20 |
1265*5c47fe09SDavid du Colombier 		(dev->speed == Highspeed && dev->ishub != 0)<<25 |	// MTT
1266*5c47fe09SDavid du Colombier 		(dev->ishub != 0)<<26 | slot->nep<<27;
1267*5c47fe09SDavid du Colombier 	w[1] = dev->rootport<<16;
1268*5c47fe09SDavid du Colombier 
1269*5c47fe09SDavid du Colombier 	/* find the parent hub that this device is conected to */
1270*5c47fe09SDavid du Colombier 	qlock(&ctlr->slotlock);
1271*5c47fe09SDavid du Colombier 	for(i=1; i<=ctlr->nslots; i++){
1272*5c47fe09SDavid du Colombier 		hub = ctlr->slot[i];
1273*5c47fe09SDavid du Colombier 		if(hub == nil || hub->dev == nil || hub->dev->aux != hub)
1274*5c47fe09SDavid du Colombier 			continue;
1275*5c47fe09SDavid du Colombier 		if(hub == slot || hub->dev == dev)
1276*5c47fe09SDavid du Colombier 			continue;
1277*5c47fe09SDavid du Colombier 		if(!hub->dev->ishub)
1278*5c47fe09SDavid du Colombier 			continue;
1279*5c47fe09SDavid du Colombier 		if(hub->dev->addr != dev->hub)
1280*5c47fe09SDavid du Colombier 			continue;
1281*5c47fe09SDavid du Colombier 		if(hub->dev->rootport != dev->rootport)
1282*5c47fe09SDavid du Colombier 			continue;
1283*5c47fe09SDavid du Colombier 
1284*5c47fe09SDavid du Colombier 		if(dev->speed < Highspeed && hub->dev->speed == Highspeed){
1285*5c47fe09SDavid du Colombier 			w[0] |= 1<<25;	// MTT
1286*5c47fe09SDavid du Colombier 			w[2] = hub->id | dev->port<<8;
1287*5c47fe09SDavid du Colombier 		}
1288*5c47fe09SDavid du Colombier 		break;
1289*5c47fe09SDavid du Colombier 	}
1290*5c47fe09SDavid du Colombier 	qunlock(&ctlr->slotlock);
1291*5c47fe09SDavid du Colombier 
1292*5c47fe09SDavid du Colombier 	/* (input) ep context 0 */
1293*5c47fe09SDavid du Colombier 	w += 8<<ctlr->csz;
1294*5c47fe09SDavid du Colombier 	initepctx(w, io[OWRITE].ring, ep);
1295*5c47fe09SDavid du Colombier 
1296*5c47fe09SDavid du Colombier 	dmaflush(1, slot->ibase, 32*33 << ctlr->csz);
1297*5c47fe09SDavid du Colombier 	err = ctlrcmd(ctlr, CR_ADDRESSDEV | (slot->id<<24), 0, PCIWADDR(slot->ibase), nil);
1298*5c47fe09SDavid du Colombier 	dmaflush(0, slot->obase, 32*32 << ctlr->csz);
1299*5c47fe09SDavid du Colombier 	if(err != nil)
1300*5c47fe09SDavid du Colombier 		error(err);
1301*5c47fe09SDavid du Colombier 
1302*5c47fe09SDavid du Colombier 	/* (output) slot context */
1303*5c47fe09SDavid du Colombier 	w = slot->obase;
1304*5c47fe09SDavid du Colombier 
1305*5c47fe09SDavid du Colombier 	dev->addr = w[3] & 0xFF;
1306*5c47fe09SDavid du Colombier 
1307*5c47fe09SDavid du Colombier 	dev->aux = slot;
1308*5c47fe09SDavid du Colombier 	dev->free = freeslot;
1309*5c47fe09SDavid du Colombier 
1310*5c47fe09SDavid du Colombier 	poperror();
1311*5c47fe09SDavid du Colombier 	poperror();
1312*5c47fe09SDavid du Colombier }
1313*5c47fe09SDavid du Colombier 
1314*5c47fe09SDavid du Colombier static long
isoread(Ep *,uchar *,long)1315*5c47fe09SDavid du Colombier isoread(Ep *, uchar *, long)
1316*5c47fe09SDavid du Colombier {
1317*5c47fe09SDavid du Colombier 	error(Egreg);
1318*5c47fe09SDavid du Colombier 	return 0;
1319*5c47fe09SDavid du Colombier }
1320*5c47fe09SDavid du Colombier 
1321*5c47fe09SDavid du Colombier static long
isowrite(Ep * ep,uchar * p,long n)1322*5c47fe09SDavid du Colombier isowrite(Ep *ep, uchar *p, long n)
1323*5c47fe09SDavid du Colombier {
1324*5c47fe09SDavid du Colombier 	uchar *s, *d;
1325*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
1326*5c47fe09SDavid du Colombier 	Epio *io;
1327*5c47fe09SDavid du Colombier 	u32int i, µ;
1328*5c47fe09SDavid du Colombier 	long m;
1329*5c47fe09SDavid du Colombier 
1330*5c47fe09SDavid du Colombier 	s = p;
1331*5c47fe09SDavid du Colombier 	io = (Epio*)ep->aux + OWRITE;
1332*5c47fe09SDavid du Colombier 	qlock(io);
1333*5c47fe09SDavid du Colombier 	if(waserror()){
1334*5c47fe09SDavid du Colombier 		qunlock(io);
1335*5c47fe09SDavid du Colombier 		nexterror();
1336*5c47fe09SDavid du Colombier 	}
1337*5c47fe09SDavid du Colombier 	µ = io->period;
1338*5c47fe09SDavid du Colombier 	ctlr = ep->hp->aux;
1339*5c47fe09SDavid du Colombier 	if(needrecover(ctlr))
1340*5c47fe09SDavid du Colombier 		error(Erecover);
1341*5c47fe09SDavid du Colombier 	for(i = io->frame;; i++){
1342*5c47fe09SDavid du Colombier 		for(;;){
1343*5c47fe09SDavid du Colombier 			m = (int)(io->ring->wp - io->ring->rp);
1344*5c47fe09SDavid du Colombier 			if(m <= 0)
1345*5c47fe09SDavid du Colombier 				i = (80 + µframe(ctlr))/µ;
1346*5c47fe09SDavid du Colombier 			if(m < io->ring->mask)
1347*5c47fe09SDavid du Colombier 				break;
1348*5c47fe09SDavid du Colombier 			*io->ring->doorbell = io->ring->id;
1349*5c47fe09SDavid du Colombier 			tsleep(&up->sleep, return0, nil, 5);
1350*5c47fe09SDavid du Colombier 		}
1351*5c47fe09SDavid du Colombier 		m = ((io->incr + (i*io->incr&255))>>8)*ep->samplesz;
1352*5c47fe09SDavid du Colombier 		d = io->b->rp + (i&io->ring->mask)*io->tdsz;
1353*5c47fe09SDavid du Colombier 		m -= io->nleft, d += io->nleft;
1354*5c47fe09SDavid du Colombier 		if(n < m){
1355*5c47fe09SDavid du Colombier 			memmove(d, p, n);
1356*5c47fe09SDavid du Colombier 			p += n;
1357*5c47fe09SDavid du Colombier 			io->nleft += n;
1358*5c47fe09SDavid du Colombier 			break;
1359*5c47fe09SDavid du Colombier 		}
1360*5c47fe09SDavid du Colombier 		memmove(d, p, m);
1361*5c47fe09SDavid du Colombier 		p += m, n -= m;
1362*5c47fe09SDavid du Colombier 		m += io->nleft, d -= io->nleft;
1363*5c47fe09SDavid du Colombier 		io->nleft = 0;
1364*5c47fe09SDavid du Colombier 		dmaflush(1, d, m);
1365*5c47fe09SDavid du Colombier 		queuetd(io->ring, TR_ISOCH | (i*µ/8 & 0x7ff)<<20 | TR_IOC, m, PCIWADDR(d), nil);
1366*5c47fe09SDavid du Colombier 	}
1367*5c47fe09SDavid du Colombier 	io->frame = i;
1368*5c47fe09SDavid du Colombier 	while(io->ring->rp != io->ring->wp){
1369*5c47fe09SDavid du Colombier 		int d = (int)(i*µ - µframe(ctlr))/8;
1370*5c47fe09SDavid du Colombier 		//d -= ep->sampledelay*1000 / ep->hz;
1371*5c47fe09SDavid du Colombier 		if(d < 5)
1372*5c47fe09SDavid du Colombier 			break;
1373*5c47fe09SDavid du Colombier 		*io->ring->doorbell = io->ring->id;
1374*5c47fe09SDavid du Colombier 		tsleep(&up->sleep, return0, nil, d);
1375*5c47fe09SDavid du Colombier 	}
1376*5c47fe09SDavid du Colombier 	qunlock(io);
1377*5c47fe09SDavid du Colombier 	poperror();
1378*5c47fe09SDavid du Colombier 
1379*5c47fe09SDavid du Colombier 	return p - s;
1380*5c47fe09SDavid du Colombier }
1381*5c47fe09SDavid du Colombier 
1382*5c47fe09SDavid du Colombier static char*
unstall(Ep * ep,Ring * r)1383*5c47fe09SDavid du Colombier unstall(Ep *ep, Ring *r)
1384*5c47fe09SDavid du Colombier {
1385*5c47fe09SDavid du Colombier 	char *err;
1386*5c47fe09SDavid du Colombier 
1387*5c47fe09SDavid du Colombier 	switch(r->ctx[0]&7){
1388*5c47fe09SDavid du Colombier 	case 2:	/* halted */
1389*5c47fe09SDavid du Colombier 	case 4:	/* error */
1390*5c47fe09SDavid du Colombier 		ep->clrhalt = 1;
1391*5c47fe09SDavid du Colombier 	}
1392*5c47fe09SDavid du Colombier 	if(ep->clrhalt){
1393*5c47fe09SDavid du Colombier 		ep->clrhalt = 0;
1394*5c47fe09SDavid du Colombier 		err = ctlrcmd(r->slot->ctlr, CR_RESETEP | (r->id<<16) | (r->slot->id<<24), 0, 0, nil);
1395*5c47fe09SDavid du Colombier 		dmaflush(0, r->ctx, 8*4 << r->slot->ctlr->csz);
1396*5c47fe09SDavid du Colombier 		if(err != nil)
1397*5c47fe09SDavid du Colombier 			return err;
1398*5c47fe09SDavid du Colombier 		r->stopped = 1;
1399*5c47fe09SDavid du Colombier 	}
1400*5c47fe09SDavid du Colombier 	if(r->stopped){
1401*5c47fe09SDavid du Colombier 		err = ctlrcmd(r->slot->ctlr, CR_SETTRDQP | (r->id<<16) | (r->slot->id<<24), 0, resetring(r), nil);
1402*5c47fe09SDavid du Colombier 		dmaflush(0, r->ctx, 8*4 << r->slot->ctlr->csz);
1403*5c47fe09SDavid du Colombier 		if(err != nil)
1404*5c47fe09SDavid du Colombier 			return err;
1405*5c47fe09SDavid du Colombier 		r->stopped = 0;
1406*5c47fe09SDavid du Colombier 	}
1407*5c47fe09SDavid du Colombier 	if(r->wp - r->rp >= r->mask)
1408*5c47fe09SDavid du Colombier 		return "Ring Full";
1409*5c47fe09SDavid du Colombier 	return nil;
1410*5c47fe09SDavid du Colombier }
1411*5c47fe09SDavid du Colombier 
1412*5c47fe09SDavid du Colombier static long
epread(Ep * ep,void * va,long n)1413*5c47fe09SDavid du Colombier epread(Ep *ep, void *va, long n)
1414*5c47fe09SDavid du Colombier {
1415*5c47fe09SDavid du Colombier 	Epio *io;
1416*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
1417*5c47fe09SDavid du Colombier 	uchar *p;
1418*5c47fe09SDavid du Colombier 	char *err;
1419*5c47fe09SDavid du Colombier 	Wait w[1];
1420*5c47fe09SDavid du Colombier 
1421*5c47fe09SDavid du Colombier 	if(ep->dev->isroot)
1422*5c47fe09SDavid du Colombier 		error(Egreg);
1423*5c47fe09SDavid du Colombier 
1424*5c47fe09SDavid du Colombier 	p = va;
1425*5c47fe09SDavid du Colombier 	if(ep->ttype == Tctl){
1426*5c47fe09SDavid du Colombier 		io = (Epio*)ep->aux + OREAD;
1427*5c47fe09SDavid du Colombier 		qlock(io);
1428*5c47fe09SDavid du Colombier 		if(io->b == nil || BLEN(io->b) == 0){
1429*5c47fe09SDavid du Colombier 			qunlock(io);
1430*5c47fe09SDavid du Colombier 			return 0;
1431*5c47fe09SDavid du Colombier 		}
1432*5c47fe09SDavid du Colombier 		if(n > BLEN(io->b))
1433*5c47fe09SDavid du Colombier 			n = BLEN(io->b);
1434*5c47fe09SDavid du Colombier 		memmove(p, io->b->rp, n);
1435*5c47fe09SDavid du Colombier 		io->b->rp += n;
1436*5c47fe09SDavid du Colombier 		qunlock(io);
1437*5c47fe09SDavid du Colombier 		return n;
1438*5c47fe09SDavid du Colombier 	} else if(ep->ttype == Tiso)
1439*5c47fe09SDavid du Colombier 		return isoread(ep, p, n);
1440*5c47fe09SDavid du Colombier 
1441*5c47fe09SDavid du Colombier 	if((uintptr)p <= KZERO){
1442*5c47fe09SDavid du Colombier 		Block *b;
1443*5c47fe09SDavid du Colombier 
1444*5c47fe09SDavid du Colombier 		b = allocb(n);
1445*5c47fe09SDavid du Colombier 		if(waserror()){
1446*5c47fe09SDavid du Colombier 			freeb(b);
1447*5c47fe09SDavid du Colombier 			nexterror();
1448*5c47fe09SDavid du Colombier 		}
1449*5c47fe09SDavid du Colombier 		n = epread(ep, b->rp, n);
1450*5c47fe09SDavid du Colombier 		memmove(p, b->rp, n);
1451*5c47fe09SDavid du Colombier 		freeb(b);
1452*5c47fe09SDavid du Colombier 		poperror();
1453*5c47fe09SDavid du Colombier 		return n;
1454*5c47fe09SDavid du Colombier 	}
1455*5c47fe09SDavid du Colombier 
1456*5c47fe09SDavid du Colombier 	ctlr = (Ctlr*)ep->hp->aux;
1457*5c47fe09SDavid du Colombier 	io = (Epio*)ep->aux + OREAD;
1458*5c47fe09SDavid du Colombier 	qlock(io);
1459*5c47fe09SDavid du Colombier 	if(waserror()){
1460*5c47fe09SDavid du Colombier 		dmaflush(0, io->ring->ctx, 8*4 << ctlr->csz);
1461*5c47fe09SDavid du Colombier 		qunlock(io);
1462*5c47fe09SDavid du Colombier 		nexterror();
1463*5c47fe09SDavid du Colombier 	}
1464*5c47fe09SDavid du Colombier 
1465*5c47fe09SDavid du Colombier 	if((err = unstall(ep, io->ring)) != nil)
1466*5c47fe09SDavid du Colombier 		error(err);
1467*5c47fe09SDavid du Colombier 
1468*5c47fe09SDavid du Colombier 	dmaflush(1, p, n);
1469*5c47fe09SDavid du Colombier 	queuetd(io->ring, TR_NORMAL | TR_IOC, n, PCIWADDR(p), w);
1470*5c47fe09SDavid du Colombier 	err = waittd(ctlr, w, ep->tmout);
1471*5c47fe09SDavid du Colombier 	dmaflush(0, p, n);
1472*5c47fe09SDavid du Colombier 	if(err != nil)
1473*5c47fe09SDavid du Colombier 		error(err);
1474*5c47fe09SDavid du Colombier 
1475*5c47fe09SDavid du Colombier 	qunlock(io);
1476*5c47fe09SDavid du Colombier 	poperror();
1477*5c47fe09SDavid du Colombier 
1478*5c47fe09SDavid du Colombier 	n -= (w->er[2] & 0xFFFFFF);
1479*5c47fe09SDavid du Colombier 	if(n < 0)
1480*5c47fe09SDavid du Colombier 		n = 0;
1481*5c47fe09SDavid du Colombier 
1482*5c47fe09SDavid du Colombier 	return n;
1483*5c47fe09SDavid du Colombier }
1484*5c47fe09SDavid du Colombier 
1485*5c47fe09SDavid du Colombier static long
epwrite(Ep * ep,void * va,long n)1486*5c47fe09SDavid du Colombier epwrite(Ep *ep, void *va, long n)
1487*5c47fe09SDavid du Colombier {
1488*5c47fe09SDavid du Colombier 	Wait w[3];
1489*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
1490*5c47fe09SDavid du Colombier 	Epio *io;
1491*5c47fe09SDavid du Colombier 	uchar *p;
1492*5c47fe09SDavid du Colombier 	char *err;
1493*5c47fe09SDavid du Colombier 
1494*5c47fe09SDavid du Colombier 	if(ep->dev->isroot)
1495*5c47fe09SDavid du Colombier 		error(Egreg);
1496*5c47fe09SDavid du Colombier 
1497*5c47fe09SDavid du Colombier 	p = va;
1498*5c47fe09SDavid du Colombier 	if(ep->ttype == Tctl){
1499*5c47fe09SDavid du Colombier 		int dir, len;
1500*5c47fe09SDavid du Colombier 		Ring *ring;
1501*5c47fe09SDavid du Colombier 		Slot *slot;
1502*5c47fe09SDavid du Colombier 
1503*5c47fe09SDavid du Colombier 		if(n < 8)
1504*5c47fe09SDavid du Colombier 			error(Eshort);
1505*5c47fe09SDavid du Colombier 
1506*5c47fe09SDavid du Colombier 		if(p[0] == 0x00 && p[1] == 0x05)
1507*5c47fe09SDavid du Colombier 			return n;
1508*5c47fe09SDavid du Colombier 
1509*5c47fe09SDavid du Colombier 		ctlr = (Ctlr*)ep->hp->aux;
1510*5c47fe09SDavid du Colombier 		io = (Epio*)ep->aux + OREAD;
1511*5c47fe09SDavid du Colombier 		ring = io[OWRITE-OREAD].ring;
1512*5c47fe09SDavid du Colombier 		slot = ring->slot;
1513*5c47fe09SDavid du Colombier 		qlock(io);
1514*5c47fe09SDavid du Colombier 		if(waserror()){
1515*5c47fe09SDavid du Colombier 			ilock(ring);
1516*5c47fe09SDavid du Colombier 			ring->pending = nil;
1517*5c47fe09SDavid du Colombier 			iunlock(ring);
1518*5c47fe09SDavid du Colombier 			dmaflush(0, ring->ctx, 8*4 << ctlr->csz);
1519*5c47fe09SDavid du Colombier 			qunlock(io);
1520*5c47fe09SDavid du Colombier 			nexterror();
1521*5c47fe09SDavid du Colombier 		}
1522*5c47fe09SDavid du Colombier 		if(io->b != nil){
1523*5c47fe09SDavid du Colombier 			freeb(io->b);
1524*5c47fe09SDavid du Colombier 			io->b = nil;
1525*5c47fe09SDavid du Colombier 		}
1526*5c47fe09SDavid du Colombier 		len = GET2(&p[6]);
1527*5c47fe09SDavid du Colombier 		dir = (p[0] & Rd2h) != 0;
1528*5c47fe09SDavid du Colombier 		if(len > 0){
1529*5c47fe09SDavid du Colombier 			io->b = allocb(len);
1530*5c47fe09SDavid du Colombier 			if(dir == 0){	/* out */
1531*5c47fe09SDavid du Colombier 				assert(len >= n-8);
1532*5c47fe09SDavid du Colombier 				memmove(io->b->wp, p+8, n-8);
1533*5c47fe09SDavid du Colombier 			} else {
1534*5c47fe09SDavid du Colombier 				memset(io->b->wp, 0, len);
1535*5c47fe09SDavid du Colombier 				io->b->wp += len;
1536*5c47fe09SDavid du Colombier 			}
1537*5c47fe09SDavid du Colombier 		}
1538*5c47fe09SDavid du Colombier 		if((err = unstall(ep, ring)) != nil)
1539*5c47fe09SDavid du Colombier 			error(err);
1540*5c47fe09SDavid du Colombier 
1541*5c47fe09SDavid du Colombier 		if((ring->ctx[1]>>16) != ep->maxpkt){
1542*5c47fe09SDavid du Colombier 			u32int *w = slot->ibase;
1543*5c47fe09SDavid du Colombier 			w[0] = 0;
1544*5c47fe09SDavid du Colombier 			w[1] = 1<<ring->id;
1545*5c47fe09SDavid du Colombier 			w += (ring->id+1)*8<<ctlr->csz;
1546*5c47fe09SDavid du Colombier 			initepctx(w, ring, ep);
1547*5c47fe09SDavid du Colombier 			dmaflush(1, slot->ibase, 32*33 << ctlr->csz);
1548*5c47fe09SDavid du Colombier 			err = ctlrcmd(ctlr, CR_EVALCTX | (slot->id<<24), 0, PCIWADDR(slot->ibase), nil);
1549*5c47fe09SDavid du Colombier 			dmaflush(0, slot->obase, 32*32 << ctlr->csz);
1550*5c47fe09SDavid du Colombier 			if(err != nil)
1551*5c47fe09SDavid du Colombier 				error(err);
1552*5c47fe09SDavid du Colombier 		}
1553*5c47fe09SDavid du Colombier 
1554*5c47fe09SDavid du Colombier 		queuetd(ring, TR_SETUPSTAGE | (len > 0 ? 2+dir : 0)<<16 | TR_IDT | TR_IOC, 8,
1555*5c47fe09SDavid du Colombier 			p[0] | p[1]<<8 | GET2(&p[2])<<16 |
1556*5c47fe09SDavid du Colombier 			(u64int)(GET2(&p[4]) | len<<16)<<32, &w[0]);
1557*5c47fe09SDavid du Colombier 		if(len > 0){
1558*5c47fe09SDavid du Colombier 			dmaflush(1, io->b->rp, len);
1559*5c47fe09SDavid du Colombier 			queuetd(ring, TR_DATASTAGE | dir<<16 | TR_IOC, len,
1560*5c47fe09SDavid du Colombier 				PCIWADDR(io->b->rp), &w[1]);
1561*5c47fe09SDavid du Colombier 		}
1562*5c47fe09SDavid du Colombier 		queuetd(ring, TR_STATUSSTAGE | (len == 0 || !dir)<<16 | TR_IOC, 0, 0, &w[2]);
1563*5c47fe09SDavid du Colombier 
1564*5c47fe09SDavid du Colombier 		if((err = waittd(ctlr, &w[0], ep->tmout)) != nil)
1565*5c47fe09SDavid du Colombier 			error(err);
1566*5c47fe09SDavid du Colombier 		if(len > 0){
1567*5c47fe09SDavid du Colombier 			if((err = waittd(ctlr, &w[1], ep->tmout)) != nil)
1568*5c47fe09SDavid du Colombier 				error(err);
1569*5c47fe09SDavid du Colombier 			if(dir != 0){
1570*5c47fe09SDavid du Colombier 				dmaflush(0, io->b->rp, len);
1571*5c47fe09SDavid du Colombier 				io->b->wp -= (w[1].er[2] & 0xFFFFFF);
1572*5c47fe09SDavid du Colombier 				if(io->b->wp < io->b->rp)
1573*5c47fe09SDavid du Colombier 					io->b->wp = io->b->rp;
1574*5c47fe09SDavid du Colombier 			}
1575*5c47fe09SDavid du Colombier 		}
1576*5c47fe09SDavid du Colombier 		if((err = waittd(ctlr, &w[2], ep->tmout)) != nil)
1577*5c47fe09SDavid du Colombier 			error(err);
1578*5c47fe09SDavid du Colombier 
1579*5c47fe09SDavid du Colombier 		if(p[0] == 0x00 && p[1] == 0x09){
1580*5c47fe09SDavid du Colombier 			slot->confval = GET2(&p[2]);
1581*5c47fe09SDavid du Colombier 		} else if(p[0] == 0x01 && p[1] == 0x0d){
1582*5c47fe09SDavid du Colombier 			slot->altc = GET2(&p[2]);
1583*5c47fe09SDavid du Colombier 			slot->iface = GET2(&p[4]);
1584*5c47fe09SDavid du Colombier 		}
1585*5c47fe09SDavid du Colombier 
1586*5c47fe09SDavid du Colombier 		qunlock(io);
1587*5c47fe09SDavid du Colombier 		poperror();
1588*5c47fe09SDavid du Colombier 
1589*5c47fe09SDavid du Colombier 		return n;
1590*5c47fe09SDavid du Colombier 	} else if(ep->ttype == Tiso)
1591*5c47fe09SDavid du Colombier 		return isowrite(ep, p, n);
1592*5c47fe09SDavid du Colombier 
1593*5c47fe09SDavid du Colombier 	if((uintptr)p <= KZERO){
1594*5c47fe09SDavid du Colombier 		Block *b;
1595*5c47fe09SDavid du Colombier 
1596*5c47fe09SDavid du Colombier 		b = allocb(n);
1597*5c47fe09SDavid du Colombier 		if(waserror()){
1598*5c47fe09SDavid du Colombier 			freeb(b);
1599*5c47fe09SDavid du Colombier 			nexterror();
1600*5c47fe09SDavid du Colombier 		}
1601*5c47fe09SDavid du Colombier 		memmove(b->wp, p, n);
1602*5c47fe09SDavid du Colombier 		n = epwrite(ep, b->wp, n);
1603*5c47fe09SDavid du Colombier 		freeb(b);
1604*5c47fe09SDavid du Colombier 		poperror();
1605*5c47fe09SDavid du Colombier 		return n;
1606*5c47fe09SDavid du Colombier 	}
1607*5c47fe09SDavid du Colombier 
1608*5c47fe09SDavid du Colombier 	ctlr = (Ctlr*)ep->hp->aux;
1609*5c47fe09SDavid du Colombier 	io = (Epio*)ep->aux + OWRITE;
1610*5c47fe09SDavid du Colombier 	qlock(io);
1611*5c47fe09SDavid du Colombier 	if(waserror()){
1612*5c47fe09SDavid du Colombier 		dmaflush(0, io->ring->ctx, 8*4 << ctlr->csz);
1613*5c47fe09SDavid du Colombier 		qunlock(io);
1614*5c47fe09SDavid du Colombier 		nexterror();
1615*5c47fe09SDavid du Colombier 	}
1616*5c47fe09SDavid du Colombier 
1617*5c47fe09SDavid du Colombier 	if((err = unstall(ep, io->ring)) != nil)
1618*5c47fe09SDavid du Colombier 		error(err);
1619*5c47fe09SDavid du Colombier 
1620*5c47fe09SDavid du Colombier 	dmaflush(1, p, n);
1621*5c47fe09SDavid du Colombier 	queuetd(io->ring, TR_NORMAL | TR_IOC, n, PCIWADDR(p), w);
1622*5c47fe09SDavid du Colombier 	if((err = waittd(ctlr, w, ep->tmout)) != nil)
1623*5c47fe09SDavid du Colombier 		error(err);
1624*5c47fe09SDavid du Colombier 
1625*5c47fe09SDavid du Colombier 	qunlock(io);
1626*5c47fe09SDavid du Colombier 	poperror();
1627*5c47fe09SDavid du Colombier 
1628*5c47fe09SDavid du Colombier 	return n;
1629*5c47fe09SDavid du Colombier }
1630*5c47fe09SDavid du Colombier 
1631*5c47fe09SDavid du Colombier static char*
seprintep(char * s,char *,Ep *)1632*5c47fe09SDavid du Colombier seprintep(char *s, char*, Ep*)
1633*5c47fe09SDavid du Colombier {
1634*5c47fe09SDavid du Colombier 	return s;
1635*5c47fe09SDavid du Colombier }
1636*5c47fe09SDavid du Colombier 
1637*5c47fe09SDavid du Colombier static int
portstatus(Hci * hp,int port)1638*5c47fe09SDavid du Colombier portstatus(Hci *hp, int port)
1639*5c47fe09SDavid du Colombier {
1640*5c47fe09SDavid du Colombier 	Ctlr *ctlr = hp->aux;
1641*5c47fe09SDavid du Colombier 	u32int psc, ps;
1642*5c47fe09SDavid du Colombier 
1643*5c47fe09SDavid du Colombier 	if(ctlr->port == nil || needrecover(ctlr))
1644*5c47fe09SDavid du Colombier 		return 0;
1645*5c47fe09SDavid du Colombier 
1646*5c47fe09SDavid du Colombier 	ps = 0;
1647*5c47fe09SDavid du Colombier 	psc = ctlr->port[port-1].reg[PORTSC];
1648*5c47fe09SDavid du Colombier 	if(psc & CCS)	ps |= HPpresent;
1649*5c47fe09SDavid du Colombier 	if(psc & PED)	ps |= HPenable;
1650*5c47fe09SDavid du Colombier 	if(psc & OCA)	ps |= HPovercurrent;
1651*5c47fe09SDavid du Colombier 	if(psc & PR)	ps |= HPreset;
1652*5c47fe09SDavid du Colombier 
1653*5c47fe09SDavid du Colombier 	if((hp->superspeed & (1<<(port-1))) != 0){
1654*5c47fe09SDavid du Colombier 		ps |= psc & (PLS|PP);
1655*5c47fe09SDavid du Colombier 		if(psc & CSC)	ps |= 1<<0+16;
1656*5c47fe09SDavid du Colombier 		if(psc & OCC)	ps |= 1<<3+16;
1657*5c47fe09SDavid du Colombier 		if(psc & PRC)	ps |= 1<<4+16;
1658*5c47fe09SDavid du Colombier 		if(psc & WRC)	ps |= 1<<5+16;
1659*5c47fe09SDavid du Colombier 		if(psc & PLC)	ps |= 1<<6+16;
1660*5c47fe09SDavid du Colombier 		if(psc & CEC)	ps |= 1<<7+16;
1661*5c47fe09SDavid du Colombier 	} else {
1662*5c47fe09SDavid du Colombier 		if((ps & HPreset) == 0){
1663*5c47fe09SDavid du Colombier 			switch((psc>>10)&15){
1664*5c47fe09SDavid du Colombier 			case 1:
1665*5c47fe09SDavid du Colombier 				/* full speed */
1666*5c47fe09SDavid du Colombier 				break;
1667*5c47fe09SDavid du Colombier 			case 2:
1668*5c47fe09SDavid du Colombier 				ps |= HPslow;
1669*5c47fe09SDavid du Colombier 				break;
1670*5c47fe09SDavid du Colombier 			case 3:
1671*5c47fe09SDavid du Colombier 				ps |= HPhigh;
1672*5c47fe09SDavid du Colombier 				break;
1673*5c47fe09SDavid du Colombier 			}
1674*5c47fe09SDavid du Colombier 		}
1675*5c47fe09SDavid du Colombier 		if(psc & PP)	ps |= HPpower;
1676*5c47fe09SDavid du Colombier 		if(psc & CSC)	ps |= HPstatuschg;
1677*5c47fe09SDavid du Colombier 		if(psc & PRC)	ps |= HPchange;
1678*5c47fe09SDavid du Colombier 	}
1679*5c47fe09SDavid du Colombier 
1680*5c47fe09SDavid du Colombier 	return ps;
1681*5c47fe09SDavid du Colombier }
1682*5c47fe09SDavid du Colombier 
1683*5c47fe09SDavid du Colombier static int
portenable(Hci *,int,int)1684*5c47fe09SDavid du Colombier portenable(Hci*, int, int)
1685*5c47fe09SDavid du Colombier {
1686*5c47fe09SDavid du Colombier 	return 0;
1687*5c47fe09SDavid du Colombier }
1688*5c47fe09SDavid du Colombier 
1689*5c47fe09SDavid du Colombier static int
portreset(Hci * hp,int port,int on)1690*5c47fe09SDavid du Colombier portreset(Hci *hp, int port, int on)
1691*5c47fe09SDavid du Colombier {
1692*5c47fe09SDavid du Colombier 	Ctlr *ctlr = hp->aux;
1693*5c47fe09SDavid du Colombier 
1694*5c47fe09SDavid du Colombier 	if(ctlr->port == nil || needrecover(ctlr))
1695*5c47fe09SDavid du Colombier 		return 0;
1696*5c47fe09SDavid du Colombier 
1697*5c47fe09SDavid du Colombier 	if(on){
1698*5c47fe09SDavid du Colombier 		ctlr->port[port-1].reg[PORTSC] |= PR;
1699*5c47fe09SDavid du Colombier 		tsleep(&up->sleep, return0, nil, 200);
1700*5c47fe09SDavid du Colombier 	}
1701*5c47fe09SDavid du Colombier 	return 0;
1702*5c47fe09SDavid du Colombier }
1703*5c47fe09SDavid du Colombier 
1704*5c47fe09SDavid du Colombier 
1705*5c47fe09SDavid du Colombier static Ctlr *ctlrs[Nhcis];
1706*5c47fe09SDavid du Colombier 
1707*5c47fe09SDavid du Colombier static void
scanpci(void)1708*5c47fe09SDavid du Colombier scanpci(void)
1709*5c47fe09SDavid du Colombier {
1710*5c47fe09SDavid du Colombier 	static int already = 0;
1711*5c47fe09SDavid du Colombier 	int i;
1712*5c47fe09SDavid du Colombier 	uintpci io;
1713*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
1714*5c47fe09SDavid du Colombier 	Pcidev *p;
1715*5c47fe09SDavid du Colombier 	u32int *mmio;
1716*5c47fe09SDavid du Colombier 
1717*5c47fe09SDavid du Colombier 	if(already)
1718*5c47fe09SDavid du Colombier 		return;
1719*5c47fe09SDavid du Colombier 	already = 1;
1720*5c47fe09SDavid du Colombier 	p = nil;
1721*5c47fe09SDavid du Colombier 	while ((p = pcimatch(p, 0, 0)) != nil) {
1722*5c47fe09SDavid du Colombier 		/*
1723*5c47fe09SDavid du Colombier 		 * Find XHCI controllers (Programming Interface = 0x30).
1724*5c47fe09SDavid du Colombier 		 */
1725*5c47fe09SDavid du Colombier 		if(p->ccrb != Pcibcserial || p->ccru != Pciscusb || p->ccrp != 0x30)
1726*5c47fe09SDavid du Colombier 			continue;
1727*5c47fe09SDavid du Colombier 		io = p->mem[0].bar & ~0x0f;
1728*5c47fe09SDavid du Colombier 		if(io == 0)
1729*5c47fe09SDavid du Colombier 			continue;
1730*5c47fe09SDavid du Colombier 		print("usbxhci: %#x %#x: port %llux size %#x irq %d\n",
1731*5c47fe09SDavid du Colombier 			p->vid, p->did, io, p->mem[0].size, p->intl);
1732*5c47fe09SDavid du Colombier 		mmio = (u32int*)mmukmapx(VIRTPCI, io, p->mem[0].size);
1733*5c47fe09SDavid du Colombier 		if(mmio == nil){
1734*5c47fe09SDavid du Colombier 			print("usbxhci: cannot map registers\n");
1735*5c47fe09SDavid du Colombier 			continue;
1736*5c47fe09SDavid du Colombier 		}
1737*5c47fe09SDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
1738*5c47fe09SDavid du Colombier 		if(ctlr == nil){
1739*5c47fe09SDavid du Colombier 			print("usbxhci: no memory\n");
1740*5c47fe09SDavid du Colombier 			continue;
1741*5c47fe09SDavid du Colombier 		}
1742*5c47fe09SDavid du Colombier 		ctlr->base = io;
1743*5c47fe09SDavid du Colombier 		ctlr->active = nil;
1744*5c47fe09SDavid du Colombier 		ctlr->pcidev = p;
1745*5c47fe09SDavid du Colombier 		ctlr->mmio = mmio;
1746*5c47fe09SDavid du Colombier 		for(i = 0; i < nelem(ctlrs); i++)
1747*5c47fe09SDavid du Colombier 			if(ctlrs[i] == nil){
1748*5c47fe09SDavid du Colombier 				ctlrs[i] = ctlr;
1749*5c47fe09SDavid du Colombier 				break;
1750*5c47fe09SDavid du Colombier 			}
1751*5c47fe09SDavid du Colombier 		if(i >= nelem(ctlrs))
1752*5c47fe09SDavid du Colombier 			print("xhci: bug: more than %d controllers\n", nelem(ctlrs));
1753*5c47fe09SDavid du Colombier 	}
1754*5c47fe09SDavid du Colombier }
1755*5c47fe09SDavid du Colombier 
1756*5c47fe09SDavid du Colombier static int
reset(Hci * hp)1757*5c47fe09SDavid du Colombier reset(Hci *hp)
1758*5c47fe09SDavid du Colombier {
1759*5c47fe09SDavid du Colombier 	Ctlr *ctlr;
1760*5c47fe09SDavid du Colombier 	int i;
1761*5c47fe09SDavid du Colombier 
1762*5c47fe09SDavid du Colombier 	if(getconf("*nousbxhci"))
1763*5c47fe09SDavid du Colombier 		return -1;
1764*5c47fe09SDavid du Colombier 
1765*5c47fe09SDavid du Colombier 	scanpci();
1766*5c47fe09SDavid du Colombier 
1767*5c47fe09SDavid du Colombier 	/*
1768*5c47fe09SDavid du Colombier 	 * Any adapter matches if no hp->port is supplied,
1769*5c47fe09SDavid du Colombier 	 * otherwise the ports must match.
1770*5c47fe09SDavid du Colombier 	 */
1771*5c47fe09SDavid du Colombier 	for(i = 0; i < nelem(ctlrs) && ctlrs[i] != nil; i++){
1772*5c47fe09SDavid du Colombier 		ctlr = ctlrs[i];
1773*5c47fe09SDavid du Colombier 		if(ctlr->active == nil)
1774*5c47fe09SDavid du Colombier 		if(hp->port == 0 || hp->port == ctlr->base){
1775*5c47fe09SDavid du Colombier 			ctlr->active = hp;
1776*5c47fe09SDavid du Colombier 			goto Found;
1777*5c47fe09SDavid du Colombier 		}
1778*5c47fe09SDavid du Colombier 	}
1779*5c47fe09SDavid du Colombier 	return -1;
1780*5c47fe09SDavid du Colombier 
1781*5c47fe09SDavid du Colombier Found:
1782*5c47fe09SDavid du Colombier 	hp->aux = ctlr;
1783*5c47fe09SDavid du Colombier 	hp->port = ctlr->base;
1784*5c47fe09SDavid du Colombier 	hp->irq = ctlr->pcidev->intl;
1785*5c47fe09SDavid du Colombier 	hp->tbdf = ctlr->pcidev->tbdf;
1786*5c47fe09SDavid du Colombier 
1787*5c47fe09SDavid du Colombier 	hp->init = init;
1788*5c47fe09SDavid du Colombier 	hp->dump = dump;
1789*5c47fe09SDavid du Colombier 	hp->interrupt = interrupt;
1790*5c47fe09SDavid du Colombier 	hp->epopen = epopen;
1791*5c47fe09SDavid du Colombier 	hp->epclose = epclose;
1792*5c47fe09SDavid du Colombier 	hp->epread = epread;
1793*5c47fe09SDavid du Colombier 	hp->epwrite = epwrite;
1794*5c47fe09SDavid du Colombier 	hp->seprintep = seprintep;
1795*5c47fe09SDavid du Colombier 	hp->portenable = portenable;
1796*5c47fe09SDavid du Colombier 	hp->portreset = portreset;
1797*5c47fe09SDavid du Colombier 	hp->portstatus = portstatus;
1798*5c47fe09SDavid du Colombier 	hp->shutdown = shutdown;
1799*5c47fe09SDavid du Colombier 	hp->debug = setdebug;
1800*5c47fe09SDavid du Colombier 	hp->type = "xhci";
1801*5c47fe09SDavid du Colombier 
1802*5c47fe09SDavid du Colombier 	return 0;
1803*5c47fe09SDavid du Colombier }
1804*5c47fe09SDavid du Colombier 
1805*5c47fe09SDavid du Colombier void
usbxhcilink(void)1806*5c47fe09SDavid du Colombier usbxhcilink(void)
1807*5c47fe09SDavid du Colombier {
1808*5c47fe09SDavid du Colombier 	addhcitype("xhci", reset);
1809*5c47fe09SDavid du Colombier }
1810*5c47fe09SDavid du Colombier 
1811*5c47fe09SDavid du Colombier void
dmaflush(int clean,void * p,ulong len)1812*5c47fe09SDavid du Colombier dmaflush(int clean, void *p, ulong len)
1813*5c47fe09SDavid du Colombier {
1814*5c47fe09SDavid du Colombier 	uintptr s = (uintptr)p;
1815*5c47fe09SDavid du Colombier 	uintptr e = (uintptr)p + len;
1816*5c47fe09SDavid du Colombier 
1817*5c47fe09SDavid du Colombier 	if(clean){
1818*5c47fe09SDavid du Colombier 		s &= ~(BLOCKALIGN-1);
1819*5c47fe09SDavid du Colombier 		e += BLOCKALIGN-1;
1820*5c47fe09SDavid du Colombier 		e &= ~(BLOCKALIGN-1);
1821*5c47fe09SDavid du Colombier 		cachedwbse((void*)s, e - s);
1822*5c47fe09SDavid du Colombier 		return;
1823*5c47fe09SDavid du Colombier 	}
1824*5c47fe09SDavid du Colombier 	if(s & BLOCKALIGN-1){
1825*5c47fe09SDavid du Colombier 		s &= ~(BLOCKALIGN-1);
1826*5c47fe09SDavid du Colombier 		cachedwbinvse((void*)s, BLOCKALIGN);
1827*5c47fe09SDavid du Colombier 		s += BLOCKALIGN;
1828*5c47fe09SDavid du Colombier 	}
1829*5c47fe09SDavid du Colombier 	if(e & BLOCKALIGN-1){
1830*5c47fe09SDavid du Colombier 		e &= ~(BLOCKALIGN-1);
1831*5c47fe09SDavid du Colombier 		if(e < s)
1832*5c47fe09SDavid du Colombier 			return;
1833*5c47fe09SDavid du Colombier 		cachedwbinvse((void*)e, BLOCKALIGN);
1834*5c47fe09SDavid du Colombier 	}
1835*5c47fe09SDavid du Colombier 	if(s < e)
1836*5c47fe09SDavid du Colombier 		cachedinvse((void*)s, e - s);
1837*5c47fe09SDavid du Colombier }
1838