xref: /plan9-contrib/sys/src/9/pc/etheriwl.c (revision 43f728cba48694b0a42c538a38fdf975a0ae5433)
1*43f728cbSDavid du Colombier /*
2*43f728cbSDavid du Colombier  * Intel WiFi Link driver.
3*43f728cbSDavid du Colombier  *
4*43f728cbSDavid du Colombier  * Written without any documentation but Damien Bergaminis
5*43f728cbSDavid du Colombier  * OpenBSD iwn(4) driver sources. Requires intel firmware
6*43f728cbSDavid du Colombier  * to be present in /lib/firmware/iwn-* on attach.
7*43f728cbSDavid du Colombier  */
8*43f728cbSDavid du Colombier 
9*43f728cbSDavid du Colombier #include "u.h"
10*43f728cbSDavid du Colombier #include "../port/lib.h"
11*43f728cbSDavid du Colombier #include "mem.h"
12*43f728cbSDavid du Colombier #include "dat.h"
13*43f728cbSDavid du Colombier #include "fns.h"
14*43f728cbSDavid du Colombier #include "io.h"
15*43f728cbSDavid du Colombier #include "../port/error.h"
16*43f728cbSDavid du Colombier #include "../port/netif.h"
17*43f728cbSDavid du Colombier 
18*43f728cbSDavid du Colombier #include "etherif.h"
19*43f728cbSDavid du Colombier #include "wifi.h"
20*43f728cbSDavid du Colombier 
21*43f728cbSDavid du Colombier enum {
22*43f728cbSDavid du Colombier 	Ntxlog		= 8,
23*43f728cbSDavid du Colombier 	Ntx		= 1<<Ntxlog,
24*43f728cbSDavid du Colombier 	Nrxlog		= 8,
25*43f728cbSDavid du Colombier 	Nrx		= 1<<Nrxlog,
26*43f728cbSDavid du Colombier 
27*43f728cbSDavid du Colombier 	Rstatsize	= 16,
28*43f728cbSDavid du Colombier 	Rbufsize	= 4*1024,
29*43f728cbSDavid du Colombier 	Rdscsize	= 8,
30*43f728cbSDavid du Colombier 
31*43f728cbSDavid du Colombier 	Tbufsize	= 4*1024,
32*43f728cbSDavid du Colombier 	Tdscsize	= 128,
33*43f728cbSDavid du Colombier 	Tcmdsize	= 140,
34*43f728cbSDavid du Colombier };
35*43f728cbSDavid du Colombier 
36*43f728cbSDavid du Colombier /* registers */
37*43f728cbSDavid du Colombier enum {
38*43f728cbSDavid du Colombier 	Cfg		= 0x000,	/* config register */
39*43f728cbSDavid du Colombier 		MacSi		= 1<<8,
40*43f728cbSDavid du Colombier 		RadioSi		= 1<<9,
41*43f728cbSDavid du Colombier 		EepromLocked	= 1<<21,
42*43f728cbSDavid du Colombier 		NicReady	= 1<<22,
43*43f728cbSDavid du Colombier 		HapwakeL1A	= 1<<23,
44*43f728cbSDavid du Colombier 		PrepareDone	= 1<<25,
45*43f728cbSDavid du Colombier 		Prepare		= 1<<27,
46*43f728cbSDavid du Colombier 
47*43f728cbSDavid du Colombier 	Isr		= 0x008,	/* interrupt status */
48*43f728cbSDavid du Colombier 	Imr		= 0x00c,	/* interrupt mask */
49*43f728cbSDavid du Colombier 		Ialive		= 1<<0,
50*43f728cbSDavid du Colombier 		Iwakeup		= 1<<1,
51*43f728cbSDavid du Colombier 		Iswrx		= 1<<3,
52*43f728cbSDavid du Colombier 		Ictreached	= 1<<6,
53*43f728cbSDavid du Colombier 		Irftoggled	= 1<<7,
54*43f728cbSDavid du Colombier 		Iswerr		= 1<<25,
55*43f728cbSDavid du Colombier 		Isched		= 1<<26,
56*43f728cbSDavid du Colombier 		Ifhtx		= 1<<27,
57*43f728cbSDavid du Colombier 		Irxperiodic	= 1<<28,
58*43f728cbSDavid du Colombier 		Ihwerr		= 1<<29,
59*43f728cbSDavid du Colombier 		Ifhrx		= 1<<31,
60*43f728cbSDavid du Colombier 
61*43f728cbSDavid du Colombier 		Ierr		= Iswerr | Ihwerr,
62*43f728cbSDavid du Colombier 		Idefmask	= Ierr | Ifhtx | Ifhrx | Ialive | Iwakeup | Iswrx | Ictreached | Irftoggled,
63*43f728cbSDavid du Colombier 
64*43f728cbSDavid du Colombier 	FhIsr		= 0x010,	/* second interrupt status */
65*43f728cbSDavid du Colombier 
66*43f728cbSDavid du Colombier 	Reset		= 0x020,
67*43f728cbSDavid du Colombier 
68*43f728cbSDavid du Colombier 	Rev		= 0x028,	/* hardware revision */
69*43f728cbSDavid du Colombier 
70*43f728cbSDavid du Colombier 	EepromIo	= 0x02c,	/* EEPROM i/o register */
71*43f728cbSDavid du Colombier 	EepromGp	= 0x030,
72*43f728cbSDavid du Colombier 
73*43f728cbSDavid du Colombier 	OtpromGp	= 0x034,
74*43f728cbSDavid du Colombier 		DevSelOtp	= 1<<16,
75*43f728cbSDavid du Colombier 		RelativeAccess	= 1<<17,
76*43f728cbSDavid du Colombier 		EccCorrStts	= 1<<20,
77*43f728cbSDavid du Colombier 		EccUncorrStts	= 1<<21,
78*43f728cbSDavid du Colombier 
79*43f728cbSDavid du Colombier 	Gpc		= 0x024,	/* gp cntrl */
80*43f728cbSDavid du Colombier 		MacAccessEna	= 1<<0,
81*43f728cbSDavid du Colombier 		MacClockReady	= 1<<0,
82*43f728cbSDavid du Colombier 		InitDone	= 1<<2,
83*43f728cbSDavid du Colombier 		MacAccessReq	= 1<<3,
84*43f728cbSDavid du Colombier 		NicSleep	= 1<<4,
85*43f728cbSDavid du Colombier 		RfKill		= 1<<27,
86*43f728cbSDavid du Colombier 
87*43f728cbSDavid du Colombier 	Gio		= 0x03c,
88*43f728cbSDavid du Colombier 		EnaL0S		= 1<<1,
89*43f728cbSDavid du Colombier 
90*43f728cbSDavid du Colombier 	GpDrv	= 0x050,
91*43f728cbSDavid du Colombier 		GpDrvCalV6	= 1<<2,
92*43f728cbSDavid du Colombier 		GpDrv1X2	= 1<<3,
93*43f728cbSDavid du Colombier 		GpDrvRadioIqInvert	= 1<<7,
94*43f728cbSDavid du Colombier 
95*43f728cbSDavid du Colombier 	Led		= 0x094,
96*43f728cbSDavid du Colombier 		LedBsmCtrl	= 1<<5,
97*43f728cbSDavid du Colombier 		LedOn		= 0x38,
98*43f728cbSDavid du Colombier 		LedOff		= 0x78,
99*43f728cbSDavid du Colombier 
100*43f728cbSDavid du Colombier 	UcodeGp1Clr	= 0x05c,
101*43f728cbSDavid du Colombier 		UcodeGp1RfKill		= 1<<1,
102*43f728cbSDavid du Colombier 		UcodeGp1CmdBlocked	= 1<<2,
103*43f728cbSDavid du Colombier 		UcodeGp1CtempStopRf	= 1<<3,
104*43f728cbSDavid du Colombier 
105*43f728cbSDavid du Colombier 	ShadowRegCtrl	= 0x0a8,
106*43f728cbSDavid du Colombier 
107*43f728cbSDavid du Colombier 	Giochicken	= 0x100,
108*43f728cbSDavid du Colombier 		L1AnoL0Srx	= 1<<23,
109*43f728cbSDavid du Colombier 		DisL0Stimer	= 1<<29,
110*43f728cbSDavid du Colombier 
111*43f728cbSDavid du Colombier 	AnaPll		= 0x20c,
112*43f728cbSDavid du Colombier 
113*43f728cbSDavid du Colombier 	Dbghpetmem	= 0x240,
114*43f728cbSDavid du Colombier 	Dbglinkpwrmgmt	= 0x250,
115*43f728cbSDavid du Colombier 
116*43f728cbSDavid du Colombier 	MemRaddr	= 0x40c,
117*43f728cbSDavid du Colombier 	MemWaddr	= 0x410,
118*43f728cbSDavid du Colombier 	MemWdata	= 0x418,
119*43f728cbSDavid du Colombier 	MemRdata	= 0x41c,
120*43f728cbSDavid du Colombier 
121*43f728cbSDavid du Colombier 	PrphWaddr	= 0x444,
122*43f728cbSDavid du Colombier 	PrphRaddr	= 0x448,
123*43f728cbSDavid du Colombier 	PrphWdata	= 0x44c,
124*43f728cbSDavid du Colombier 	PrphRdata	= 0x450,
125*43f728cbSDavid du Colombier 
126*43f728cbSDavid du Colombier 	HbusTargWptr	= 0x460,
127*43f728cbSDavid du Colombier };
128*43f728cbSDavid du Colombier 
129*43f728cbSDavid du Colombier /*
130*43f728cbSDavid du Colombier  * Flow-Handler registers.
131*43f728cbSDavid du Colombier  */
132*43f728cbSDavid du Colombier enum {
133*43f728cbSDavid du Colombier 	FhTfbdCtrl0	= 0x1900,	// +q*8
134*43f728cbSDavid du Colombier 	FhTfbdCtrl1	= 0x1904,	// +q*8
135*43f728cbSDavid du Colombier 
136*43f728cbSDavid du Colombier 	FhKwAddr	= 0x197c,
137*43f728cbSDavid du Colombier 
138*43f728cbSDavid du Colombier 	FhSramAddr	= 0x19a4,	// +q*4
139*43f728cbSDavid du Colombier 	FhCbbcQueue	= 0x19d0,	// +q*4
140*43f728cbSDavid du Colombier 	FhStatusWptr	= 0x1bc0,
141*43f728cbSDavid du Colombier 	FhRxBase	= 0x1bc4,
142*43f728cbSDavid du Colombier 	FhRxWptr	= 0x1bc8,
143*43f728cbSDavid du Colombier 	FhRxConfig	= 0x1c00,
144*43f728cbSDavid du Colombier 		FhRxConfigEna		= 1<<31,
145*43f728cbSDavid du Colombier 		FhRxConfigRbSize8K	= 1<<16,
146*43f728cbSDavid du Colombier 		FhRxConfigSingleFrame	= 1<<15,
147*43f728cbSDavid du Colombier 		FhRxConfigIrqDstHost	= 1<<12,
148*43f728cbSDavid du Colombier 		FhRxConfigIgnRxfEmpty	= 1<<2,
149*43f728cbSDavid du Colombier 
150*43f728cbSDavid du Colombier 		FhRxConfigNrbdShift	= 20,
151*43f728cbSDavid du Colombier 		FhRxConfigRbTimeoutShift= 4,
152*43f728cbSDavid du Colombier 
153*43f728cbSDavid du Colombier 	FhRxStatus	= 0x1c44,
154*43f728cbSDavid du Colombier 
155*43f728cbSDavid du Colombier 	FhTxConfig	= 0x1d00,	// +q*32
156*43f728cbSDavid du Colombier 		FhTxConfigDmaCreditEna	= 1<<3,
157*43f728cbSDavid du Colombier 		FhTxConfigDmaEna	= 1<<31,
158*43f728cbSDavid du Colombier 		FhTxConfigCirqHostEndTfd= 1<<20,
159*43f728cbSDavid du Colombier 
160*43f728cbSDavid du Colombier 	FhTxBufStatus	= 0x1d08,	// +q*32
161*43f728cbSDavid du Colombier 		FhTxBufStatusTbNumShift	= 20,
162*43f728cbSDavid du Colombier 		FhTxBufStatusTbIdxShift = 12,
163*43f728cbSDavid du Colombier 		FhTxBufStatusTfbdValid	= 3,
164*43f728cbSDavid du Colombier 
165*43f728cbSDavid du Colombier 	FhTxChicken	= 0x1e98,
166*43f728cbSDavid du Colombier 	FhTxStatus	= 0x1eb0,
167*43f728cbSDavid du Colombier };
168*43f728cbSDavid du Colombier 
169*43f728cbSDavid du Colombier /*
170*43f728cbSDavid du Colombier  * NIC internal memory offsets.
171*43f728cbSDavid du Colombier  */
172*43f728cbSDavid du Colombier enum {
173*43f728cbSDavid du Colombier 	ApmgClkCtrl	= 0x3000,
174*43f728cbSDavid du Colombier 	ApmgClkEna	= 0x3004,
175*43f728cbSDavid du Colombier 	ApmgClkDis	= 0x3008,
176*43f728cbSDavid du Colombier 		DmaClkRqt	= 1<<9,
177*43f728cbSDavid du Colombier 		BsmClkRqt	= 1<<11,
178*43f728cbSDavid du Colombier 
179*43f728cbSDavid du Colombier 	ApmgPs		= 0x300c,
180*43f728cbSDavid du Colombier 		EarlyPwroffDis	= 1<<22,
181*43f728cbSDavid du Colombier 		PwrSrcVMain	= 0<<24,
182*43f728cbSDavid du Colombier 		PwrSrcVAux	= 2<<24,
183*43f728cbSDavid du Colombier 		PwrSrcMask	= 3<<24,
184*43f728cbSDavid du Colombier 		ResetReq	= 1<<26,
185*43f728cbSDavid du Colombier 
186*43f728cbSDavid du Colombier 	ApmgDigitalSvr	= 0x3058,
187*43f728cbSDavid du Colombier 	ApmgAnalogSvr	= 0x306c,
188*43f728cbSDavid du Colombier 	ApmgPciStt	= 0x3010,
189*43f728cbSDavid du Colombier 	BsmWrCtrl	= 0x3400,
190*43f728cbSDavid du Colombier 	BsmWrMemSrc	= 0x3404,
191*43f728cbSDavid du Colombier 	BsmWrMemDst	= 0x3408,
192*43f728cbSDavid du Colombier 	BsmWrDwCount	= 0x340c,
193*43f728cbSDavid du Colombier 	BsmDramTextAddr	= 0x3490,
194*43f728cbSDavid du Colombier 	BsmDramTextSize	= 0x3494,
195*43f728cbSDavid du Colombier 	BsmDramDataAddr	= 0x3498,
196*43f728cbSDavid du Colombier 	BsmDramDataSize	= 0x349c,
197*43f728cbSDavid du Colombier 	BsmSramBase	= 0x3800,
198*43f728cbSDavid du Colombier };
199*43f728cbSDavid du Colombier 
200*43f728cbSDavid du Colombier /*
201*43f728cbSDavid du Colombier  * TX scheduler registers.
202*43f728cbSDavid du Colombier  */
203*43f728cbSDavid du Colombier enum {
204*43f728cbSDavid du Colombier 	SchedBase		= 0xa02c00,
205*43f728cbSDavid du Colombier 	SchedSramAddr		= SchedBase,
206*43f728cbSDavid du Colombier 
207*43f728cbSDavid du Colombier 	SchedDramAddr4965	= SchedBase+0x010,
208*43f728cbSDavid du Colombier 	SchedTxFact4965		= SchedBase+0x01c,
209*43f728cbSDavid du Colombier 	SchedQueueRdptr4965	= SchedBase+0x064,	// +q*4
210*43f728cbSDavid du Colombier 	SchedQChainSel4965	= SchedBase+0x0d0,
211*43f728cbSDavid du Colombier 	SchedIntrMask4965	= SchedBase+0x0e4,
212*43f728cbSDavid du Colombier 	SchedQueueStatus4965	= SchedBase+0x104,	// +q*4
213*43f728cbSDavid du Colombier 
214*43f728cbSDavid du Colombier 	SchedDramAddr5000	= SchedBase+0x008,
215*43f728cbSDavid du Colombier 	SchedTxFact5000		= SchedBase+0x010,
216*43f728cbSDavid du Colombier 	SchedQueueRdptr5000	= SchedBase+0x068,	// +q*4
217*43f728cbSDavid du Colombier 	SchedQChainSel5000	= SchedBase+0x0e8,
218*43f728cbSDavid du Colombier 	SchedIntrMask5000	= SchedBase+0x108,
219*43f728cbSDavid du Colombier 	SchedQueueStatus5000	= SchedBase+0x10c,	// +q*4
220*43f728cbSDavid du Colombier 	SchedAggrSel5000	= SchedBase+0x248,
221*43f728cbSDavid du Colombier };
222*43f728cbSDavid du Colombier 
223*43f728cbSDavid du Colombier enum {
224*43f728cbSDavid du Colombier 	SchedCtxOff4965		= 0x380,
225*43f728cbSDavid du Colombier 	SchedCtxLen4965		= 416,
226*43f728cbSDavid du Colombier 
227*43f728cbSDavid du Colombier 	SchedCtxOff5000		= 0x600,
228*43f728cbSDavid du Colombier 	SchedCtxLen5000		= 512,
229*43f728cbSDavid du Colombier };
230*43f728cbSDavid du Colombier 
231*43f728cbSDavid du Colombier enum {
232*43f728cbSDavid du Colombier 	FilterPromisc		= 1<<0,
233*43f728cbSDavid du Colombier 	FilterCtl		= 1<<1,
234*43f728cbSDavid du Colombier 	FilterMulticast		= 1<<2,
235*43f728cbSDavid du Colombier 	FilterNoDecrypt		= 1<<3,
236*43f728cbSDavid du Colombier 	FilterBSS		= 1<<5,
237*43f728cbSDavid du Colombier 	FilterBeacon		= 1<<6,
238*43f728cbSDavid du Colombier };
239*43f728cbSDavid du Colombier 
240*43f728cbSDavid du Colombier enum {
241*43f728cbSDavid du Colombier 	RFlag24Ghz		= 1<<0,
242*43f728cbSDavid du Colombier 	RFlagCCK		= 1<<1,
243*43f728cbSDavid du Colombier 	RFlagAuto		= 1<<2,
244*43f728cbSDavid du Colombier 	RFlagShSlot		= 1<<4,
245*43f728cbSDavid du Colombier 	RFlagShPreamble		= 1<<5,
246*43f728cbSDavid du Colombier 	RFlagNoDiversity	= 1<<7,
247*43f728cbSDavid du Colombier 	RFlagAntennaA		= 1<<8,
248*43f728cbSDavid du Colombier 	RFlagAntennaB		= 1<<9,
249*43f728cbSDavid du Colombier 	RFlagTSF		= 1<<15,
250*43f728cbSDavid du Colombier 	RFlagCTSToSelf		= 1<<30,
251*43f728cbSDavid du Colombier };
252*43f728cbSDavid du Colombier 
253*43f728cbSDavid du Colombier typedef struct FWInfo FWInfo;
254*43f728cbSDavid du Colombier typedef struct FWImage FWImage;
255*43f728cbSDavid du Colombier typedef struct FWSect FWSect;
256*43f728cbSDavid du Colombier 
257*43f728cbSDavid du Colombier typedef struct TXQ TXQ;
258*43f728cbSDavid du Colombier typedef struct RXQ RXQ;
259*43f728cbSDavid du Colombier 
260*43f728cbSDavid du Colombier typedef struct Ctlr Ctlr;
261*43f728cbSDavid du Colombier 
262*43f728cbSDavid du Colombier struct FWSect
263*43f728cbSDavid du Colombier {
264*43f728cbSDavid du Colombier 	uchar	*data;
265*43f728cbSDavid du Colombier 	uint	size;
266*43f728cbSDavid du Colombier };
267*43f728cbSDavid du Colombier 
268*43f728cbSDavid du Colombier struct FWImage
269*43f728cbSDavid du Colombier {
270*43f728cbSDavid du Colombier 	struct {
271*43f728cbSDavid du Colombier 		FWSect	text;
272*43f728cbSDavid du Colombier 		FWSect	data;
273*43f728cbSDavid du Colombier 	} init, main, boot;
274*43f728cbSDavid du Colombier 
275*43f728cbSDavid du Colombier 	uint	rev;
276*43f728cbSDavid du Colombier 	uint	build;
277*43f728cbSDavid du Colombier 	char	descr[64+1];
278*43f728cbSDavid du Colombier 	uchar	data[];
279*43f728cbSDavid du Colombier };
280*43f728cbSDavid du Colombier 
281*43f728cbSDavid du Colombier struct FWInfo
282*43f728cbSDavid du Colombier {
283*43f728cbSDavid du Colombier 	uchar	major;
284*43f728cbSDavid du Colombier 	uchar	minjor;
285*43f728cbSDavid du Colombier 	uchar	type;
286*43f728cbSDavid du Colombier 	uchar	subtype;
287*43f728cbSDavid du Colombier 
288*43f728cbSDavid du Colombier 	u32int	logptr;
289*43f728cbSDavid du Colombier 	u32int	errptr;
290*43f728cbSDavid du Colombier 	u32int	tstamp;
291*43f728cbSDavid du Colombier 	u32int	valid;
292*43f728cbSDavid du Colombier };
293*43f728cbSDavid du Colombier 
294*43f728cbSDavid du Colombier struct TXQ
295*43f728cbSDavid du Colombier {
296*43f728cbSDavid du Colombier 	uint	n;
297*43f728cbSDavid du Colombier 	uint	i;
298*43f728cbSDavid du Colombier 	Block	**b;
299*43f728cbSDavid du Colombier 	uchar	*d;
300*43f728cbSDavid du Colombier 	uchar	*c;
301*43f728cbSDavid du Colombier 
302*43f728cbSDavid du Colombier 	uint	lastcmd;
303*43f728cbSDavid du Colombier 
304*43f728cbSDavid du Colombier 	Rendez;
305*43f728cbSDavid du Colombier 	QLock;
306*43f728cbSDavid du Colombier };
307*43f728cbSDavid du Colombier 
308*43f728cbSDavid du Colombier struct RXQ
309*43f728cbSDavid du Colombier {
310*43f728cbSDavid du Colombier 	uint	i;
311*43f728cbSDavid du Colombier 	Block	**b;
312*43f728cbSDavid du Colombier 	u32int	*p;
313*43f728cbSDavid du Colombier 	uchar	*s;
314*43f728cbSDavid du Colombier };
315*43f728cbSDavid du Colombier 
316*43f728cbSDavid du Colombier struct Ctlr {
317*43f728cbSDavid du Colombier 	Lock;
318*43f728cbSDavid du Colombier 	QLock;
319*43f728cbSDavid du Colombier 
320*43f728cbSDavid du Colombier 	Ctlr *link;
321*43f728cbSDavid du Colombier 	Pcidev *pdev;
322*43f728cbSDavid du Colombier 	Wifi *wifi;
323*43f728cbSDavid du Colombier 
324*43f728cbSDavid du Colombier 	int type;
325*43f728cbSDavid du Colombier 	int port;
326*43f728cbSDavid du Colombier 	int power;
327*43f728cbSDavid du Colombier 	int active;
328*43f728cbSDavid du Colombier 	int broken;
329*43f728cbSDavid du Colombier 	int attached;
330*43f728cbSDavid du Colombier 
331*43f728cbSDavid du Colombier 	u32int ie;
332*43f728cbSDavid du Colombier 
333*43f728cbSDavid du Colombier 	u32int *nic;
334*43f728cbSDavid du Colombier 	uchar *kwpage;
335*43f728cbSDavid du Colombier 
336*43f728cbSDavid du Colombier 	/* assigned node ids in hardware node table or -1 if unassigned */
337*43f728cbSDavid du Colombier 	int bcastnodeid;
338*43f728cbSDavid du Colombier 	int bssnodeid;
339*43f728cbSDavid du Colombier 
340*43f728cbSDavid du Colombier 	/* current receiver settings */
341*43f728cbSDavid du Colombier 	uchar bssid[Eaddrlen];
342*43f728cbSDavid du Colombier 	int channel;
343*43f728cbSDavid du Colombier 	int prom;
344*43f728cbSDavid du Colombier 	int aid;
345*43f728cbSDavid du Colombier 
346*43f728cbSDavid du Colombier 	RXQ rx;
347*43f728cbSDavid du Colombier 	TXQ tx[20];
348*43f728cbSDavid du Colombier 
349*43f728cbSDavid du Colombier 	struct {
350*43f728cbSDavid du Colombier 		Rendez;
351*43f728cbSDavid du Colombier 		u32int	m;
352*43f728cbSDavid du Colombier 		u32int	w;
353*43f728cbSDavid du Colombier 	} wait;
354*43f728cbSDavid du Colombier 
355*43f728cbSDavid du Colombier 	struct {
356*43f728cbSDavid du Colombier 		uchar	type;
357*43f728cbSDavid du Colombier 		uchar	step;
358*43f728cbSDavid du Colombier 		uchar	dash;
359*43f728cbSDavid du Colombier 		uchar	txantmask;
360*43f728cbSDavid du Colombier 		uchar	rxantmask;
361*43f728cbSDavid du Colombier 	} rfcfg;
362*43f728cbSDavid du Colombier 
363*43f728cbSDavid du Colombier 	struct {
364*43f728cbSDavid du Colombier 		int	otp;
365*43f728cbSDavid du Colombier 		uint	off;
366*43f728cbSDavid du Colombier 
367*43f728cbSDavid du Colombier 		uchar	version;
368*43f728cbSDavid du Colombier 		uchar	type;
369*43f728cbSDavid du Colombier 		u16int	volt;
370*43f728cbSDavid du Colombier 		u16int	temp;
371*43f728cbSDavid du Colombier 		u16int	rawtemp;
372*43f728cbSDavid du Colombier 
373*43f728cbSDavid du Colombier 		char	regdom[4+1];
374*43f728cbSDavid du Colombier 
375*43f728cbSDavid du Colombier 		u32int	crystal;
376*43f728cbSDavid du Colombier 	} eeprom;
377*43f728cbSDavid du Colombier 
378*43f728cbSDavid du Colombier 	struct {
379*43f728cbSDavid du Colombier 		Block	*cmd[21];
380*43f728cbSDavid du Colombier 		int	done;
381*43f728cbSDavid du Colombier 	} calib;
382*43f728cbSDavid du Colombier 
383*43f728cbSDavid du Colombier 	struct {
384*43f728cbSDavid du Colombier 		u32int	base;
385*43f728cbSDavid du Colombier 		uchar	*s;
386*43f728cbSDavid du Colombier 	} sched;
387*43f728cbSDavid du Colombier 
388*43f728cbSDavid du Colombier 	FWInfo fwinfo;
389*43f728cbSDavid du Colombier 	FWImage *fw;
390*43f728cbSDavid du Colombier };
391*43f728cbSDavid du Colombier 
392*43f728cbSDavid du Colombier /* controller types */
393*43f728cbSDavid du Colombier enum {
394*43f728cbSDavid du Colombier 	Type4965	= 0,
395*43f728cbSDavid du Colombier 	Type5300	= 2,
396*43f728cbSDavid du Colombier 	Type5350	= 3,
397*43f728cbSDavid du Colombier 	Type5150	= 4,
398*43f728cbSDavid du Colombier 	Type5100	= 5,
399*43f728cbSDavid du Colombier 	Type1000	= 6,
400*43f728cbSDavid du Colombier 	Type6000	= 7,
401*43f728cbSDavid du Colombier 	Type6050	= 8,
402*43f728cbSDavid du Colombier 	Type6005	= 11,	/* also Centrino Advanced-N 6030, 6235 */
403*43f728cbSDavid du Colombier 	Type2030	= 12,
404*43f728cbSDavid du Colombier };
405*43f728cbSDavid du Colombier 
406*43f728cbSDavid du Colombier static char *fwname[32] = {
407*43f728cbSDavid du Colombier 	[Type4965] "iwn-4965",
408*43f728cbSDavid du Colombier 	[Type5300] "iwn-5000",
409*43f728cbSDavid du Colombier 	[Type5350] "iwn-5000",
410*43f728cbSDavid du Colombier 	[Type5150] "iwn-5150",
411*43f728cbSDavid du Colombier 	[Type5100] "iwn-5000",
412*43f728cbSDavid du Colombier 	[Type1000] "iwn-1000",
413*43f728cbSDavid du Colombier 	[Type6000] "iwn-6000",
414*43f728cbSDavid du Colombier 	[Type6050] "iwn-6050",
415*43f728cbSDavid du Colombier 	[Type6005] "iwn-6005", /* see in iwlattach() below */
416*43f728cbSDavid du Colombier 	[Type2030] "iwn-2030",
417*43f728cbSDavid du Colombier };
418*43f728cbSDavid du Colombier 
419*43f728cbSDavid du Colombier static char *qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block);
420*43f728cbSDavid du Colombier static char *flushq(Ctlr *ctlr, uint qid);
421*43f728cbSDavid du Colombier static char *cmd(Ctlr *ctlr, uint code, uchar *data, int size);
422*43f728cbSDavid du Colombier 
423*43f728cbSDavid du Colombier #define csr32r(c, r)	(*((c)->nic+((r)/4)))
424*43f728cbSDavid du Colombier #define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
425*43f728cbSDavid du Colombier 
426*43f728cbSDavid du Colombier static uint
get16(uchar * p)427*43f728cbSDavid du Colombier get16(uchar *p){
428*43f728cbSDavid du Colombier 	return *((u16int*)p);
429*43f728cbSDavid du Colombier }
430*43f728cbSDavid du Colombier static uint
get32(uchar * p)431*43f728cbSDavid du Colombier get32(uchar *p){
432*43f728cbSDavid du Colombier 	return *((u32int*)p);
433*43f728cbSDavid du Colombier }
434*43f728cbSDavid du Colombier static void
put32(uchar * p,uint v)435*43f728cbSDavid du Colombier put32(uchar *p, uint v){
436*43f728cbSDavid du Colombier 	*((u32int*)p) = v;
437*43f728cbSDavid du Colombier }
438*43f728cbSDavid du Colombier static void
put16(uchar * p,uint v)439*43f728cbSDavid du Colombier put16(uchar *p, uint v){
440*43f728cbSDavid du Colombier 	*((u16int*)p) = v;
441*43f728cbSDavid du Colombier };
442*43f728cbSDavid du Colombier 
443*43f728cbSDavid du Colombier static char*
niclock(Ctlr * ctlr)444*43f728cbSDavid du Colombier niclock(Ctlr *ctlr)
445*43f728cbSDavid du Colombier {
446*43f728cbSDavid du Colombier 	int i;
447*43f728cbSDavid du Colombier 
448*43f728cbSDavid du Colombier 	csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) | MacAccessReq);
449*43f728cbSDavid du Colombier 	for(i=0; i<1000; i++){
450*43f728cbSDavid du Colombier 		if((csr32r(ctlr, Gpc) & (NicSleep | MacAccessEna)) == MacAccessEna)
451*43f728cbSDavid du Colombier 			return 0;
452*43f728cbSDavid du Colombier 		delay(10);
453*43f728cbSDavid du Colombier 	}
454*43f728cbSDavid du Colombier 	return "niclock: timeout";
455*43f728cbSDavid du Colombier }
456*43f728cbSDavid du Colombier 
457*43f728cbSDavid du Colombier static void
nicunlock(Ctlr * ctlr)458*43f728cbSDavid du Colombier nicunlock(Ctlr *ctlr)
459*43f728cbSDavid du Colombier {
460*43f728cbSDavid du Colombier 	csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) & ~MacAccessReq);
461*43f728cbSDavid du Colombier }
462*43f728cbSDavid du Colombier 
463*43f728cbSDavid du Colombier static u32int
prphread(Ctlr * ctlr,uint off)464*43f728cbSDavid du Colombier prphread(Ctlr *ctlr, uint off)
465*43f728cbSDavid du Colombier {
466*43f728cbSDavid du Colombier 	csr32w(ctlr, PrphRaddr, ((sizeof(u32int)-1)<<24) | off);
467*43f728cbSDavid du Colombier 	coherence();
468*43f728cbSDavid du Colombier 	return csr32r(ctlr, PrphRdata);
469*43f728cbSDavid du Colombier }
470*43f728cbSDavid du Colombier static void
prphwrite(Ctlr * ctlr,uint off,u32int data)471*43f728cbSDavid du Colombier prphwrite(Ctlr *ctlr, uint off, u32int data)
472*43f728cbSDavid du Colombier {
473*43f728cbSDavid du Colombier 	csr32w(ctlr, PrphWaddr, ((sizeof(u32int)-1)<<24) | off);
474*43f728cbSDavid du Colombier 	coherence();
475*43f728cbSDavid du Colombier 	csr32w(ctlr, PrphWdata, data);
476*43f728cbSDavid du Colombier }
477*43f728cbSDavid du Colombier 
478*43f728cbSDavid du Colombier static u32int
memread(Ctlr * ctlr,uint off)479*43f728cbSDavid du Colombier memread(Ctlr *ctlr, uint off)
480*43f728cbSDavid du Colombier {
481*43f728cbSDavid du Colombier 	csr32w(ctlr, MemRaddr, off);
482*43f728cbSDavid du Colombier 	coherence();
483*43f728cbSDavid du Colombier 	return csr32r(ctlr, MemRdata);
484*43f728cbSDavid du Colombier }
485*43f728cbSDavid du Colombier static void
memwrite(Ctlr * ctlr,uint off,u32int data)486*43f728cbSDavid du Colombier memwrite(Ctlr *ctlr, uint off, u32int data)
487*43f728cbSDavid du Colombier {
488*43f728cbSDavid du Colombier 	csr32w(ctlr, MemWaddr, off);
489*43f728cbSDavid du Colombier 	coherence();
490*43f728cbSDavid du Colombier 	csr32w(ctlr, MemWdata, data);
491*43f728cbSDavid du Colombier }
492*43f728cbSDavid du Colombier 
493*43f728cbSDavid du Colombier static void
setfwinfo(Ctlr * ctlr,uchar * d,int len)494*43f728cbSDavid du Colombier setfwinfo(Ctlr *ctlr, uchar *d, int len)
495*43f728cbSDavid du Colombier {
496*43f728cbSDavid du Colombier 	FWInfo *i;
497*43f728cbSDavid du Colombier 
498*43f728cbSDavid du Colombier 	if(len < 32)
499*43f728cbSDavid du Colombier 		return;
500*43f728cbSDavid du Colombier 	i = &ctlr->fwinfo;
501*43f728cbSDavid du Colombier 	i->minjor = *d++;
502*43f728cbSDavid du Colombier 	i->major = *d++;
503*43f728cbSDavid du Colombier 	d += 2+8;
504*43f728cbSDavid du Colombier 	i->type = *d++;
505*43f728cbSDavid du Colombier 	i->subtype = *d++;
506*43f728cbSDavid du Colombier 	d += 2;
507*43f728cbSDavid du Colombier 	i->logptr = get32(d); d += 4;
508*43f728cbSDavid du Colombier 	i->errptr = get32(d); d += 4;
509*43f728cbSDavid du Colombier 	i->tstamp = get32(d); d += 4;
510*43f728cbSDavid du Colombier 	i->valid = get32(d);
511*43f728cbSDavid du Colombier };
512*43f728cbSDavid du Colombier 
513*43f728cbSDavid du Colombier static void
dumpctlr(Ctlr * ctlr)514*43f728cbSDavid du Colombier dumpctlr(Ctlr *ctlr)
515*43f728cbSDavid du Colombier {
516*43f728cbSDavid du Colombier 	u32int dump[13];
517*43f728cbSDavid du Colombier 	int i;
518*43f728cbSDavid du Colombier 
519*43f728cbSDavid du Colombier 	print("lastcmd: %ud (0x%ux)\n", ctlr->tx[4].lastcmd,  ctlr->tx[4].lastcmd);
520*43f728cbSDavid du Colombier 	if(ctlr->fwinfo.errptr == 0){
521*43f728cbSDavid du Colombier 		print("no error pointer\n");
522*43f728cbSDavid du Colombier 		return;
523*43f728cbSDavid du Colombier 	}
524*43f728cbSDavid du Colombier 	for(i=0; i<nelem(dump); i++)
525*43f728cbSDavid du Colombier 		dump[i] = memread(ctlr, ctlr->fwinfo.errptr + i*4);
526*43f728cbSDavid du Colombier 	print(	"error:\tid %ux, pc %ux,\n"
527*43f728cbSDavid du Colombier 		"\tbranchlink %.8ux %.8ux, interruptlink %.8ux %.8ux,\n"
528*43f728cbSDavid du Colombier 		"\terrordata %.8ux %.8ux, srcline %ud, tsf %ux, time %ux\n",
529*43f728cbSDavid du Colombier 		dump[1], dump[2],
530*43f728cbSDavid du Colombier 		dump[4], dump[3], dump[6], dump[5],
531*43f728cbSDavid du Colombier 		dump[7], dump[8], dump[9], dump[10], dump[11]);
532*43f728cbSDavid du Colombier }
533*43f728cbSDavid du Colombier 
534*43f728cbSDavid du Colombier static char*
eepromlock(Ctlr * ctlr)535*43f728cbSDavid du Colombier eepromlock(Ctlr *ctlr)
536*43f728cbSDavid du Colombier {
537*43f728cbSDavid du Colombier 	int i, j;
538*43f728cbSDavid du Colombier 
539*43f728cbSDavid du Colombier 	for(i=0; i<100; i++){
540*43f728cbSDavid du Colombier 		csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | EepromLocked);
541*43f728cbSDavid du Colombier 		for(j=0; j<100; j++){
542*43f728cbSDavid du Colombier 			if(csr32r(ctlr, Cfg) & EepromLocked)
543*43f728cbSDavid du Colombier 				return 0;
544*43f728cbSDavid du Colombier 			delay(10);
545*43f728cbSDavid du Colombier 		}
546*43f728cbSDavid du Colombier 	}
547*43f728cbSDavid du Colombier 	return "eepromlock: timeout";
548*43f728cbSDavid du Colombier }
549*43f728cbSDavid du Colombier static void
eepromunlock(Ctlr * ctlr)550*43f728cbSDavid du Colombier eepromunlock(Ctlr *ctlr)
551*43f728cbSDavid du Colombier {
552*43f728cbSDavid du Colombier 	csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) & ~EepromLocked);
553*43f728cbSDavid du Colombier }
554*43f728cbSDavid du Colombier static char*
eepromread(Ctlr * ctlr,void * data,int count,uint off)555*43f728cbSDavid du Colombier eepromread(Ctlr *ctlr, void *data, int count, uint off)
556*43f728cbSDavid du Colombier {
557*43f728cbSDavid du Colombier 	uchar *out = data;
558*43f728cbSDavid du Colombier 	u32int w, s;
559*43f728cbSDavid du Colombier 	int i;
560*43f728cbSDavid du Colombier 
561*43f728cbSDavid du Colombier 	w = 0;
562*43f728cbSDavid du Colombier 	off += ctlr->eeprom.off;
563*43f728cbSDavid du Colombier 	for(; count > 0; count -= 2, off++){
564*43f728cbSDavid du Colombier 		csr32w(ctlr, EepromIo, off << 2);
565*43f728cbSDavid du Colombier 		for(i=0; i<10; i++){
566*43f728cbSDavid du Colombier 			w = csr32r(ctlr, EepromIo);
567*43f728cbSDavid du Colombier 			if(w & 1)
568*43f728cbSDavid du Colombier 				break;
569*43f728cbSDavid du Colombier 			delay(5);
570*43f728cbSDavid du Colombier 		}
571*43f728cbSDavid du Colombier 		if(i == 10)
572*43f728cbSDavid du Colombier 			return "eepromread: timeout";
573*43f728cbSDavid du Colombier 		if(ctlr->eeprom.otp){
574*43f728cbSDavid du Colombier 			s = csr32r(ctlr, OtpromGp);
575*43f728cbSDavid du Colombier 			if(s & EccUncorrStts)
576*43f728cbSDavid du Colombier 				return "eepromread: otprom ecc error";
577*43f728cbSDavid du Colombier 			if(s & EccCorrStts)
578*43f728cbSDavid du Colombier 				csr32w(ctlr, OtpromGp, s);
579*43f728cbSDavid du Colombier 		}
580*43f728cbSDavid du Colombier 		*out++ = w >> 16;
581*43f728cbSDavid du Colombier 		if(count > 1)
582*43f728cbSDavid du Colombier 			*out++ = w >> 24;
583*43f728cbSDavid du Colombier 	}
584*43f728cbSDavid du Colombier 	return 0;
585*43f728cbSDavid du Colombier }
586*43f728cbSDavid du Colombier 
587*43f728cbSDavid du Colombier static char*
handover(Ctlr * ctlr)588*43f728cbSDavid du Colombier handover(Ctlr *ctlr)
589*43f728cbSDavid du Colombier {
590*43f728cbSDavid du Colombier 	int i;
591*43f728cbSDavid du Colombier 
592*43f728cbSDavid du Colombier 	csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | NicReady);
593*43f728cbSDavid du Colombier 	for(i=0; i<5; i++){
594*43f728cbSDavid du Colombier 		if(csr32r(ctlr, Cfg) & NicReady)
595*43f728cbSDavid du Colombier 			return 0;
596*43f728cbSDavid du Colombier 		delay(10);
597*43f728cbSDavid du Colombier 	}
598*43f728cbSDavid du Colombier 	csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | Prepare);
599*43f728cbSDavid du Colombier 	for(i=0; i<15000; i++){
600*43f728cbSDavid du Colombier 		if((csr32r(ctlr, Cfg) & PrepareDone) == 0)
601*43f728cbSDavid du Colombier 			break;
602*43f728cbSDavid du Colombier 		delay(10);
603*43f728cbSDavid du Colombier 	}
604*43f728cbSDavid du Colombier 	if(i >= 15000)
605*43f728cbSDavid du Colombier 		return "handover: timeout";
606*43f728cbSDavid du Colombier 	csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | NicReady);
607*43f728cbSDavid du Colombier 	for(i=0; i<5; i++){
608*43f728cbSDavid du Colombier 		if(csr32r(ctlr, Cfg) & NicReady)
609*43f728cbSDavid du Colombier 			return 0;
610*43f728cbSDavid du Colombier 		delay(10);
611*43f728cbSDavid du Colombier 	}
612*43f728cbSDavid du Colombier 	return "handover: timeout";
613*43f728cbSDavid du Colombier }
614*43f728cbSDavid du Colombier 
615*43f728cbSDavid du Colombier static char*
clockwait(Ctlr * ctlr)616*43f728cbSDavid du Colombier clockwait(Ctlr *ctlr)
617*43f728cbSDavid du Colombier {
618*43f728cbSDavid du Colombier 	int i;
619*43f728cbSDavid du Colombier 
620*43f728cbSDavid du Colombier 	/* Set "initialization complete" bit. */
621*43f728cbSDavid du Colombier 	csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) | InitDone);
622*43f728cbSDavid du Colombier 	for(i=0; i<2500; i++){
623*43f728cbSDavid du Colombier 		if(csr32r(ctlr, Gpc) & MacClockReady)
624*43f728cbSDavid du Colombier 			return 0;
625*43f728cbSDavid du Colombier 		delay(10);
626*43f728cbSDavid du Colombier 	}
627*43f728cbSDavid du Colombier 	return "clockwait: timeout";
628*43f728cbSDavid du Colombier }
629*43f728cbSDavid du Colombier 
630*43f728cbSDavid du Colombier static char*
poweron(Ctlr * ctlr)631*43f728cbSDavid du Colombier poweron(Ctlr *ctlr)
632*43f728cbSDavid du Colombier {
633*43f728cbSDavid du Colombier 	int capoff;
634*43f728cbSDavid du Colombier 	char *err;
635*43f728cbSDavid du Colombier 
636*43f728cbSDavid du Colombier 	/* Disable L0s exit timer (NMI bug workaround). */
637*43f728cbSDavid du Colombier 	csr32w(ctlr, Giochicken, csr32r(ctlr, Giochicken) | DisL0Stimer);
638*43f728cbSDavid du Colombier 
639*43f728cbSDavid du Colombier 	/* Don't wait for ICH L0s (ICH bug workaround). */
640*43f728cbSDavid du Colombier 	csr32w(ctlr, Giochicken, csr32r(ctlr, Giochicken) | L1AnoL0Srx);
641*43f728cbSDavid du Colombier 
642*43f728cbSDavid du Colombier 	/* Set FH wait threshold to max (HW bug under stress workaround). */
643*43f728cbSDavid du Colombier 	csr32w(ctlr, Dbghpetmem, csr32r(ctlr, Dbghpetmem) | 0xffff0000);
644*43f728cbSDavid du Colombier 
645*43f728cbSDavid du Colombier 	/* Enable HAP INTA to move adapter from L1a to L0s. */
646*43f728cbSDavid du Colombier 	csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | HapwakeL1A);
647*43f728cbSDavid du Colombier 
648*43f728cbSDavid du Colombier 	capoff = pcicap(ctlr->pdev, PciCapPCIe);
649*43f728cbSDavid du Colombier 	if(capoff != -1){
650*43f728cbSDavid du Colombier 		/* Workaround for HW instability in PCIe L0->L0s->L1 transition. */
651*43f728cbSDavid du Colombier 		if(pcicfgr16(ctlr->pdev, capoff + 0x10) & 0x2)	/* LCSR -> L1 Entry enabled. */
652*43f728cbSDavid du Colombier 			csr32w(ctlr, Gio, csr32r(ctlr, Gio) | EnaL0S);
653*43f728cbSDavid du Colombier 		else
654*43f728cbSDavid du Colombier 			csr32w(ctlr, Gio, csr32r(ctlr, Gio) & ~EnaL0S);
655*43f728cbSDavid du Colombier 	}
656*43f728cbSDavid du Colombier 
657*43f728cbSDavid du Colombier 	if(ctlr->type != Type4965 && ctlr->type <= Type1000)
658*43f728cbSDavid du Colombier 		csr32w(ctlr, AnaPll, csr32r(ctlr, AnaPll) | 0x00880300);
659*43f728cbSDavid du Colombier 
660*43f728cbSDavid du Colombier 	/* Wait for clock stabilization before accessing prph. */
661*43f728cbSDavid du Colombier 	if((err = clockwait(ctlr)) != nil)
662*43f728cbSDavid du Colombier 		return err;
663*43f728cbSDavid du Colombier 
664*43f728cbSDavid du Colombier 	if((err = niclock(ctlr)) != nil)
665*43f728cbSDavid du Colombier 		return err;
666*43f728cbSDavid du Colombier 
667*43f728cbSDavid du Colombier 	/* Enable DMA and BSM (Bootstrap State Machine). */
668*43f728cbSDavid du Colombier 	if(ctlr->type == Type4965)
669*43f728cbSDavid du Colombier 		prphwrite(ctlr, ApmgClkEna, DmaClkRqt | BsmClkRqt);
670*43f728cbSDavid du Colombier 	else
671*43f728cbSDavid du Colombier 		prphwrite(ctlr, ApmgClkEna, DmaClkRqt);
672*43f728cbSDavid du Colombier 	delay(20);
673*43f728cbSDavid du Colombier 
674*43f728cbSDavid du Colombier 	/* Disable L1-Active. */
675*43f728cbSDavid du Colombier 	prphwrite(ctlr, ApmgPciStt, prphread(ctlr, ApmgPciStt) | (1<<11));
676*43f728cbSDavid du Colombier 
677*43f728cbSDavid du Colombier 	nicunlock(ctlr);
678*43f728cbSDavid du Colombier 
679*43f728cbSDavid du Colombier 	ctlr->power = 1;
680*43f728cbSDavid du Colombier 
681*43f728cbSDavid du Colombier 	return 0;
682*43f728cbSDavid du Colombier }
683*43f728cbSDavid du Colombier 
684*43f728cbSDavid du Colombier static void
poweroff(Ctlr * ctlr)685*43f728cbSDavid du Colombier poweroff(Ctlr *ctlr)
686*43f728cbSDavid du Colombier {
687*43f728cbSDavid du Colombier 	int i, j;
688*43f728cbSDavid du Colombier 
689*43f728cbSDavid du Colombier 	csr32w(ctlr, Reset, 1);
690*43f728cbSDavid du Colombier 
691*43f728cbSDavid du Colombier 	/* Disable interrupts */
692*43f728cbSDavid du Colombier 	ctlr->ie = 0;
693*43f728cbSDavid du Colombier 	csr32w(ctlr, Imr, 0);
694*43f728cbSDavid du Colombier 	csr32w(ctlr, Isr, ~0);
695*43f728cbSDavid du Colombier 	csr32w(ctlr, FhIsr, ~0);
696*43f728cbSDavid du Colombier 
697*43f728cbSDavid du Colombier 	/* Stop scheduler */
698*43f728cbSDavid du Colombier 	if(ctlr->type != Type4965)
699*43f728cbSDavid du Colombier 		prphwrite(ctlr, SchedTxFact5000, 0);
700*43f728cbSDavid du Colombier 	else
701*43f728cbSDavid du Colombier 		prphwrite(ctlr, SchedTxFact4965, 0);
702*43f728cbSDavid du Colombier 
703*43f728cbSDavid du Colombier 	/* Stop TX ring */
704*43f728cbSDavid du Colombier 	if(niclock(ctlr) == nil){
705*43f728cbSDavid du Colombier 		for(i = (ctlr->type != Type4965) ? 7 : 6; i >= 0; i--){
706*43f728cbSDavid du Colombier 			csr32w(ctlr, FhTxConfig + i*32, 0);
707*43f728cbSDavid du Colombier 			for(j = 0; j < 200; j++){
708*43f728cbSDavid du Colombier 				if(csr32r(ctlr, FhTxStatus) & (0x10000<<i))
709*43f728cbSDavid du Colombier 					break;
710*43f728cbSDavid du Colombier 				delay(10);
711*43f728cbSDavid du Colombier 			}
712*43f728cbSDavid du Colombier 		}
713*43f728cbSDavid du Colombier 		nicunlock(ctlr);
714*43f728cbSDavid du Colombier 	}
715*43f728cbSDavid du Colombier 
716*43f728cbSDavid du Colombier 	/* Stop RX ring */
717*43f728cbSDavid du Colombier 	if(niclock(ctlr) == nil){
718*43f728cbSDavid du Colombier 		csr32w(ctlr, FhRxConfig, 0);
719*43f728cbSDavid du Colombier 		for(j = 0; j < 200; j++){
720*43f728cbSDavid du Colombier 			if(csr32r(ctlr, FhRxStatus) & 0x1000000)
721*43f728cbSDavid du Colombier 				break;
722*43f728cbSDavid du Colombier 			delay(10);
723*43f728cbSDavid du Colombier 		}
724*43f728cbSDavid du Colombier 		nicunlock(ctlr);
725*43f728cbSDavid du Colombier 	}
726*43f728cbSDavid du Colombier 
727*43f728cbSDavid du Colombier 	/* Disable DMA */
728*43f728cbSDavid du Colombier 	if(niclock(ctlr) == nil){
729*43f728cbSDavid du Colombier 		prphwrite(ctlr, ApmgClkDis, DmaClkRqt);
730*43f728cbSDavid du Colombier 		nicunlock(ctlr);
731*43f728cbSDavid du Colombier 	}
732*43f728cbSDavid du Colombier 	delay(5);
733*43f728cbSDavid du Colombier 
734*43f728cbSDavid du Colombier 	/* Stop busmaster DMA activity. */
735*43f728cbSDavid du Colombier 	csr32w(ctlr, Reset, csr32r(ctlr, Reset) | (1<<9));
736*43f728cbSDavid du Colombier 	for(j = 0; j < 100; j++){
737*43f728cbSDavid du Colombier 		if(csr32r(ctlr, Reset) & (1<<8))
738*43f728cbSDavid du Colombier 			break;
739*43f728cbSDavid du Colombier 		delay(10);
740*43f728cbSDavid du Colombier 	}
741*43f728cbSDavid du Colombier 
742*43f728cbSDavid du Colombier 	/* Reset the entire device. */
743*43f728cbSDavid du Colombier 	csr32w(ctlr, Reset, csr32r(ctlr, Reset) | (1<<7));
744*43f728cbSDavid du Colombier 	delay(10);
745*43f728cbSDavid du Colombier 
746*43f728cbSDavid du Colombier 	/* Clear "initialization complete" bit. */
747*43f728cbSDavid du Colombier 	csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) & ~InitDone);
748*43f728cbSDavid du Colombier 
749*43f728cbSDavid du Colombier 	ctlr->power = 0;
750*43f728cbSDavid du Colombier }
751*43f728cbSDavid du Colombier 
752*43f728cbSDavid du Colombier static char*
rominit(Ctlr * ctlr)753*43f728cbSDavid du Colombier rominit(Ctlr *ctlr)
754*43f728cbSDavid du Colombier {
755*43f728cbSDavid du Colombier 	uint prev, last;
756*43f728cbSDavid du Colombier 	uchar buf[2];
757*43f728cbSDavid du Colombier 	char *err;
758*43f728cbSDavid du Colombier 	int i;
759*43f728cbSDavid du Colombier 
760*43f728cbSDavid du Colombier 	ctlr->eeprom.otp = 0;
761*43f728cbSDavid du Colombier 	ctlr->eeprom.off = 0;
762*43f728cbSDavid du Colombier 	if(ctlr->type < Type1000 || (csr32r(ctlr, OtpromGp) & DevSelOtp) == 0)
763*43f728cbSDavid du Colombier 		return nil;
764*43f728cbSDavid du Colombier 
765*43f728cbSDavid du Colombier 	/* Wait for clock stabilization before accessing prph. */
766*43f728cbSDavid du Colombier 	if((err = clockwait(ctlr)) != nil)
767*43f728cbSDavid du Colombier 		return err;
768*43f728cbSDavid du Colombier 
769*43f728cbSDavid du Colombier 	if((err = niclock(ctlr)) != nil)
770*43f728cbSDavid du Colombier 		return err;
771*43f728cbSDavid du Colombier 	prphwrite(ctlr, ApmgPs, prphread(ctlr, ApmgPs) | ResetReq);
772*43f728cbSDavid du Colombier 	delay(5);
773*43f728cbSDavid du Colombier 	prphwrite(ctlr, ApmgPs, prphread(ctlr, ApmgPs) & ~ResetReq);
774*43f728cbSDavid du Colombier 	nicunlock(ctlr);
775*43f728cbSDavid du Colombier 
776*43f728cbSDavid du Colombier 	/* Set auto clock gate disable bit for HW with OTP shadow RAM. */
777*43f728cbSDavid du Colombier 	if(ctlr->type != Type1000)
778*43f728cbSDavid du Colombier 		csr32w(ctlr, Dbglinkpwrmgmt, csr32r(ctlr, Dbglinkpwrmgmt) | (1<<31));
779*43f728cbSDavid du Colombier 
780*43f728cbSDavid du Colombier 	csr32w(ctlr, EepromGp, csr32r(ctlr, EepromGp) & ~0x00000180);
781*43f728cbSDavid du Colombier 
782*43f728cbSDavid du Colombier 	/* Clear ECC status. */
783*43f728cbSDavid du Colombier 	csr32w(ctlr, OtpromGp, csr32r(ctlr, OtpromGp) | (EccCorrStts | EccUncorrStts));
784*43f728cbSDavid du Colombier 
785*43f728cbSDavid du Colombier 	ctlr->eeprom.otp = 1;
786*43f728cbSDavid du Colombier 	if(ctlr->type != Type1000)
787*43f728cbSDavid du Colombier 		return nil;
788*43f728cbSDavid du Colombier 
789*43f728cbSDavid du Colombier 	/* Switch to absolute addressing mode. */
790*43f728cbSDavid du Colombier 	csr32w(ctlr, OtpromGp, csr32r(ctlr, OtpromGp) & ~RelativeAccess);
791*43f728cbSDavid du Colombier 
792*43f728cbSDavid du Colombier 	/*
793*43f728cbSDavid du Colombier 	 * Find the block before last block (contains the EEPROM image)
794*43f728cbSDavid du Colombier 	 * for HW without OTP shadow RAM.
795*43f728cbSDavid du Colombier 	 */
796*43f728cbSDavid du Colombier 	prev = last = 0;
797*43f728cbSDavid du Colombier 	for(i=0; i<3; i++){
798*43f728cbSDavid du Colombier 		if((err = eepromread(ctlr, buf, 2, last)) != nil)
799*43f728cbSDavid du Colombier 			return err;
800*43f728cbSDavid du Colombier 		if(get16(buf) == 0)
801*43f728cbSDavid du Colombier 			break;
802*43f728cbSDavid du Colombier 		prev = last;
803*43f728cbSDavid du Colombier 		last = get16(buf);
804*43f728cbSDavid du Colombier 	}
805*43f728cbSDavid du Colombier 	if(i == 0 || i >= 3)
806*43f728cbSDavid du Colombier 		return "rominit: missing eeprom image";
807*43f728cbSDavid du Colombier 
808*43f728cbSDavid du Colombier 	ctlr->eeprom.off = prev+1;
809*43f728cbSDavid du Colombier 	return nil;
810*43f728cbSDavid du Colombier }
811*43f728cbSDavid du Colombier 
812*43f728cbSDavid du Colombier static int
iwlinit(Ether * edev)813*43f728cbSDavid du Colombier iwlinit(Ether *edev)
814*43f728cbSDavid du Colombier {
815*43f728cbSDavid du Colombier 	Ctlr *ctlr;
816*43f728cbSDavid du Colombier 	char *err;
817*43f728cbSDavid du Colombier 	uchar b[4];
818*43f728cbSDavid du Colombier 	uint u, caloff, regoff;
819*43f728cbSDavid du Colombier 
820*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
821*43f728cbSDavid du Colombier 	if((err = handover(ctlr)) != nil)
822*43f728cbSDavid du Colombier 		goto Err;
823*43f728cbSDavid du Colombier 	if((err = poweron(ctlr)) != nil)
824*43f728cbSDavid du Colombier 		goto Err;
825*43f728cbSDavid du Colombier 	if((csr32r(ctlr, EepromGp) & 0x7) == 0){
826*43f728cbSDavid du Colombier 		err = "bad rom signature";
827*43f728cbSDavid du Colombier 		goto Err;
828*43f728cbSDavid du Colombier 	}
829*43f728cbSDavid du Colombier 	if((err = eepromlock(ctlr)) != nil)
830*43f728cbSDavid du Colombier 		goto Err;
831*43f728cbSDavid du Colombier 	if((err = rominit(ctlr)) != nil)
832*43f728cbSDavid du Colombier 		goto Err2;
833*43f728cbSDavid du Colombier 	if((err = eepromread(ctlr, edev->ea, sizeof(edev->ea), 0x15)) != nil){
834*43f728cbSDavid du Colombier 		eepromunlock(ctlr);
835*43f728cbSDavid du Colombier 		goto Err;
836*43f728cbSDavid du Colombier 	}
837*43f728cbSDavid du Colombier 	if((err = eepromread(ctlr, b, 2, 0x048)) != nil){
838*43f728cbSDavid du Colombier 	Err2:
839*43f728cbSDavid du Colombier 		eepromunlock(ctlr);
840*43f728cbSDavid du Colombier 		goto Err;
841*43f728cbSDavid du Colombier 	}
842*43f728cbSDavid du Colombier 	u = get16(b);
843*43f728cbSDavid du Colombier 	ctlr->rfcfg.type = u & 3;	u >>= 2;
844*43f728cbSDavid du Colombier 	ctlr->rfcfg.step = u & 3;	u >>= 2;
845*43f728cbSDavid du Colombier 	ctlr->rfcfg.dash = u & 3;	u >>= 4;
846*43f728cbSDavid du Colombier 	ctlr->rfcfg.txantmask = u & 15;	u >>= 4;
847*43f728cbSDavid du Colombier 	ctlr->rfcfg.rxantmask = u & 15;
848*43f728cbSDavid du Colombier 	if((err = eepromread(ctlr, b, 2, 0x66)) != nil)
849*43f728cbSDavid du Colombier 		goto Err2;
850*43f728cbSDavid du Colombier 	regoff = get16(b);
851*43f728cbSDavid du Colombier 	if((err = eepromread(ctlr, b, 4, regoff+1)) != nil)
852*43f728cbSDavid du Colombier 		goto Err2;
853*43f728cbSDavid du Colombier 	strncpy(ctlr->eeprom.regdom, (char*)b, 4);
854*43f728cbSDavid du Colombier 	ctlr->eeprom.regdom[4] = 0;
855*43f728cbSDavid du Colombier 	if((err = eepromread(ctlr, b, 2, 0x67)) != nil)
856*43f728cbSDavid du Colombier 		goto Err2;
857*43f728cbSDavid du Colombier 	caloff = get16(b);
858*43f728cbSDavid du Colombier 	if((err = eepromread(ctlr, b, 4, caloff)) != nil)
859*43f728cbSDavid du Colombier 		goto Err2;
860*43f728cbSDavid du Colombier 	ctlr->eeprom.version = b[0];
861*43f728cbSDavid du Colombier 	ctlr->eeprom.type = b[1];
862*43f728cbSDavid du Colombier 	ctlr->eeprom.volt = get16(b+2);
863*43f728cbSDavid du Colombier 
864*43f728cbSDavid du Colombier 	ctlr->eeprom.temp = 0;
865*43f728cbSDavid du Colombier 	ctlr->eeprom.rawtemp = 0;
866*43f728cbSDavid du Colombier 	if(ctlr->type == Type2030){
867*43f728cbSDavid du Colombier 		if((err = eepromread(ctlr, b, 2, caloff + 0x12a)) != nil)
868*43f728cbSDavid du Colombier 			goto Err2;
869*43f728cbSDavid du Colombier 		ctlr->eeprom.temp = get16(b);
870*43f728cbSDavid du Colombier 		if((err = eepromread(ctlr, b, 2, caloff + 0x12b)) != nil)
871*43f728cbSDavid du Colombier 			goto Err2;
872*43f728cbSDavid du Colombier 		ctlr->eeprom.rawtemp = get16(b);
873*43f728cbSDavid du Colombier 	}
874*43f728cbSDavid du Colombier 
875*43f728cbSDavid du Colombier 	if(ctlr->type != Type4965 && ctlr->type != Type5150){
876*43f728cbSDavid du Colombier 		if((err = eepromread(ctlr, b, 4, caloff + 0x128)) != nil)
877*43f728cbSDavid du Colombier 			goto Err2;
878*43f728cbSDavid du Colombier 		ctlr->eeprom.crystal = get32(b);
879*43f728cbSDavid du Colombier 	}
880*43f728cbSDavid du Colombier 	eepromunlock(ctlr);
881*43f728cbSDavid du Colombier 
882*43f728cbSDavid du Colombier 	switch(ctlr->type){
883*43f728cbSDavid du Colombier 	case Type4965:
884*43f728cbSDavid du Colombier 		ctlr->rfcfg.txantmask = 3;
885*43f728cbSDavid du Colombier 		ctlr->rfcfg.rxantmask = 7;
886*43f728cbSDavid du Colombier 		break;
887*43f728cbSDavid du Colombier 	case Type5100:
888*43f728cbSDavid du Colombier 		ctlr->rfcfg.txantmask = 2;
889*43f728cbSDavid du Colombier 		ctlr->rfcfg.rxantmask = 3;
890*43f728cbSDavid du Colombier 		break;
891*43f728cbSDavid du Colombier 	case Type6000:
892*43f728cbSDavid du Colombier 		if(ctlr->pdev->did == 0x422c || ctlr->pdev->did == 0x4230){
893*43f728cbSDavid du Colombier 			ctlr->rfcfg.txantmask = 6;
894*43f728cbSDavid du Colombier 			ctlr->rfcfg.rxantmask = 6;
895*43f728cbSDavid du Colombier 		}
896*43f728cbSDavid du Colombier 		break;
897*43f728cbSDavid du Colombier 	}
898*43f728cbSDavid du Colombier 	poweroff(ctlr);
899*43f728cbSDavid du Colombier 	return 0;
900*43f728cbSDavid du Colombier Err:
901*43f728cbSDavid du Colombier 	print("iwlinit: %s\n", err);
902*43f728cbSDavid du Colombier 	poweroff(ctlr);
903*43f728cbSDavid du Colombier 	return -1;
904*43f728cbSDavid du Colombier }
905*43f728cbSDavid du Colombier 
906*43f728cbSDavid du Colombier static char*
crackfw(FWImage * i,uchar * data,uint size,int alt)907*43f728cbSDavid du Colombier crackfw(FWImage *i, uchar *data, uint size, int alt)
908*43f728cbSDavid du Colombier {
909*43f728cbSDavid du Colombier 	uchar *p, *e;
910*43f728cbSDavid du Colombier 	FWSect *s;
911*43f728cbSDavid du Colombier 
912*43f728cbSDavid du Colombier 	memset(i, 0, sizeof(*i));
913*43f728cbSDavid du Colombier 	if(size < 4){
914*43f728cbSDavid du Colombier Tooshort:
915*43f728cbSDavid du Colombier 		return "firmware image too short";
916*43f728cbSDavid du Colombier 	}
917*43f728cbSDavid du Colombier 	p = data;
918*43f728cbSDavid du Colombier 	e = p + size;
919*43f728cbSDavid du Colombier 	i->rev = get32(p); p += 4;
920*43f728cbSDavid du Colombier 	if(i->rev == 0){
921*43f728cbSDavid du Colombier 		uvlong altmask;
922*43f728cbSDavid du Colombier 
923*43f728cbSDavid du Colombier 		if(size < (4+64+4+4+8))
924*43f728cbSDavid du Colombier 			goto Tooshort;
925*43f728cbSDavid du Colombier 		if(memcmp(p, "IWL\n", 4) != 0)
926*43f728cbSDavid du Colombier 			return "bad firmware signature";
927*43f728cbSDavid du Colombier 		p += 4;
928*43f728cbSDavid du Colombier 		strncpy(i->descr, (char*)p, 64);
929*43f728cbSDavid du Colombier 		i->descr[64] = 0;
930*43f728cbSDavid du Colombier 		p += 64;
931*43f728cbSDavid du Colombier 		i->rev = get32(p); p += 4;
932*43f728cbSDavid du Colombier 		i->build = get32(p); p += 4;
933*43f728cbSDavid du Colombier 		altmask = get32(p); p += 4;
934*43f728cbSDavid du Colombier 		altmask |= (uvlong)get32(p) << 32; p += 4;
935*43f728cbSDavid du Colombier 		while(alt > 0 && (altmask & (1ULL<<alt)) == 0)
936*43f728cbSDavid du Colombier 			alt--;
937*43f728cbSDavid du Colombier 		while(p < e){
938*43f728cbSDavid du Colombier 			FWSect dummy;
939*43f728cbSDavid du Colombier 
940*43f728cbSDavid du Colombier 			if((p + 2+2+4) > e)
941*43f728cbSDavid du Colombier 				goto Tooshort;
942*43f728cbSDavid du Colombier 			switch(get16(p)){
943*43f728cbSDavid du Colombier 			case 1:	s = &i->main.text; break;
944*43f728cbSDavid du Colombier 			case 2: s = &i->main.data; break;
945*43f728cbSDavid du Colombier 			case 3: s = &i->init.text; break;
946*43f728cbSDavid du Colombier 			case 4: s = &i->init.data; break;
947*43f728cbSDavid du Colombier 			case 5: s = &i->boot.text; break;
948*43f728cbSDavid du Colombier 			default:s = &dummy;
949*43f728cbSDavid du Colombier 			}
950*43f728cbSDavid du Colombier 			p += 2;
951*43f728cbSDavid du Colombier 			if(get16(p) != 0 && get16(p) != alt)
952*43f728cbSDavid du Colombier 				s = &dummy;
953*43f728cbSDavid du Colombier 			p += 2;
954*43f728cbSDavid du Colombier 			s->size = get32(p); p += 4;
955*43f728cbSDavid du Colombier 			s->data = p;
956*43f728cbSDavid du Colombier 			if((p + s->size) > e)
957*43f728cbSDavid du Colombier 				goto Tooshort;
958*43f728cbSDavid du Colombier 			p += (s->size + 3) & ~3;
959*43f728cbSDavid du Colombier 		}
960*43f728cbSDavid du Colombier 	} else {
961*43f728cbSDavid du Colombier 		if(((i->rev>>8) & 0xFF) < 2)
962*43f728cbSDavid du Colombier 			return "need firmware api >= 2";
963*43f728cbSDavid du Colombier 		if(((i->rev>>8) & 0xFF) >= 3){
964*43f728cbSDavid du Colombier 			i->build = get32(p); p += 4;
965*43f728cbSDavid du Colombier 		}
966*43f728cbSDavid du Colombier 		if((p + 5*4) > e)
967*43f728cbSDavid du Colombier 			goto Tooshort;
968*43f728cbSDavid du Colombier 		i->main.text.size = get32(p); p += 4;
969*43f728cbSDavid du Colombier 		i->main.data.size = get32(p); p += 4;
970*43f728cbSDavid du Colombier 		i->init.text.size = get32(p); p += 4;
971*43f728cbSDavid du Colombier 		i->init.data.size = get32(p); p += 4;
972*43f728cbSDavid du Colombier 		i->boot.text.size = get32(p); p += 4;
973*43f728cbSDavid du Colombier 		i->main.text.data = p; p += i->main.text.size;
974*43f728cbSDavid du Colombier 		i->main.data.data = p; p += i->main.data.size;
975*43f728cbSDavid du Colombier 		i->init.text.data = p; p += i->init.text.size;
976*43f728cbSDavid du Colombier 		i->init.data.data = p; p += i->init.data.size;
977*43f728cbSDavid du Colombier 		i->boot.text.data = p; p += i->boot.text.size;
978*43f728cbSDavid du Colombier 		if(p > e)
979*43f728cbSDavid du Colombier 			goto Tooshort;
980*43f728cbSDavid du Colombier 	}
981*43f728cbSDavid du Colombier 	return 0;
982*43f728cbSDavid du Colombier }
983*43f728cbSDavid du Colombier 
984*43f728cbSDavid du Colombier static FWImage*
readfirmware(char * name)985*43f728cbSDavid du Colombier readfirmware(char *name)
986*43f728cbSDavid du Colombier {
987*43f728cbSDavid du Colombier 	uchar dirbuf[sizeof(Dir)+100], *data;
988*43f728cbSDavid du Colombier 	char buf[128], *err;
989*43f728cbSDavid du Colombier 	FWImage *fw;
990*43f728cbSDavid du Colombier 	int n, r;
991*43f728cbSDavid du Colombier 	Chan *c;
992*43f728cbSDavid du Colombier 	Dir d;
993*43f728cbSDavid du Colombier 
994*43f728cbSDavid du Colombier 	if(!iseve())
995*43f728cbSDavid du Colombier 		error(Eperm);
996*43f728cbSDavid du Colombier 	if(!waserror()){
997*43f728cbSDavid du Colombier 		snprint(buf, sizeof buf, "/boot/%s", name);
998*43f728cbSDavid du Colombier 		c = namec(buf, Aopen, OREAD, 0);
999*43f728cbSDavid du Colombier 		poperror();
1000*43f728cbSDavid du Colombier 	} else {
1001*43f728cbSDavid du Colombier 		snprint(buf, sizeof buf, "/lib/firmware/%s", name);
1002*43f728cbSDavid du Colombier 		c = namec(buf, Aopen, OREAD, 0);
1003*43f728cbSDavid du Colombier 	}
1004*43f728cbSDavid du Colombier 	if(waserror()){
1005*43f728cbSDavid du Colombier 		cclose(c);
1006*43f728cbSDavid du Colombier 		nexterror();
1007*43f728cbSDavid du Colombier 	}
1008*43f728cbSDavid du Colombier 	n = devtab[c->type]->stat(c, dirbuf, sizeof dirbuf);
1009*43f728cbSDavid du Colombier 	if(n <= 0)
1010*43f728cbSDavid du Colombier 		error("can't stat firmware");
1011*43f728cbSDavid du Colombier 	convM2D(dirbuf, n, &d, nil);
1012*43f728cbSDavid du Colombier 	fw = smalloc(sizeof(*fw) + 16 + d.length);
1013*43f728cbSDavid du Colombier 	data = (uchar*)(fw+1);
1014*43f728cbSDavid du Colombier 	if(waserror()){
1015*43f728cbSDavid du Colombier 		free(fw);
1016*43f728cbSDavid du Colombier 		nexterror();
1017*43f728cbSDavid du Colombier 	}
1018*43f728cbSDavid du Colombier 	r = 0;
1019*43f728cbSDavid du Colombier 	while(r < d.length){
1020*43f728cbSDavid du Colombier 		n = devtab[c->type]->read(c, data+r, d.length-r, (vlong)r);
1021*43f728cbSDavid du Colombier 		if(n <= 0)
1022*43f728cbSDavid du Colombier 			break;
1023*43f728cbSDavid du Colombier 		r += n;
1024*43f728cbSDavid du Colombier 	}
1025*43f728cbSDavid du Colombier 	if((err = crackfw(fw, data, r, 1)) != nil)
1026*43f728cbSDavid du Colombier 		error(err);
1027*43f728cbSDavid du Colombier 	poperror();
1028*43f728cbSDavid du Colombier 	poperror();
1029*43f728cbSDavid du Colombier 	cclose(c);
1030*43f728cbSDavid du Colombier 	return fw;
1031*43f728cbSDavid du Colombier }
1032*43f728cbSDavid du Colombier 
1033*43f728cbSDavid du Colombier 
1034*43f728cbSDavid du Colombier static int
gotirq(void * arg)1035*43f728cbSDavid du Colombier gotirq(void *arg)
1036*43f728cbSDavid du Colombier {
1037*43f728cbSDavid du Colombier 	Ctlr *ctlr = arg;
1038*43f728cbSDavid du Colombier 	return (ctlr->wait.m & ctlr->wait.w) != 0;
1039*43f728cbSDavid du Colombier }
1040*43f728cbSDavid du Colombier 
1041*43f728cbSDavid du Colombier static u32int
irqwait(Ctlr * ctlr,u32int mask,int timeout)1042*43f728cbSDavid du Colombier irqwait(Ctlr *ctlr, u32int mask, int timeout)
1043*43f728cbSDavid du Colombier {
1044*43f728cbSDavid du Colombier 	u32int r;
1045*43f728cbSDavid du Colombier 
1046*43f728cbSDavid du Colombier 	ilock(ctlr);
1047*43f728cbSDavid du Colombier 	r = ctlr->wait.m & mask;
1048*43f728cbSDavid du Colombier 	if(r == 0){
1049*43f728cbSDavid du Colombier 		ctlr->wait.w = mask;
1050*43f728cbSDavid du Colombier 		iunlock(ctlr);
1051*43f728cbSDavid du Colombier 		if(!waserror()){
1052*43f728cbSDavid du Colombier 			tsleep(&ctlr->wait, gotirq, ctlr, timeout);
1053*43f728cbSDavid du Colombier 			poperror();
1054*43f728cbSDavid du Colombier 		}
1055*43f728cbSDavid du Colombier 		ilock(ctlr);
1056*43f728cbSDavid du Colombier 		ctlr->wait.w = 0;
1057*43f728cbSDavid du Colombier 		r = ctlr->wait.m & mask;
1058*43f728cbSDavid du Colombier 	}
1059*43f728cbSDavid du Colombier 	ctlr->wait.m &= ~r;
1060*43f728cbSDavid du Colombier 	iunlock(ctlr);
1061*43f728cbSDavid du Colombier 	return r;
1062*43f728cbSDavid du Colombier }
1063*43f728cbSDavid du Colombier 
1064*43f728cbSDavid du Colombier static int
rbplant(Ctlr * ctlr,int i)1065*43f728cbSDavid du Colombier rbplant(Ctlr *ctlr, int i)
1066*43f728cbSDavid du Colombier {
1067*43f728cbSDavid du Colombier 	Block *b;
1068*43f728cbSDavid du Colombier 
1069*43f728cbSDavid du Colombier 	b = iallocb(Rbufsize + 256);
1070*43f728cbSDavid du Colombier 	if(b == nil)
1071*43f728cbSDavid du Colombier 		return -1;
1072*43f728cbSDavid du Colombier 	b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, 256);
1073*43f728cbSDavid du Colombier 	memset(b->rp, 0, Rdscsize);
1074*43f728cbSDavid du Colombier 	ctlr->rx.b[i] = b;
1075*43f728cbSDavid du Colombier 	ctlr->rx.p[i] = PCIWADDR(b->rp) >> 8;
1076*43f728cbSDavid du Colombier 	return 0;
1077*43f728cbSDavid du Colombier }
1078*43f728cbSDavid du Colombier 
1079*43f728cbSDavid du Colombier static char*
initring(Ctlr * ctlr)1080*43f728cbSDavid du Colombier initring(Ctlr *ctlr)
1081*43f728cbSDavid du Colombier {
1082*43f728cbSDavid du Colombier 	RXQ *rx;
1083*43f728cbSDavid du Colombier 	TXQ *tx;
1084*43f728cbSDavid du Colombier 	int i, q;
1085*43f728cbSDavid du Colombier 
1086*43f728cbSDavid du Colombier 	rx = &ctlr->rx;
1087*43f728cbSDavid du Colombier 	if(rx->b == nil)
1088*43f728cbSDavid du Colombier 		rx->b = malloc(sizeof(Block*) * Nrx);
1089*43f728cbSDavid du Colombier 	if(rx->p == nil)
1090*43f728cbSDavid du Colombier 		rx->p = mallocalign(sizeof(u32int) * Nrx, 256, 0, 0);
1091*43f728cbSDavid du Colombier 	if(rx->s == nil)
1092*43f728cbSDavid du Colombier 		rx->s = mallocalign(Rstatsize, 16, 0, 0);
1093*43f728cbSDavid du Colombier 	if(rx->b == nil || rx->p == nil || rx->s == nil)
1094*43f728cbSDavid du Colombier 		return "no memory for rx ring";
1095*43f728cbSDavid du Colombier 	memset(ctlr->rx.s, 0, Rstatsize);
1096*43f728cbSDavid du Colombier 	for(i=0; i<Nrx; i++){
1097*43f728cbSDavid du Colombier 		rx->p[i] = 0;
1098*43f728cbSDavid du Colombier 		if(rx->b[i] != nil){
1099*43f728cbSDavid du Colombier 			freeb(rx->b[i]);
1100*43f728cbSDavid du Colombier 			rx->b[i] = nil;
1101*43f728cbSDavid du Colombier 		}
1102*43f728cbSDavid du Colombier 		if(rbplant(ctlr, i) < 0)
1103*43f728cbSDavid du Colombier 			return "no memory for rx descriptors";
1104*43f728cbSDavid du Colombier 	}
1105*43f728cbSDavid du Colombier 	rx->i = 0;
1106*43f728cbSDavid du Colombier 
1107*43f728cbSDavid du Colombier 	if(ctlr->sched.s == nil)
1108*43f728cbSDavid du Colombier 		ctlr->sched.s = mallocalign(512 * nelem(ctlr->tx) * 2, 1024, 0, 0);
1109*43f728cbSDavid du Colombier 	if(ctlr->sched.s == nil)
1110*43f728cbSDavid du Colombier 		return "no memory for sched buffer";
1111*43f728cbSDavid du Colombier 	memset(ctlr->sched.s, 0, 512 * nelem(ctlr->tx));
1112*43f728cbSDavid du Colombier 
1113*43f728cbSDavid du Colombier 	for(q=0; q<nelem(ctlr->tx); q++){
1114*43f728cbSDavid du Colombier 		tx = &ctlr->tx[q];
1115*43f728cbSDavid du Colombier 		if(tx->b == nil)
1116*43f728cbSDavid du Colombier 			tx->b = malloc(sizeof(Block*) * Ntx);
1117*43f728cbSDavid du Colombier 		if(tx->d == nil)
1118*43f728cbSDavid du Colombier 			tx->d = mallocalign(Tdscsize * Ntx, 256, 0, 0);
1119*43f728cbSDavid du Colombier 		if(tx->c == nil)
1120*43f728cbSDavid du Colombier 			tx->c = mallocalign(Tcmdsize * Ntx, 4, 0, 0);
1121*43f728cbSDavid du Colombier 		if(tx->b == nil || tx->d == nil || tx->c == nil)
1122*43f728cbSDavid du Colombier 			return "no memory for tx ring";
1123*43f728cbSDavid du Colombier 		memset(tx->d, 0, Tdscsize * Ntx);
1124*43f728cbSDavid du Colombier 		memset(tx->c, 0, Tcmdsize * Ntx);
1125*43f728cbSDavid du Colombier 		for(i=0; i<Ntx; i++){
1126*43f728cbSDavid du Colombier 			if(tx->b[i] != nil){
1127*43f728cbSDavid du Colombier 				freeb(tx->b[i]);
1128*43f728cbSDavid du Colombier 				tx->b[i] = nil;
1129*43f728cbSDavid du Colombier 			}
1130*43f728cbSDavid du Colombier 		}
1131*43f728cbSDavid du Colombier 		tx->i = 0;
1132*43f728cbSDavid du Colombier 		tx->n = 0;
1133*43f728cbSDavid du Colombier 		tx->lastcmd = 0;
1134*43f728cbSDavid du Colombier 	}
1135*43f728cbSDavid du Colombier 
1136*43f728cbSDavid du Colombier 	if(ctlr->kwpage == nil)
1137*43f728cbSDavid du Colombier 		ctlr->kwpage = mallocalign(4096, 4096, 0, 0);
1138*43f728cbSDavid du Colombier 	if(ctlr->kwpage == nil)
1139*43f728cbSDavid du Colombier 		return "no memory for kwpage";
1140*43f728cbSDavid du Colombier 	memset(ctlr->kwpage, 0, 4096);
1141*43f728cbSDavid du Colombier 
1142*43f728cbSDavid du Colombier 	return nil;
1143*43f728cbSDavid du Colombier }
1144*43f728cbSDavid du Colombier 
1145*43f728cbSDavid du Colombier static char*
reset(Ctlr * ctlr)1146*43f728cbSDavid du Colombier reset(Ctlr *ctlr)
1147*43f728cbSDavid du Colombier {
1148*43f728cbSDavid du Colombier 	char *err;
1149*43f728cbSDavid du Colombier 	int i, q;
1150*43f728cbSDavid du Colombier 
1151*43f728cbSDavid du Colombier 	if(ctlr->power)
1152*43f728cbSDavid du Colombier 		poweroff(ctlr);
1153*43f728cbSDavid du Colombier 	if((err = initring(ctlr)) != nil)
1154*43f728cbSDavid du Colombier 		return err;
1155*43f728cbSDavid du Colombier 	if((err = poweron(ctlr)) != nil)
1156*43f728cbSDavid du Colombier 		return err;
1157*43f728cbSDavid du Colombier 
1158*43f728cbSDavid du Colombier 	if((err = niclock(ctlr)) != nil)
1159*43f728cbSDavid du Colombier 		return err;
1160*43f728cbSDavid du Colombier 	prphwrite(ctlr, ApmgPs, (prphread(ctlr, ApmgPs) & ~PwrSrcMask) | PwrSrcVMain);
1161*43f728cbSDavid du Colombier 	nicunlock(ctlr);
1162*43f728cbSDavid du Colombier 
1163*43f728cbSDavid du Colombier 	csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | RadioSi | MacSi);
1164*43f728cbSDavid du Colombier 
1165*43f728cbSDavid du Colombier 	if((err = niclock(ctlr)) != nil)
1166*43f728cbSDavid du Colombier 		return err;
1167*43f728cbSDavid du Colombier 	if(ctlr->type != Type4965)
1168*43f728cbSDavid du Colombier 		prphwrite(ctlr, ApmgPs, prphread(ctlr, ApmgPs) | EarlyPwroffDis);
1169*43f728cbSDavid du Colombier 	if(ctlr->type == Type1000){
1170*43f728cbSDavid du Colombier 		/*
1171*43f728cbSDavid du Colombier 		 * Select first Switching Voltage Regulator (1.32V) to
1172*43f728cbSDavid du Colombier 		 * solve a stability issue related to noisy DC2DC line
1173*43f728cbSDavid du Colombier 		 * in the silicon of 1000 Series.
1174*43f728cbSDavid du Colombier 		 */
1175*43f728cbSDavid du Colombier 		prphwrite(ctlr, ApmgDigitalSvr,
1176*43f728cbSDavid du Colombier 			(prphread(ctlr, ApmgDigitalSvr) & ~(0xf<<5)) | (3<<5));
1177*43f728cbSDavid du Colombier 	}
1178*43f728cbSDavid du Colombier 	nicunlock(ctlr);
1179*43f728cbSDavid du Colombier 
1180*43f728cbSDavid du Colombier 	if((err = niclock(ctlr)) != nil)
1181*43f728cbSDavid du Colombier 		return err;
1182*43f728cbSDavid du Colombier 	if((ctlr->type == Type6005 || ctlr->type == Type6050) && ctlr->eeprom.version == 6)
1183*43f728cbSDavid du Colombier 		csr32w(ctlr, GpDrv, csr32r(ctlr, GpDrv) | GpDrvCalV6);
1184*43f728cbSDavid du Colombier 	if(ctlr->type == Type6005)
1185*43f728cbSDavid du Colombier 		csr32w(ctlr, GpDrv, csr32r(ctlr, GpDrv) | GpDrv1X2);
1186*43f728cbSDavid du Colombier 	if(ctlr->type == Type2030)
1187*43f728cbSDavid du Colombier 		csr32w(ctlr, GpDrv, csr32r(ctlr, GpDrv) | GpDrvRadioIqInvert);
1188*43f728cbSDavid du Colombier 	nicunlock(ctlr);
1189*43f728cbSDavid du Colombier 
1190*43f728cbSDavid du Colombier 	if((err = niclock(ctlr)) != nil)
1191*43f728cbSDavid du Colombier 		return err;
1192*43f728cbSDavid du Colombier 	csr32w(ctlr, FhRxConfig, 0);
1193*43f728cbSDavid du Colombier 	csr32w(ctlr, FhRxWptr, 0);
1194*43f728cbSDavid du Colombier 	csr32w(ctlr, FhRxBase, PCIWADDR(ctlr->rx.p) >> 8);
1195*43f728cbSDavid du Colombier 	csr32w(ctlr, FhStatusWptr, PCIWADDR(ctlr->rx.s) >> 4);
1196*43f728cbSDavid du Colombier 	csr32w(ctlr, FhRxConfig,
1197*43f728cbSDavid du Colombier 		FhRxConfigEna |
1198*43f728cbSDavid du Colombier 		FhRxConfigIgnRxfEmpty |
1199*43f728cbSDavid du Colombier 		FhRxConfigIrqDstHost |
1200*43f728cbSDavid du Colombier 		FhRxConfigSingleFrame |
1201*43f728cbSDavid du Colombier 		(Nrxlog << FhRxConfigNrbdShift));
1202*43f728cbSDavid du Colombier 	csr32w(ctlr, FhRxWptr, (Nrx-1) & ~7);
1203*43f728cbSDavid du Colombier 	nicunlock(ctlr);
1204*43f728cbSDavid du Colombier 
1205*43f728cbSDavid du Colombier 	if((err = niclock(ctlr)) != nil)
1206*43f728cbSDavid du Colombier 		return err;
1207*43f728cbSDavid du Colombier 	if(ctlr->type != Type4965)
1208*43f728cbSDavid du Colombier 		prphwrite(ctlr, SchedTxFact5000, 0);
1209*43f728cbSDavid du Colombier 	else
1210*43f728cbSDavid du Colombier 		prphwrite(ctlr, SchedTxFact4965, 0);
1211*43f728cbSDavid du Colombier 	csr32w(ctlr, FhKwAddr, PCIWADDR(ctlr->kwpage) >> 4);
1212*43f728cbSDavid du Colombier 	for(q = (ctlr->type != Type4965) ? 19 : 15; q >= 0; q--)
1213*43f728cbSDavid du Colombier 		csr32w(ctlr, FhCbbcQueue + q*4, PCIWADDR(ctlr->tx[q].d) >> 8);
1214*43f728cbSDavid du Colombier 	nicunlock(ctlr);
1215*43f728cbSDavid du Colombier 
1216*43f728cbSDavid du Colombier 	for(i = (ctlr->type != Type4965) ? 7 : 6; i >= 0; i--)
1217*43f728cbSDavid du Colombier 		csr32w(ctlr, FhTxConfig + i*32, FhTxConfigDmaEna | FhTxConfigDmaCreditEna);
1218*43f728cbSDavid du Colombier 
1219*43f728cbSDavid du Colombier 	csr32w(ctlr, UcodeGp1Clr, UcodeGp1RfKill);
1220*43f728cbSDavid du Colombier 	csr32w(ctlr, UcodeGp1Clr, UcodeGp1CmdBlocked);
1221*43f728cbSDavid du Colombier 
1222*43f728cbSDavid du Colombier 	ctlr->broken = 0;
1223*43f728cbSDavid du Colombier 	ctlr->wait.m = 0;
1224*43f728cbSDavid du Colombier 	ctlr->wait.w = 0;
1225*43f728cbSDavid du Colombier 
1226*43f728cbSDavid du Colombier 	ctlr->ie = Idefmask;
1227*43f728cbSDavid du Colombier 	csr32w(ctlr, Imr, ctlr->ie);
1228*43f728cbSDavid du Colombier 	csr32w(ctlr, Isr, ~0);
1229*43f728cbSDavid du Colombier 
1230*43f728cbSDavid du Colombier 	if(ctlr->type >= Type6000)
1231*43f728cbSDavid du Colombier 		csr32w(ctlr, ShadowRegCtrl, csr32r(ctlr, ShadowRegCtrl) | 0x800fffff);
1232*43f728cbSDavid du Colombier 
1233*43f728cbSDavid du Colombier 	return nil;
1234*43f728cbSDavid du Colombier }
1235*43f728cbSDavid du Colombier 
1236*43f728cbSDavid du Colombier static char*
sendbtcoexadv(Ctlr * ctlr)1237*43f728cbSDavid du Colombier sendbtcoexadv(Ctlr *ctlr)
1238*43f728cbSDavid du Colombier {
1239*43f728cbSDavid du Colombier 	static u32int btcoex3wire[12] = {
1240*43f728cbSDavid du Colombier 		0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
1241*43f728cbSDavid du Colombier 		0xcc00ff28,	0x0000aaaa, 0xcc00aaaa, 0x0000aaaa,
1242*43f728cbSDavid du Colombier 		0xc0004000, 0x00004000, 0xf0005000, 0xf0005000,
1243*43f728cbSDavid du Colombier 	};
1244*43f728cbSDavid du Colombier 
1245*43f728cbSDavid du Colombier 	uchar c[Tcmdsize], *p;
1246*43f728cbSDavid du Colombier 	char *err;
1247*43f728cbSDavid du Colombier 	int i;
1248*43f728cbSDavid du Colombier 
1249*43f728cbSDavid du Colombier 	/* set BT config */
1250*43f728cbSDavid du Colombier 	memset(c, 0, sizeof(c));
1251*43f728cbSDavid du Colombier 	p = c;
1252*43f728cbSDavid du Colombier 
1253*43f728cbSDavid du Colombier 	if(ctlr->type == Type2030){
1254*43f728cbSDavid du Colombier 		*p++ = 145; /* flags */
1255*43f728cbSDavid du Colombier 		p++; /* lead time */
1256*43f728cbSDavid du Colombier 		*p++ = 5; /* max kill */
1257*43f728cbSDavid du Colombier 		*p++ = 1; /* bt3 t7 timer */
1258*43f728cbSDavid du Colombier 		put32(p, 0xffff0000); /* kill ack */
1259*43f728cbSDavid du Colombier 		p += 4;
1260*43f728cbSDavid du Colombier 		put32(p, 0xffff0000); /* kill cts */
1261*43f728cbSDavid du Colombier 		p += 4;
1262*43f728cbSDavid du Colombier 		*p++ = 2; /* sample time */
1263*43f728cbSDavid du Colombier 		*p++ = 0xc; /* bt3 t2 timer */
1264*43f728cbSDavid du Colombier 		p += 2; /* bt4 reaction */
1265*43f728cbSDavid du Colombier 		for (i = 0; i < nelem(btcoex3wire); i++){
1266*43f728cbSDavid du Colombier 			put32(p, btcoex3wire[i]);
1267*43f728cbSDavid du Colombier 			p += 4;
1268*43f728cbSDavid du Colombier 		}
1269*43f728cbSDavid du Colombier 		p += 2; /* bt4 decision */
1270*43f728cbSDavid du Colombier 		put16(p, 0xff); /* valid */
1271*43f728cbSDavid du Colombier 		p += 2;
1272*43f728cbSDavid du Colombier 		put32(p, 0xf0); /* prio boost */
1273*43f728cbSDavid du Colombier 		p += 4;
1274*43f728cbSDavid du Colombier 		p++; /* reserved */
1275*43f728cbSDavid du Colombier 		p++; /* tx prio boost */
1276*43f728cbSDavid du Colombier 		p += 2; /* rx prio boost */
1277*43f728cbSDavid du Colombier 	}
1278*43f728cbSDavid du Colombier 	if((err = cmd(ctlr, 155, c, p-c)) != nil)
1279*43f728cbSDavid du Colombier 		return err;
1280*43f728cbSDavid du Colombier 
1281*43f728cbSDavid du Colombier 	/* set BT priority */
1282*43f728cbSDavid du Colombier 	memset(c, 0, sizeof(c));
1283*43f728cbSDavid du Colombier 	p = c;
1284*43f728cbSDavid du Colombier 
1285*43f728cbSDavid du Colombier 	*p++ = 0x6; /* init1 */
1286*43f728cbSDavid du Colombier 	*p++ = 0x7; /* init2 */
1287*43f728cbSDavid du Colombier 	*p++ = 0x2; /* periodic low1 */
1288*43f728cbSDavid du Colombier 	*p++ = 0x3; /* periodic low2 */
1289*43f728cbSDavid du Colombier 	*p++ = 0x4; /* periodic high1 */
1290*43f728cbSDavid du Colombier 	*p++ = 0x5; /* periodic high2 */
1291*43f728cbSDavid du Colombier 	*p++ = 0x6; /* dtim */
1292*43f728cbSDavid du Colombier 	*p++ = 0x8; /* scan52 */
1293*43f728cbSDavid du Colombier 	*p++ = 0xa; /* scan24 */
1294*43f728cbSDavid du Colombier 	p += 7; /* reserved */
1295*43f728cbSDavid du Colombier 	if((err = cmd(ctlr, 204, c, p-c)) != nil)
1296*43f728cbSDavid du Colombier 		return err;
1297*43f728cbSDavid du Colombier 
1298*43f728cbSDavid du Colombier 	/* force BT state machine change */
1299*43f728cbSDavid du Colombier 	memset(c, 0, sizeof(c));
1300*43f728cbSDavid du Colombier 	p = c;
1301*43f728cbSDavid du Colombier 
1302*43f728cbSDavid du Colombier 	*p++ = 1; /* open */
1303*43f728cbSDavid du Colombier 	*p++ = 1; /* type */
1304*43f728cbSDavid du Colombier 	p += 2; /* reserved */
1305*43f728cbSDavid du Colombier 	if((err = cmd(ctlr, 205, c, p-c)) != nil)
1306*43f728cbSDavid du Colombier 		return err;
1307*43f728cbSDavid du Colombier 
1308*43f728cbSDavid du Colombier 	c[0] = 0; /* open */
1309*43f728cbSDavid du Colombier 	return cmd(ctlr, 205, c, p-c);
1310*43f728cbSDavid du Colombier }
1311*43f728cbSDavid du Colombier 
1312*43f728cbSDavid du Colombier static char*
postboot(Ctlr * ctlr)1313*43f728cbSDavid du Colombier postboot(Ctlr *ctlr)
1314*43f728cbSDavid du Colombier {
1315*43f728cbSDavid du Colombier 	uint ctxoff, ctxlen, dramaddr;
1316*43f728cbSDavid du Colombier 	char *err;
1317*43f728cbSDavid du Colombier 	int i, q;
1318*43f728cbSDavid du Colombier 
1319*43f728cbSDavid du Colombier 	if((err = niclock(ctlr)) != nil)
1320*43f728cbSDavid du Colombier 		return err;
1321*43f728cbSDavid du Colombier 
1322*43f728cbSDavid du Colombier 	if(ctlr->type != Type4965){
1323*43f728cbSDavid du Colombier 		dramaddr = SchedDramAddr5000;
1324*43f728cbSDavid du Colombier 		ctxoff = SchedCtxOff5000;
1325*43f728cbSDavid du Colombier 		ctxlen = SchedCtxLen5000;
1326*43f728cbSDavid du Colombier 	} else {
1327*43f728cbSDavid du Colombier 		dramaddr = SchedDramAddr4965;
1328*43f728cbSDavid du Colombier 		ctxoff = SchedCtxOff4965;
1329*43f728cbSDavid du Colombier 		ctxlen = SchedCtxLen4965;
1330*43f728cbSDavid du Colombier 	}
1331*43f728cbSDavid du Colombier 
1332*43f728cbSDavid du Colombier 	ctlr->sched.base = prphread(ctlr, SchedSramAddr);
1333*43f728cbSDavid du Colombier 	for(i=0; i < ctxlen; i += 4)
1334*43f728cbSDavid du Colombier 		memwrite(ctlr, ctlr->sched.base + ctxoff + i, 0);
1335*43f728cbSDavid du Colombier 
1336*43f728cbSDavid du Colombier 	prphwrite(ctlr, dramaddr, PCIWADDR(ctlr->sched.s)>>10);
1337*43f728cbSDavid du Colombier 
1338*43f728cbSDavid du Colombier 	csr32w(ctlr, FhTxChicken, csr32r(ctlr, FhTxChicken) | 2);
1339*43f728cbSDavid du Colombier 
1340*43f728cbSDavid du Colombier 	if(ctlr->type != Type4965){
1341*43f728cbSDavid du Colombier 		/* Enable chain mode for all queues, except command queue 4. */
1342*43f728cbSDavid du Colombier 		prphwrite(ctlr, SchedQChainSel5000, 0xfffef);
1343*43f728cbSDavid du Colombier 		prphwrite(ctlr, SchedAggrSel5000, 0);
1344*43f728cbSDavid du Colombier 
1345*43f728cbSDavid du Colombier 		for(q=0; q<20; q++){
1346*43f728cbSDavid du Colombier 			prphwrite(ctlr, SchedQueueRdptr5000 + q*4, 0);
1347*43f728cbSDavid du Colombier 			csr32w(ctlr, HbusTargWptr, q << 8);
1348*43f728cbSDavid du Colombier 
1349*43f728cbSDavid du Colombier 			memwrite(ctlr, ctlr->sched.base + ctxoff + q*8, 0);
1350*43f728cbSDavid du Colombier 			/* Set scheduler window size and frame limit. */
1351*43f728cbSDavid du Colombier 			memwrite(ctlr, ctlr->sched.base + ctxoff + q*8 + 4, 64<<16 | 64);
1352*43f728cbSDavid du Colombier 		}
1353*43f728cbSDavid du Colombier 		/* Enable interrupts for all our 20 queues. */
1354*43f728cbSDavid du Colombier 		prphwrite(ctlr, SchedIntrMask5000, 0xfffff);
1355*43f728cbSDavid du Colombier 
1356*43f728cbSDavid du Colombier 		/* Identify TX FIFO rings (0-7). */
1357*43f728cbSDavid du Colombier 		prphwrite(ctlr, SchedTxFact5000, 0xff);
1358*43f728cbSDavid du Colombier 	} else {
1359*43f728cbSDavid du Colombier 		/* Disable chain mode for all our 16 queues. */
1360*43f728cbSDavid du Colombier 		prphwrite(ctlr, SchedQChainSel4965, 0);
1361*43f728cbSDavid du Colombier 
1362*43f728cbSDavid du Colombier 		for(q=0; q<16; q++) {
1363*43f728cbSDavid du Colombier 			prphwrite(ctlr, SchedQueueRdptr4965 + q*4, 0);
1364*43f728cbSDavid du Colombier 			csr32w(ctlr, HbusTargWptr, q << 8);
1365*43f728cbSDavid du Colombier 
1366*43f728cbSDavid du Colombier 			/* Set scheduler window size. */
1367*43f728cbSDavid du Colombier 			memwrite(ctlr, ctlr->sched.base + ctxoff + q*8, 64);
1368*43f728cbSDavid du Colombier 			/* Set scheduler window size and frame limit. */
1369*43f728cbSDavid du Colombier 			memwrite(ctlr, ctlr->sched.base + ctxoff + q*8 + 4, 64<<16);
1370*43f728cbSDavid du Colombier 		}
1371*43f728cbSDavid du Colombier 		/* Enable interrupts for all our 16 queues. */
1372*43f728cbSDavid du Colombier 		prphwrite(ctlr, SchedIntrMask4965, 0xffff);
1373*43f728cbSDavid du Colombier 
1374*43f728cbSDavid du Colombier 		/* Identify TX FIFO rings (0-7). */
1375*43f728cbSDavid du Colombier 		prphwrite(ctlr, SchedTxFact4965, 0xff);
1376*43f728cbSDavid du Colombier 	}
1377*43f728cbSDavid du Colombier 
1378*43f728cbSDavid du Colombier 	/* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */
1379*43f728cbSDavid du Colombier 	for(q=0; q<7; q++){
1380*43f728cbSDavid du Colombier 		if(ctlr->type != Type4965){
1381*43f728cbSDavid du Colombier 			static uchar qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 };
1382*43f728cbSDavid du Colombier 			prphwrite(ctlr, SchedQueueStatus5000 + q*4, 0x00ff0018 | qid2fifo[q]);
1383*43f728cbSDavid du Colombier 		} else {
1384*43f728cbSDavid du Colombier 			static uchar qid2fifo[] = { 3, 2, 1, 0, 4, 5, 6 };
1385*43f728cbSDavid du Colombier 			prphwrite(ctlr, SchedQueueStatus4965 + q*4, 0x0007fc01 | qid2fifo[q]<<1);
1386*43f728cbSDavid du Colombier 		}
1387*43f728cbSDavid du Colombier 	}
1388*43f728cbSDavid du Colombier 	nicunlock(ctlr);
1389*43f728cbSDavid du Colombier 
1390*43f728cbSDavid du Colombier 	if(ctlr->type != Type4965){
1391*43f728cbSDavid du Colombier 		uchar c[Tcmdsize];
1392*43f728cbSDavid du Colombier 
1393*43f728cbSDavid du Colombier 		/* disable wimax coexistance */
1394*43f728cbSDavid du Colombier 		memset(c, 0, sizeof(c));
1395*43f728cbSDavid du Colombier 		if((err = cmd(ctlr, 90, c, 4+4*16)) != nil)
1396*43f728cbSDavid du Colombier 			return err;
1397*43f728cbSDavid du Colombier 
1398*43f728cbSDavid du Colombier 		if(ctlr->type != Type5150){
1399*43f728cbSDavid du Colombier 			/* calibrate crystal */
1400*43f728cbSDavid du Colombier 			memset(c, 0, sizeof(c));
1401*43f728cbSDavid du Colombier 			c[0] = 15;	/* code */
1402*43f728cbSDavid du Colombier 			c[1] = 0;	/* group */
1403*43f728cbSDavid du Colombier 			c[2] = 1;	/* ngroup */
1404*43f728cbSDavid du Colombier 			c[3] = 1;	/* isvalid */
1405*43f728cbSDavid du Colombier 			c[4] = ctlr->eeprom.crystal;
1406*43f728cbSDavid du Colombier 			c[5] = ctlr->eeprom.crystal>>16;
1407*43f728cbSDavid du Colombier 			/* for some reason 8086:4238 needs a second try */
1408*43f728cbSDavid du Colombier 			if(cmd(ctlr, 176, c, 8) != nil && (err = cmd(ctlr, 176, c, 8)) != nil)
1409*43f728cbSDavid du Colombier 				return err;
1410*43f728cbSDavid du Colombier 		}
1411*43f728cbSDavid du Colombier 
1412*43f728cbSDavid du Colombier 		if(ctlr->calib.done == 0){
1413*43f728cbSDavid du Colombier 			/* query calibration (init firmware) */
1414*43f728cbSDavid du Colombier 			memset(c, 0, sizeof(c));
1415*43f728cbSDavid du Colombier 			put32(c + 0*(5*4) + 0, 0xffffffff);
1416*43f728cbSDavid du Colombier 			put32(c + 0*(5*4) + 4, 0xffffffff);
1417*43f728cbSDavid du Colombier 			put32(c + 0*(5*4) + 8, 0xffffffff);
1418*43f728cbSDavid du Colombier 			put32(c + 2*(5*4) + 0, 0xffffffff);
1419*43f728cbSDavid du Colombier 			if((err = cmd(ctlr, 101, c, (((2*(5*4))+4)*2)+4)) != nil)
1420*43f728cbSDavid du Colombier 				return err;
1421*43f728cbSDavid du Colombier 
1422*43f728cbSDavid du Colombier 			/* wait to collect calibration records */
1423*43f728cbSDavid du Colombier 			if(irqwait(ctlr, Ierr, 2000))
1424*43f728cbSDavid du Colombier 				return "calibration failed";
1425*43f728cbSDavid du Colombier 
1426*43f728cbSDavid du Colombier 			if(ctlr->calib.done == 0){
1427*43f728cbSDavid du Colombier 				print("iwl: no calibration results\n");
1428*43f728cbSDavid du Colombier 				ctlr->calib.done = 1;
1429*43f728cbSDavid du Colombier 			}
1430*43f728cbSDavid du Colombier 		} else {
1431*43f728cbSDavid du Colombier 			static uchar cmds[] = {8, 9, 11, 17, 16};
1432*43f728cbSDavid du Colombier 
1433*43f728cbSDavid du Colombier 			/* send calibration records (runtime firmware) */
1434*43f728cbSDavid du Colombier 			for(q=0; q<nelem(cmds); q++){
1435*43f728cbSDavid du Colombier 				Block *b;
1436*43f728cbSDavid du Colombier 
1437*43f728cbSDavid du Colombier 				i = cmds[q];
1438*43f728cbSDavid du Colombier 				if(i == 8 && ctlr->type != Type5150 && ctlr->type != Type2030)
1439*43f728cbSDavid du Colombier 					continue;
1440*43f728cbSDavid du Colombier 				if(i == 17 && (ctlr->type >= Type6000 || ctlr->type == Type5150) &&
1441*43f728cbSDavid du Colombier 					ctlr->type != Type2030)
1442*43f728cbSDavid du Colombier 					continue;
1443*43f728cbSDavid du Colombier 
1444*43f728cbSDavid du Colombier 				if((b = ctlr->calib.cmd[i]) == nil)
1445*43f728cbSDavid du Colombier 					continue;
1446*43f728cbSDavid du Colombier 				b = copyblock(b, BLEN(b));
1447*43f728cbSDavid du Colombier 				if((err = qcmd(ctlr, 4, 176, nil, 0, b)) != nil){
1448*43f728cbSDavid du Colombier 					freeb(b);
1449*43f728cbSDavid du Colombier 					return err;
1450*43f728cbSDavid du Colombier 				}
1451*43f728cbSDavid du Colombier 				if((err = flushq(ctlr, 4)) != nil)
1452*43f728cbSDavid du Colombier 					return err;
1453*43f728cbSDavid du Colombier 			}
1454*43f728cbSDavid du Colombier 
1455*43f728cbSDavid du Colombier 			/* temperature sensor offset */
1456*43f728cbSDavid du Colombier 			switch (ctlr->type){
1457*43f728cbSDavid du Colombier 			case Type6005:
1458*43f728cbSDavid du Colombier 				memset(c, 0, sizeof(c));
1459*43f728cbSDavid du Colombier 				c[0] = 18;
1460*43f728cbSDavid du Colombier 				c[1] = 0;
1461*43f728cbSDavid du Colombier 				c[2] = 1;
1462*43f728cbSDavid du Colombier 				c[3] = 1;
1463*43f728cbSDavid du Colombier 				put16(c + 4, 2700);
1464*43f728cbSDavid du Colombier 				if((err = cmd(ctlr, 176, c, 4+2+2)) != nil)
1465*43f728cbSDavid du Colombier 					return err;
1466*43f728cbSDavid du Colombier 				break;
1467*43f728cbSDavid du Colombier 
1468*43f728cbSDavid du Colombier 			case Type2030:
1469*43f728cbSDavid du Colombier 				memset(c, 0, sizeof(c));
1470*43f728cbSDavid du Colombier 				c[0] = 18;
1471*43f728cbSDavid du Colombier 				c[1] = 0;
1472*43f728cbSDavid du Colombier 				c[2] = 1;
1473*43f728cbSDavid du Colombier 				c[3] = 1;
1474*43f728cbSDavid du Colombier 				if(ctlr->eeprom.rawtemp != 0){
1475*43f728cbSDavid du Colombier 					put16(c + 4, ctlr->eeprom.temp);
1476*43f728cbSDavid du Colombier 					put16(c + 6, ctlr->eeprom.rawtemp);
1477*43f728cbSDavid du Colombier 				} else{
1478*43f728cbSDavid du Colombier 					put16(c + 4, 2700);
1479*43f728cbSDavid du Colombier 					put16(c + 6, 2700);
1480*43f728cbSDavid du Colombier 				}
1481*43f728cbSDavid du Colombier 				put16(c + 8, ctlr->eeprom.volt);
1482*43f728cbSDavid du Colombier 				if((err = cmd(ctlr, 176, c, 4+2+2+2+2)) != nil)
1483*43f728cbSDavid du Colombier 					return err;
1484*43f728cbSDavid du Colombier 				break;
1485*43f728cbSDavid du Colombier 			}
1486*43f728cbSDavid du Colombier 
1487*43f728cbSDavid du Colombier 			if(ctlr->type == Type6005 || ctlr->type == Type6050){
1488*43f728cbSDavid du Colombier 				/* runtime DC calibration */
1489*43f728cbSDavid du Colombier 				memset(c, 0, sizeof(c));
1490*43f728cbSDavid du Colombier 				put32(c + 0*(5*4) + 0, 0xffffffff);
1491*43f728cbSDavid du Colombier 				put32(c + 0*(5*4) + 4, 1<<1);
1492*43f728cbSDavid du Colombier 				if((err = cmd(ctlr, 101, c, (((2*(5*4))+4)*2)+4)) != nil)
1493*43f728cbSDavid du Colombier 					return err;
1494*43f728cbSDavid du Colombier 			}
1495*43f728cbSDavid du Colombier 
1496*43f728cbSDavid du Colombier 			/* set tx antenna config */
1497*43f728cbSDavid du Colombier 			put32(c, ctlr->rfcfg.txantmask & 7);
1498*43f728cbSDavid du Colombier 			if((err = cmd(ctlr, 152, c, 4)) != nil)
1499*43f728cbSDavid du Colombier 				return err;
1500*43f728cbSDavid du Colombier 
1501*43f728cbSDavid du Colombier 			if(ctlr->type == Type2030){
1502*43f728cbSDavid du Colombier 				if((err = sendbtcoexadv(ctlr)) != nil)
1503*43f728cbSDavid du Colombier 					return err;
1504*43f728cbSDavid du Colombier 			}
1505*43f728cbSDavid du Colombier 		}
1506*43f728cbSDavid du Colombier 	}
1507*43f728cbSDavid du Colombier 
1508*43f728cbSDavid du Colombier 	return nil;
1509*43f728cbSDavid du Colombier }
1510*43f728cbSDavid du Colombier 
1511*43f728cbSDavid du Colombier static char*
loadfirmware1(Ctlr * ctlr,u32int dst,uchar * data,int size)1512*43f728cbSDavid du Colombier loadfirmware1(Ctlr *ctlr, u32int dst, uchar *data, int size)
1513*43f728cbSDavid du Colombier {
1514*43f728cbSDavid du Colombier 	uchar *dma;
1515*43f728cbSDavid du Colombier 	char *err;
1516*43f728cbSDavid du Colombier 
1517*43f728cbSDavid du Colombier 	dma = mallocalign(size, 16, 0, 0);
1518*43f728cbSDavid du Colombier 	if(dma == nil)
1519*43f728cbSDavid du Colombier 		return "no memory for dma";
1520*43f728cbSDavid du Colombier 	memmove(dma, data, size);
1521*43f728cbSDavid du Colombier 	coherence();
1522*43f728cbSDavid du Colombier 	if((err = niclock(ctlr)) != 0){
1523*43f728cbSDavid du Colombier 		free(dma);
1524*43f728cbSDavid du Colombier 		return err;
1525*43f728cbSDavid du Colombier 	}
1526*43f728cbSDavid du Colombier 	csr32w(ctlr, FhTxConfig + 9*32, 0);
1527*43f728cbSDavid du Colombier 	csr32w(ctlr, FhSramAddr + 9*4, dst);
1528*43f728cbSDavid du Colombier 	csr32w(ctlr, FhTfbdCtrl0 + 9*8, PCIWADDR(dma));
1529*43f728cbSDavid du Colombier 	csr32w(ctlr, FhTfbdCtrl1 + 9*8, size);
1530*43f728cbSDavid du Colombier 	csr32w(ctlr, FhTxBufStatus + 9*32,
1531*43f728cbSDavid du Colombier 		(1<<FhTxBufStatusTbNumShift) |
1532*43f728cbSDavid du Colombier 		(1<<FhTxBufStatusTbIdxShift) |
1533*43f728cbSDavid du Colombier 		FhTxBufStatusTfbdValid);
1534*43f728cbSDavid du Colombier 	csr32w(ctlr, FhTxConfig + 9*32, FhTxConfigDmaEna | FhTxConfigCirqHostEndTfd);
1535*43f728cbSDavid du Colombier 	nicunlock(ctlr);
1536*43f728cbSDavid du Colombier 	if(irqwait(ctlr, Ifhtx|Ierr, 5000) != Ifhtx){
1537*43f728cbSDavid du Colombier 		free(dma);
1538*43f728cbSDavid du Colombier 		return "dma error / timeout";
1539*43f728cbSDavid du Colombier 	}
1540*43f728cbSDavid du Colombier 	free(dma);
1541*43f728cbSDavid du Colombier 	return 0;
1542*43f728cbSDavid du Colombier }
1543*43f728cbSDavid du Colombier 
1544*43f728cbSDavid du Colombier static char*
boot(Ctlr * ctlr)1545*43f728cbSDavid du Colombier boot(Ctlr *ctlr)
1546*43f728cbSDavid du Colombier {
1547*43f728cbSDavid du Colombier 	int i, n, size;
1548*43f728cbSDavid du Colombier 	uchar *p, *dma;
1549*43f728cbSDavid du Colombier 	FWImage *fw;
1550*43f728cbSDavid du Colombier 	char *err;
1551*43f728cbSDavid du Colombier 
1552*43f728cbSDavid du Colombier 	fw = ctlr->fw;
1553*43f728cbSDavid du Colombier 
1554*43f728cbSDavid du Colombier 	if(fw->boot.text.size == 0){
1555*43f728cbSDavid du Colombier 		if(ctlr->calib.done == 0){
1556*43f728cbSDavid du Colombier 			if((err = loadfirmware1(ctlr, 0x00000000, fw->init.text.data, fw->init.text.size)) != nil)
1557*43f728cbSDavid du Colombier 				return err;
1558*43f728cbSDavid du Colombier 			if((err = loadfirmware1(ctlr, 0x00800000, fw->init.data.data, fw->init.data.size)) != nil)
1559*43f728cbSDavid du Colombier 				return err;
1560*43f728cbSDavid du Colombier 			csr32w(ctlr, Reset, 0);
1561*43f728cbSDavid du Colombier 			if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive)
1562*43f728cbSDavid du Colombier 				return "init firmware boot failed";
1563*43f728cbSDavid du Colombier 			if((err = postboot(ctlr)) != nil)
1564*43f728cbSDavid du Colombier 				return err;
1565*43f728cbSDavid du Colombier 			if((err = reset(ctlr)) != nil)
1566*43f728cbSDavid du Colombier 				return err;
1567*43f728cbSDavid du Colombier 		}
1568*43f728cbSDavid du Colombier 		if((err = loadfirmware1(ctlr, 0x00000000, fw->main.text.data, fw->main.text.size)) != nil)
1569*43f728cbSDavid du Colombier 			return err;
1570*43f728cbSDavid du Colombier 		if((err = loadfirmware1(ctlr, 0x00800000, fw->main.data.data, fw->main.data.size)) != nil)
1571*43f728cbSDavid du Colombier 			return err;
1572*43f728cbSDavid du Colombier 		csr32w(ctlr, Reset, 0);
1573*43f728cbSDavid du Colombier 		if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive)
1574*43f728cbSDavid du Colombier 			return "main firmware boot failed";
1575*43f728cbSDavid du Colombier 		return postboot(ctlr);
1576*43f728cbSDavid du Colombier 	}
1577*43f728cbSDavid du Colombier 
1578*43f728cbSDavid du Colombier 	size = ROUND(fw->init.data.size, 16) + ROUND(fw->init.text.size, 16);
1579*43f728cbSDavid du Colombier 	dma = mallocalign(size, 16, 0, 0);
1580*43f728cbSDavid du Colombier 	if(dma == nil)
1581*43f728cbSDavid du Colombier 		return "no memory for dma";
1582*43f728cbSDavid du Colombier 
1583*43f728cbSDavid du Colombier 	if((err = niclock(ctlr)) != nil){
1584*43f728cbSDavid du Colombier 		free(dma);
1585*43f728cbSDavid du Colombier 		return err;
1586*43f728cbSDavid du Colombier 	}
1587*43f728cbSDavid du Colombier 
1588*43f728cbSDavid du Colombier 	p = dma;
1589*43f728cbSDavid du Colombier 	memmove(p, fw->init.data.data, fw->init.data.size);
1590*43f728cbSDavid du Colombier 	coherence();
1591*43f728cbSDavid du Colombier 	prphwrite(ctlr, BsmDramDataAddr, PCIWADDR(p) >> 4);
1592*43f728cbSDavid du Colombier 	prphwrite(ctlr, BsmDramDataSize, fw->init.data.size);
1593*43f728cbSDavid du Colombier 	p += ROUND(fw->init.data.size, 16);
1594*43f728cbSDavid du Colombier 	memmove(p, fw->init.text.data, fw->init.text.size);
1595*43f728cbSDavid du Colombier 	coherence();
1596*43f728cbSDavid du Colombier 	prphwrite(ctlr, BsmDramTextAddr, PCIWADDR(p) >> 4);
1597*43f728cbSDavid du Colombier 	prphwrite(ctlr, BsmDramTextSize, fw->init.text.size);
1598*43f728cbSDavid du Colombier 
1599*43f728cbSDavid du Colombier 	nicunlock(ctlr);
1600*43f728cbSDavid du Colombier 	if((err = niclock(ctlr)) != nil){
1601*43f728cbSDavid du Colombier 		free(dma);
1602*43f728cbSDavid du Colombier 		return err;
1603*43f728cbSDavid du Colombier 	}
1604*43f728cbSDavid du Colombier 
1605*43f728cbSDavid du Colombier 	p = fw->boot.text.data;
1606*43f728cbSDavid du Colombier 	n = fw->boot.text.size/4;
1607*43f728cbSDavid du Colombier 	for(i=0; i<n; i++, p += 4)
1608*43f728cbSDavid du Colombier 		prphwrite(ctlr, BsmSramBase+i*4, get32(p));
1609*43f728cbSDavid du Colombier 
1610*43f728cbSDavid du Colombier 	prphwrite(ctlr, BsmWrMemSrc, 0);
1611*43f728cbSDavid du Colombier 	prphwrite(ctlr, BsmWrMemDst, 0);
1612*43f728cbSDavid du Colombier 	prphwrite(ctlr, BsmWrDwCount, n);
1613*43f728cbSDavid du Colombier 
1614*43f728cbSDavid du Colombier 	prphwrite(ctlr, BsmWrCtrl, 1<<31);
1615*43f728cbSDavid du Colombier 
1616*43f728cbSDavid du Colombier 	for(i=0; i<1000; i++){
1617*43f728cbSDavid du Colombier 		if((prphread(ctlr, BsmWrCtrl) & (1<<31)) == 0)
1618*43f728cbSDavid du Colombier 			break;
1619*43f728cbSDavid du Colombier 		delay(10);
1620*43f728cbSDavid du Colombier 	}
1621*43f728cbSDavid du Colombier 	if(i == 1000){
1622*43f728cbSDavid du Colombier 		nicunlock(ctlr);
1623*43f728cbSDavid du Colombier 		free(dma);
1624*43f728cbSDavid du Colombier 		return "bootcode timeout";
1625*43f728cbSDavid du Colombier 	}
1626*43f728cbSDavid du Colombier 
1627*43f728cbSDavid du Colombier 	prphwrite(ctlr, BsmWrCtrl, 1<<30);
1628*43f728cbSDavid du Colombier 	nicunlock(ctlr);
1629*43f728cbSDavid du Colombier 
1630*43f728cbSDavid du Colombier 	csr32w(ctlr, Reset, 0);
1631*43f728cbSDavid du Colombier 	if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive){
1632*43f728cbSDavid du Colombier 		free(dma);
1633*43f728cbSDavid du Colombier 		return "init firmware boot failed";
1634*43f728cbSDavid du Colombier 	}
1635*43f728cbSDavid du Colombier 	free(dma);
1636*43f728cbSDavid du Colombier 
1637*43f728cbSDavid du Colombier 	size = ROUND(fw->main.data.size, 16) + ROUND(fw->main.text.size, 16);
1638*43f728cbSDavid du Colombier 	dma = mallocalign(size, 16, 0, 0);
1639*43f728cbSDavid du Colombier 	if(dma == nil)
1640*43f728cbSDavid du Colombier 		return "no memory for dma";
1641*43f728cbSDavid du Colombier 	if((err = niclock(ctlr)) != nil){
1642*43f728cbSDavid du Colombier 		free(dma);
1643*43f728cbSDavid du Colombier 		return err;
1644*43f728cbSDavid du Colombier 	}
1645*43f728cbSDavid du Colombier 	p = dma;
1646*43f728cbSDavid du Colombier 	memmove(p, fw->main.data.data, fw->main.data.size);
1647*43f728cbSDavid du Colombier 	coherence();
1648*43f728cbSDavid du Colombier 	prphwrite(ctlr, BsmDramDataAddr, PCIWADDR(p) >> 4);
1649*43f728cbSDavid du Colombier 	prphwrite(ctlr, BsmDramDataSize, fw->main.data.size);
1650*43f728cbSDavid du Colombier 	p += ROUND(fw->main.data.size, 16);
1651*43f728cbSDavid du Colombier 	memmove(p, fw->main.text.data, fw->main.text.size);
1652*43f728cbSDavid du Colombier 	coherence();
1653*43f728cbSDavid du Colombier 	prphwrite(ctlr, BsmDramTextAddr, PCIWADDR(p) >> 4);
1654*43f728cbSDavid du Colombier 	prphwrite(ctlr, BsmDramTextSize, fw->main.text.size | (1<<31));
1655*43f728cbSDavid du Colombier 	nicunlock(ctlr);
1656*43f728cbSDavid du Colombier 
1657*43f728cbSDavid du Colombier 	if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive){
1658*43f728cbSDavid du Colombier 		free(dma);
1659*43f728cbSDavid du Colombier 		return "main firmware boot failed";
1660*43f728cbSDavid du Colombier 	}
1661*43f728cbSDavid du Colombier 	free(dma);
1662*43f728cbSDavid du Colombier 	return postboot(ctlr);
1663*43f728cbSDavid du Colombier }
1664*43f728cbSDavid du Colombier 
1665*43f728cbSDavid du Colombier static int
txqready(void * arg)1666*43f728cbSDavid du Colombier txqready(void *arg)
1667*43f728cbSDavid du Colombier {
1668*43f728cbSDavid du Colombier 	TXQ *q = arg;
1669*43f728cbSDavid du Colombier 	return q->n < Ntx;
1670*43f728cbSDavid du Colombier }
1671*43f728cbSDavid du Colombier 
1672*43f728cbSDavid du Colombier static char*
qcmd(Ctlr * ctlr,uint qid,uint code,uchar * data,int size,Block * block)1673*43f728cbSDavid du Colombier qcmd(Ctlr *ctlr, uint qid, uint code, uchar *data, int size, Block *block)
1674*43f728cbSDavid du Colombier {
1675*43f728cbSDavid du Colombier 	uchar *d, *c;
1676*43f728cbSDavid du Colombier 	TXQ *q;
1677*43f728cbSDavid du Colombier 
1678*43f728cbSDavid du Colombier 	assert(qid < nelem(ctlr->tx));
1679*43f728cbSDavid du Colombier 	assert(size <= Tcmdsize-4);
1680*43f728cbSDavid du Colombier 
1681*43f728cbSDavid du Colombier 	ilock(ctlr);
1682*43f728cbSDavid du Colombier 	q = &ctlr->tx[qid];
1683*43f728cbSDavid du Colombier 	while(q->n >= Ntx && !ctlr->broken){
1684*43f728cbSDavid du Colombier 		iunlock(ctlr);
1685*43f728cbSDavid du Colombier 		qlock(q);
1686*43f728cbSDavid du Colombier 		if(!waserror()){
1687*43f728cbSDavid du Colombier 			tsleep(q, txqready, q, 10);
1688*43f728cbSDavid du Colombier 			poperror();
1689*43f728cbSDavid du Colombier 		}
1690*43f728cbSDavid du Colombier 		qunlock(q);
1691*43f728cbSDavid du Colombier 		ilock(ctlr);
1692*43f728cbSDavid du Colombier 	}
1693*43f728cbSDavid du Colombier 	if(ctlr->broken){
1694*43f728cbSDavid du Colombier 		iunlock(ctlr);
1695*43f728cbSDavid du Colombier 		return "qcmd: broken";
1696*43f728cbSDavid du Colombier 	}
1697*43f728cbSDavid du Colombier 	q->n++;
1698*43f728cbSDavid du Colombier 
1699*43f728cbSDavid du Colombier 	q->lastcmd = code;
1700*43f728cbSDavid du Colombier 	q->b[q->i] = block;
1701*43f728cbSDavid du Colombier 	c = q->c + q->i * Tcmdsize;
1702*43f728cbSDavid du Colombier 	d = q->d + q->i * Tdscsize;
1703*43f728cbSDavid du Colombier 
1704*43f728cbSDavid du Colombier 	/* build command */
1705*43f728cbSDavid du Colombier 	c[0] = code;
1706*43f728cbSDavid du Colombier 	c[1] = 0;	/* flags */
1707*43f728cbSDavid du Colombier 	c[2] = q->i;
1708*43f728cbSDavid du Colombier 	c[3] = qid;
1709*43f728cbSDavid du Colombier 
1710*43f728cbSDavid du Colombier 	if(size > 0)
1711*43f728cbSDavid du Colombier 		memmove(c+4, data, size);
1712*43f728cbSDavid du Colombier 
1713*43f728cbSDavid du Colombier 	size += 4;
1714*43f728cbSDavid du Colombier 
1715*43f728cbSDavid du Colombier 	/* build descriptor */
1716*43f728cbSDavid du Colombier 	*d++ = 0;
1717*43f728cbSDavid du Colombier 	*d++ = 0;
1718*43f728cbSDavid du Colombier 	*d++ = 0;
1719*43f728cbSDavid du Colombier 	*d++ = 1 + (block != nil); /* nsegs */
1720*43f728cbSDavid du Colombier 	put32(d, PCIWADDR(c));	d += 4;
1721*43f728cbSDavid du Colombier 	put16(d, size << 4); d += 2;
1722*43f728cbSDavid du Colombier 	if(block != nil){
1723*43f728cbSDavid du Colombier 		size = BLEN(block);
1724*43f728cbSDavid du Colombier 		put32(d, PCIWADDR(block->rp)); d += 4;
1725*43f728cbSDavid du Colombier 		put16(d, size << 4);
1726*43f728cbSDavid du Colombier 	}
1727*43f728cbSDavid du Colombier 
1728*43f728cbSDavid du Colombier 	coherence();
1729*43f728cbSDavid du Colombier 
1730*43f728cbSDavid du Colombier 	q->i = (q->i+1) % Ntx;
1731*43f728cbSDavid du Colombier 	csr32w(ctlr, HbusTargWptr, (qid<<8) | q->i);
1732*43f728cbSDavid du Colombier 
1733*43f728cbSDavid du Colombier 	iunlock(ctlr);
1734*43f728cbSDavid du Colombier 
1735*43f728cbSDavid du Colombier 	return nil;
1736*43f728cbSDavid du Colombier }
1737*43f728cbSDavid du Colombier 
1738*43f728cbSDavid du Colombier static int
txqempty(void * arg)1739*43f728cbSDavid du Colombier txqempty(void *arg)
1740*43f728cbSDavid du Colombier {
1741*43f728cbSDavid du Colombier 	TXQ *q = arg;
1742*43f728cbSDavid du Colombier 	return q->n == 0;
1743*43f728cbSDavid du Colombier }
1744*43f728cbSDavid du Colombier 
1745*43f728cbSDavid du Colombier static char*
flushq(Ctlr * ctlr,uint qid)1746*43f728cbSDavid du Colombier flushq(Ctlr *ctlr, uint qid)
1747*43f728cbSDavid du Colombier {
1748*43f728cbSDavid du Colombier 	TXQ *q;
1749*43f728cbSDavid du Colombier 	int i;
1750*43f728cbSDavid du Colombier 
1751*43f728cbSDavid du Colombier 	q = &ctlr->tx[qid];
1752*43f728cbSDavid du Colombier 	qlock(q);
1753*43f728cbSDavid du Colombier 	for(i = 0; i < 200 && !ctlr->broken; i++){
1754*43f728cbSDavid du Colombier 		if(txqempty(q)){
1755*43f728cbSDavid du Colombier 			qunlock(q);
1756*43f728cbSDavid du Colombier 			return nil;
1757*43f728cbSDavid du Colombier 		}
1758*43f728cbSDavid du Colombier 		if(!waserror()){
1759*43f728cbSDavid du Colombier 			tsleep(q, txqempty, q, 10);
1760*43f728cbSDavid du Colombier 			poperror();
1761*43f728cbSDavid du Colombier 		}
1762*43f728cbSDavid du Colombier 	}
1763*43f728cbSDavid du Colombier 	qunlock(q);
1764*43f728cbSDavid du Colombier 	if(ctlr->broken)
1765*43f728cbSDavid du Colombier 		return "flushq: broken";
1766*43f728cbSDavid du Colombier 	return "flushq: timeout";
1767*43f728cbSDavid du Colombier }
1768*43f728cbSDavid du Colombier 
1769*43f728cbSDavid du Colombier static char*
cmd(Ctlr * ctlr,uint code,uchar * data,int size)1770*43f728cbSDavid du Colombier cmd(Ctlr *ctlr, uint code, uchar *data, int size)
1771*43f728cbSDavid du Colombier {
1772*43f728cbSDavid du Colombier 	char *err;
1773*43f728cbSDavid du Colombier 
1774*43f728cbSDavid du Colombier 	if(0) print("cmd %ud\n", code);
1775*43f728cbSDavid du Colombier 	if((err = qcmd(ctlr, 4, code, data, size, nil)) != nil)
1776*43f728cbSDavid du Colombier 		return err;
1777*43f728cbSDavid du Colombier 	return flushq(ctlr, 4);
1778*43f728cbSDavid du Colombier }
1779*43f728cbSDavid du Colombier 
1780*43f728cbSDavid du Colombier static void
setled(Ctlr * ctlr,int which,int on,int off)1781*43f728cbSDavid du Colombier setled(Ctlr *ctlr, int which, int on, int off)
1782*43f728cbSDavid du Colombier {
1783*43f728cbSDavid du Colombier 	uchar c[8];
1784*43f728cbSDavid du Colombier 
1785*43f728cbSDavid du Colombier 	csr32w(ctlr, Led, csr32r(ctlr, Led) & ~LedBsmCtrl);
1786*43f728cbSDavid du Colombier 
1787*43f728cbSDavid du Colombier 	memset(c, 0, sizeof(c));
1788*43f728cbSDavid du Colombier 	put32(c, 10000);
1789*43f728cbSDavid du Colombier 	c[4] = which;
1790*43f728cbSDavid du Colombier 	c[5] = on;
1791*43f728cbSDavid du Colombier 	c[6] = off;
1792*43f728cbSDavid du Colombier 	cmd(ctlr, 72, c, sizeof(c));
1793*43f728cbSDavid du Colombier }
1794*43f728cbSDavid du Colombier 
1795*43f728cbSDavid du Colombier static void
addnode(Ctlr * ctlr,uchar id,uchar * addr)1796*43f728cbSDavid du Colombier addnode(Ctlr *ctlr, uchar id, uchar *addr)
1797*43f728cbSDavid du Colombier {
1798*43f728cbSDavid du Colombier 	uchar c[Tcmdsize], *p;
1799*43f728cbSDavid du Colombier 
1800*43f728cbSDavid du Colombier 	memset(p = c, 0, sizeof(c));
1801*43f728cbSDavid du Colombier 	*p++ = 0;	/* control (1 = update) */
1802*43f728cbSDavid du Colombier 	p += 3;		/* reserved */
1803*43f728cbSDavid du Colombier 	memmove(p, addr, 6);
1804*43f728cbSDavid du Colombier 	p += 6;
1805*43f728cbSDavid du Colombier 	p += 2;		/* reserved */
1806*43f728cbSDavid du Colombier 	*p++ = id;	/* node id */
1807*43f728cbSDavid du Colombier 	p++;		/* flags */
1808*43f728cbSDavid du Colombier 	p += 2;		/* reserved */
1809*43f728cbSDavid du Colombier 	p += 2;		/* kflags */
1810*43f728cbSDavid du Colombier 	p++;		/* tcs2 */
1811*43f728cbSDavid du Colombier 	p++;		/* reserved */
1812*43f728cbSDavid du Colombier 	p += 5*2;	/* ttak */
1813*43f728cbSDavid du Colombier 	p++;		/* kid */
1814*43f728cbSDavid du Colombier 	p++;		/* reserved */
1815*43f728cbSDavid du Colombier 	p += 16;	/* key */
1816*43f728cbSDavid du Colombier 	if(ctlr->type != Type4965){
1817*43f728cbSDavid du Colombier 		p += 8;		/* tcs */
1818*43f728cbSDavid du Colombier 		p += 8;		/* rxmic */
1819*43f728cbSDavid du Colombier 		p += 8;		/* txmic */
1820*43f728cbSDavid du Colombier 	}
1821*43f728cbSDavid du Colombier 	p += 4;		/* htflags */
1822*43f728cbSDavid du Colombier 	p += 4;		/* mask */
1823*43f728cbSDavid du Colombier 	p += 2;		/* disable tid */
1824*43f728cbSDavid du Colombier 	p += 2;		/* reserved */
1825*43f728cbSDavid du Colombier 	p++;		/* add ba tid */
1826*43f728cbSDavid du Colombier 	p++;		/* del ba tid */
1827*43f728cbSDavid du Colombier 	p += 2;		/* add ba ssn */
1828*43f728cbSDavid du Colombier 	p += 4;		/* reserved */
1829*43f728cbSDavid du Colombier 	cmd(ctlr, 24, c, p - c);
1830*43f728cbSDavid du Colombier }
1831*43f728cbSDavid du Colombier 
1832*43f728cbSDavid du Colombier static void
rxon(Ether * edev,Wnode * bss)1833*43f728cbSDavid du Colombier rxon(Ether *edev, Wnode *bss)
1834*43f728cbSDavid du Colombier {
1835*43f728cbSDavid du Colombier 	uchar c[Tcmdsize], *p;
1836*43f728cbSDavid du Colombier 	int filter, flags;
1837*43f728cbSDavid du Colombier 	Ctlr *ctlr;
1838*43f728cbSDavid du Colombier 	char *err;
1839*43f728cbSDavid du Colombier 
1840*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
1841*43f728cbSDavid du Colombier 	filter = FilterNoDecrypt | FilterMulticast | FilterBeacon;
1842*43f728cbSDavid du Colombier 	if(ctlr->prom){
1843*43f728cbSDavid du Colombier 		filter |= FilterPromisc;
1844*43f728cbSDavid du Colombier 		if(bss != nil)
1845*43f728cbSDavid du Colombier 			ctlr->channel = bss->channel;
1846*43f728cbSDavid du Colombier 		bss = nil;
1847*43f728cbSDavid du Colombier 	}
1848*43f728cbSDavid du Colombier 	flags = RFlagTSF | RFlagCTSToSelf | RFlag24Ghz | RFlagAuto;
1849*43f728cbSDavid du Colombier 	if(bss != nil){
1850*43f728cbSDavid du Colombier 		if(bss->cap & (1<<5))
1851*43f728cbSDavid du Colombier 			flags |= RFlagShPreamble;
1852*43f728cbSDavid du Colombier 		if(bss->cap & (1<<10))
1853*43f728cbSDavid du Colombier 			flags |= RFlagShSlot;
1854*43f728cbSDavid du Colombier 		ctlr->channel = bss->channel;
1855*43f728cbSDavid du Colombier 		memmove(ctlr->bssid, bss->bssid, Eaddrlen);
1856*43f728cbSDavid du Colombier 		ctlr->aid = bss->aid;
1857*43f728cbSDavid du Colombier 		if(ctlr->aid != 0){
1858*43f728cbSDavid du Colombier 			filter |= FilterBSS;
1859*43f728cbSDavid du Colombier 			filter &= ~FilterBeacon;
1860*43f728cbSDavid du Colombier 			ctlr->bssnodeid = -1;
1861*43f728cbSDavid du Colombier 		} else
1862*43f728cbSDavid du Colombier 			ctlr->bcastnodeid = -1;
1863*43f728cbSDavid du Colombier 	} else {
1864*43f728cbSDavid du Colombier 		memmove(ctlr->bssid, edev->bcast, Eaddrlen);
1865*43f728cbSDavid du Colombier 		ctlr->aid = 0;
1866*43f728cbSDavid du Colombier 		ctlr->bcastnodeid = -1;
1867*43f728cbSDavid du Colombier 		ctlr->bssnodeid = -1;
1868*43f728cbSDavid du Colombier 	}
1869*43f728cbSDavid du Colombier 
1870*43f728cbSDavid du Colombier 	if(ctlr->aid != 0)
1871*43f728cbSDavid du Colombier 		setled(ctlr, 2, 0, 1);		/* on when associated */
1872*43f728cbSDavid du Colombier 	else if(memcmp(ctlr->bssid, edev->bcast, Eaddrlen) != 0)
1873*43f728cbSDavid du Colombier 		setled(ctlr, 2, 10, 10);	/* slow blink when connecting */
1874*43f728cbSDavid du Colombier 	else
1875*43f728cbSDavid du Colombier 		setled(ctlr, 2, 5, 5);		/* fast blink when scanning */
1876*43f728cbSDavid du Colombier 
1877*43f728cbSDavid du Colombier 	if(ctlr->wifi->debug)
1878*43f728cbSDavid du Colombier 		print("#l%d: rxon: bssid %E, aid %x, channel %d, filter %x, flags %x\n",
1879*43f728cbSDavid du Colombier 			edev->ctlrno, ctlr->bssid, ctlr->aid, ctlr->channel, filter, flags);
1880*43f728cbSDavid du Colombier 
1881*43f728cbSDavid du Colombier 	memset(p = c, 0, sizeof(c));
1882*43f728cbSDavid du Colombier 	memmove(p, edev->ea, 6); p += 8;	/* myaddr */
1883*43f728cbSDavid du Colombier 	memmove(p, ctlr->bssid, 6); p += 8;	/* bssid */
1884*43f728cbSDavid du Colombier 	memmove(p, edev->ea, 6); p += 8;	/* wlap */
1885*43f728cbSDavid du Colombier 	*p++ = 3;				/* mode (STA) */
1886*43f728cbSDavid du Colombier 	*p++ = 0;				/* air (?) */
1887*43f728cbSDavid du Colombier 	/* rxchain */
1888*43f728cbSDavid du Colombier 	put16(p, ((ctlr->rfcfg.rxantmask & 7)<<1) | (2<<10) | (2<<12));
1889*43f728cbSDavid du Colombier 	p += 2;
1890*43f728cbSDavid du Colombier 	*p++ = 0xff;				/* ofdm mask (not yet negotiated) */
1891*43f728cbSDavid du Colombier 	*p++ = 0x0f;				/* cck mask (not yet negotiated) */
1892*43f728cbSDavid du Colombier 	put16(p, ctlr->aid & 0x3fff);
1893*43f728cbSDavid du Colombier 	p += 2;					/* aid */
1894*43f728cbSDavid du Colombier 	put32(p, flags);
1895*43f728cbSDavid du Colombier 	p += 4;
1896*43f728cbSDavid du Colombier 	put32(p, filter);
1897*43f728cbSDavid du Colombier 	p += 4;
1898*43f728cbSDavid du Colombier 	*p++ = ctlr->channel;
1899*43f728cbSDavid du Colombier 	p++;					/* reserved */
1900*43f728cbSDavid du Colombier 	*p++ = 0xff;				/* ht single mask */
1901*43f728cbSDavid du Colombier 	*p++ = 0xff;				/* ht dual mask */
1902*43f728cbSDavid du Colombier 	if(ctlr->type != Type4965){
1903*43f728cbSDavid du Colombier 		*p++ = 0xff;			/* ht triple mask */
1904*43f728cbSDavid du Colombier 		p++;				/* reserved */
1905*43f728cbSDavid du Colombier 		put16(p, 0); p += 2;		/* acquisition */
1906*43f728cbSDavid du Colombier 		p += 2;				/* reserved */
1907*43f728cbSDavid du Colombier 	}
1908*43f728cbSDavid du Colombier 	if((err = cmd(ctlr, 16, c, p - c)) != nil){
1909*43f728cbSDavid du Colombier 		print("rxon: %s\n", err);
1910*43f728cbSDavid du Colombier 		return;
1911*43f728cbSDavid du Colombier 	}
1912*43f728cbSDavid du Colombier 
1913*43f728cbSDavid du Colombier 	if(ctlr->bcastnodeid == -1){
1914*43f728cbSDavid du Colombier 		ctlr->bcastnodeid = (ctlr->type != Type4965) ? 15 : 31;
1915*43f728cbSDavid du Colombier 		addnode(ctlr, ctlr->bcastnodeid, edev->bcast);
1916*43f728cbSDavid du Colombier 	}
1917*43f728cbSDavid du Colombier 	if(ctlr->bssnodeid == -1 && bss != nil && ctlr->aid != 0){
1918*43f728cbSDavid du Colombier 		ctlr->bssnodeid = 0;
1919*43f728cbSDavid du Colombier 		addnode(ctlr, ctlr->bssnodeid, bss->bssid);
1920*43f728cbSDavid du Colombier 	}
1921*43f728cbSDavid du Colombier }
1922*43f728cbSDavid du Colombier 
1923*43f728cbSDavid du Colombier static struct ratetab {
1924*43f728cbSDavid du Colombier 	uchar	rate;
1925*43f728cbSDavid du Colombier 	uchar	plcp;
1926*43f728cbSDavid du Colombier 	uchar	flags;
1927*43f728cbSDavid du Colombier } ratetab[] = {
1928*43f728cbSDavid du Colombier 	{   2,  10, RFlagCCK },
1929*43f728cbSDavid du Colombier 	{   4,  20, RFlagCCK },
1930*43f728cbSDavid du Colombier 	{  11,  55, RFlagCCK },
1931*43f728cbSDavid du Colombier 	{  22, 110, RFlagCCK },
1932*43f728cbSDavid du Colombier 	{  12, 0xd, 0 },
1933*43f728cbSDavid du Colombier 	{  18, 0xf, 0 },
1934*43f728cbSDavid du Colombier 	{  24, 0x5, 0 },
1935*43f728cbSDavid du Colombier 	{  36, 0x7, 0 },
1936*43f728cbSDavid du Colombier 	{  48, 0x9, 0 },
1937*43f728cbSDavid du Colombier 	{  72, 0xb, 0 },
1938*43f728cbSDavid du Colombier 	{  96, 0x1, 0 },
1939*43f728cbSDavid du Colombier 	{ 108, 0x3, 0 },
1940*43f728cbSDavid du Colombier 	{ 120, 0x3, 0 }
1941*43f728cbSDavid du Colombier };
1942*43f728cbSDavid du Colombier 
1943*43f728cbSDavid du Colombier static uchar iwlrates[] = {
1944*43f728cbSDavid du Colombier 	0x80 | 2,
1945*43f728cbSDavid du Colombier 	0x80 | 4,
1946*43f728cbSDavid du Colombier 	0x80 | 11,
1947*43f728cbSDavid du Colombier 	0x80 | 22,
1948*43f728cbSDavid du Colombier 	0x80 | 12,
1949*43f728cbSDavid du Colombier 	0x80 | 18,
1950*43f728cbSDavid du Colombier 	0x80 | 24,
1951*43f728cbSDavid du Colombier 	0x80 | 36,
1952*43f728cbSDavid du Colombier 	0x80 | 48,
1953*43f728cbSDavid du Colombier 	0x80 | 72,
1954*43f728cbSDavid du Colombier 	0x80 | 96,
1955*43f728cbSDavid du Colombier 	0x80 | 108,
1956*43f728cbSDavid du Colombier 	0x80 | 120,
1957*43f728cbSDavid du Colombier 
1958*43f728cbSDavid du Colombier 	0
1959*43f728cbSDavid du Colombier };
1960*43f728cbSDavid du Colombier 
1961*43f728cbSDavid du Colombier enum {
1962*43f728cbSDavid du Colombier 	TFlagNeedProtection	= 1<<0,
1963*43f728cbSDavid du Colombier 	TFlagNeedRTS		= 1<<1,
1964*43f728cbSDavid du Colombier 	TFlagNeedCTS		= 1<<2,
1965*43f728cbSDavid du Colombier 	TFlagNeedACK		= 1<<3,
1966*43f728cbSDavid du Colombier 	TFlagLinkq		= 1<<4,
1967*43f728cbSDavid du Colombier 	TFlagImmBa		= 1<<6,
1968*43f728cbSDavid du Colombier 	TFlagFullTxOp		= 1<<7,
1969*43f728cbSDavid du Colombier 	TFlagBtDis		= 1<<12,
1970*43f728cbSDavid du Colombier 	TFlagAutoSeq		= 1<<13,
1971*43f728cbSDavid du Colombier 	TFlagMoreFrag		= 1<<14,
1972*43f728cbSDavid du Colombier 	TFlagInsertTs		= 1<<16,
1973*43f728cbSDavid du Colombier 	TFlagNeedPadding	= 1<<20,
1974*43f728cbSDavid du Colombier };
1975*43f728cbSDavid du Colombier 
1976*43f728cbSDavid du Colombier static void
transmit(Wifi * wifi,Wnode * wn,Block * b)1977*43f728cbSDavid du Colombier transmit(Wifi *wifi, Wnode *wn, Block *b)
1978*43f728cbSDavid du Colombier {
1979*43f728cbSDavid du Colombier 	int flags, nodeid, rate, ant;
1980*43f728cbSDavid du Colombier 	uchar c[Tcmdsize], *p;
1981*43f728cbSDavid du Colombier 	Ether *edev;
1982*43f728cbSDavid du Colombier 	Ctlr *ctlr;
1983*43f728cbSDavid du Colombier 	Wifipkt *w;
1984*43f728cbSDavid du Colombier 	char *err;
1985*43f728cbSDavid du Colombier 
1986*43f728cbSDavid du Colombier 	edev = wifi->ether;
1987*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
1988*43f728cbSDavid du Colombier 
1989*43f728cbSDavid du Colombier 	qlock(ctlr);
1990*43f728cbSDavid du Colombier 	if(ctlr->attached == 0 || ctlr->broken){
1991*43f728cbSDavid du Colombier 		qunlock(ctlr);
1992*43f728cbSDavid du Colombier 		freeb(b);
1993*43f728cbSDavid du Colombier 		return;
1994*43f728cbSDavid du Colombier 	}
1995*43f728cbSDavid du Colombier 
1996*43f728cbSDavid du Colombier 	if((wn->channel != ctlr->channel)
1997*43f728cbSDavid du Colombier 	|| (!ctlr->prom && (wn->aid != ctlr->aid || memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0)))
1998*43f728cbSDavid du Colombier 		rxon(edev, wn);
1999*43f728cbSDavid du Colombier 
2000*43f728cbSDavid du Colombier 	if(b == nil){
2001*43f728cbSDavid du Colombier 		/* association note has no data to transmit */
2002*43f728cbSDavid du Colombier 		qunlock(ctlr);
2003*43f728cbSDavid du Colombier 		return;
2004*43f728cbSDavid du Colombier 	}
2005*43f728cbSDavid du Colombier 
2006*43f728cbSDavid du Colombier 	flags = 0;
2007*43f728cbSDavid du Colombier 	nodeid = ctlr->bcastnodeid;
2008*43f728cbSDavid du Colombier 	p = wn->minrate;
2009*43f728cbSDavid du Colombier 	w = (Wifipkt*)b->rp;
2010*43f728cbSDavid du Colombier 	if((w->a1[0] & 1) == 0){
2011*43f728cbSDavid du Colombier 		flags |= TFlagNeedACK;
2012*43f728cbSDavid du Colombier 
2013*43f728cbSDavid du Colombier 		if(BLEN(b) > 512-4)
2014*43f728cbSDavid du Colombier 			flags |= TFlagNeedRTS;
2015*43f728cbSDavid du Colombier 
2016*43f728cbSDavid du Colombier 		if((w->fc[0] & 0x0c) == 0x08 &&	ctlr->bssnodeid != -1){
2017*43f728cbSDavid du Colombier 			nodeid = ctlr->bssnodeid;
2018*43f728cbSDavid du Colombier 			p = wn->actrate;
2019*43f728cbSDavid du Colombier 		}
2020*43f728cbSDavid du Colombier 
2021*43f728cbSDavid du Colombier 		if(flags & (TFlagNeedRTS|TFlagNeedCTS)){
2022*43f728cbSDavid du Colombier 			if(ctlr->type != Type4965){
2023*43f728cbSDavid du Colombier 				flags &= ~(TFlagNeedRTS|TFlagNeedCTS);
2024*43f728cbSDavid du Colombier 				flags |= TFlagNeedProtection;
2025*43f728cbSDavid du Colombier 			} else
2026*43f728cbSDavid du Colombier 				flags |= TFlagFullTxOp;
2027*43f728cbSDavid du Colombier 		}
2028*43f728cbSDavid du Colombier 	}
2029*43f728cbSDavid du Colombier 	qunlock(ctlr);
2030*43f728cbSDavid du Colombier 
2031*43f728cbSDavid du Colombier 	rate = 0;
2032*43f728cbSDavid du Colombier 	if(p >= iwlrates && p < &iwlrates[nelem(ratetab)])
2033*43f728cbSDavid du Colombier 		rate = p - iwlrates;
2034*43f728cbSDavid du Colombier 
2035*43f728cbSDavid du Colombier 	/* select first available antenna */
2036*43f728cbSDavid du Colombier 	ant = ctlr->rfcfg.txantmask & 7;
2037*43f728cbSDavid du Colombier 	ant |= (ant == 0);
2038*43f728cbSDavid du Colombier 	ant = ((ant - 1) & ant) ^ ant;
2039*43f728cbSDavid du Colombier 
2040*43f728cbSDavid du Colombier 	memset(p = c, 0, sizeof(c));
2041*43f728cbSDavid du Colombier 	put16(p, BLEN(b));
2042*43f728cbSDavid du Colombier 	p += 2;
2043*43f728cbSDavid du Colombier 	p += 2;		/* lnext */
2044*43f728cbSDavid du Colombier 	put32(p, flags);
2045*43f728cbSDavid du Colombier 	p += 4;
2046*43f728cbSDavid du Colombier 	put32(p, 0);
2047*43f728cbSDavid du Colombier 	p += 4;		/* scratch */
2048*43f728cbSDavid du Colombier 
2049*43f728cbSDavid du Colombier 	*p++ = ratetab[rate].plcp;
2050*43f728cbSDavid du Colombier 	*p++ = ratetab[rate].flags | (ant<<6);
2051*43f728cbSDavid du Colombier 
2052*43f728cbSDavid du Colombier 	p += 2;		/* xflags */
2053*43f728cbSDavid du Colombier 	*p++ = nodeid;
2054*43f728cbSDavid du Colombier 	*p++ = 0;	/* security */
2055*43f728cbSDavid du Colombier 	*p++ = 0;	/* linkq */
2056*43f728cbSDavid du Colombier 	p++;		/* reserved */
2057*43f728cbSDavid du Colombier 	p += 16;	/* key */
2058*43f728cbSDavid du Colombier 	p += 2;		/* fnext */
2059*43f728cbSDavid du Colombier 	p += 2;		/* reserved */
2060*43f728cbSDavid du Colombier 	put32(p, ~0);	/* lifetime */
2061*43f728cbSDavid du Colombier 	p += 4;
2062*43f728cbSDavid du Colombier 
2063*43f728cbSDavid du Colombier 	/* BUG: scratch ptr? not clear what this is for */
2064*43f728cbSDavid du Colombier 	put32(p, PCIWADDR(ctlr->kwpage));
2065*43f728cbSDavid du Colombier 	p += 5;
2066*43f728cbSDavid du Colombier 
2067*43f728cbSDavid du Colombier 	*p++ = 60;	/* rts ntries */
2068*43f728cbSDavid du Colombier 	*p++ = 15;	/* data ntries */
2069*43f728cbSDavid du Colombier 	*p++ = 0;	/* tid */
2070*43f728cbSDavid du Colombier 	put16(p, 0);	/* timeout */
2071*43f728cbSDavid du Colombier 	p += 2;
2072*43f728cbSDavid du Colombier 	p += 2;		/* txop */
2073*43f728cbSDavid du Colombier 	if((err = qcmd(ctlr, 0, 28, c, p - c, b)) != nil){
2074*43f728cbSDavid du Colombier 		print("transmit: %s\n", err);
2075*43f728cbSDavid du Colombier 		freeb(b);
2076*43f728cbSDavid du Colombier 	}
2077*43f728cbSDavid du Colombier }
2078*43f728cbSDavid du Colombier 
2079*43f728cbSDavid du Colombier static long
iwlctl(Ether * edev,void * buf,long n)2080*43f728cbSDavid du Colombier iwlctl(Ether *edev, void *buf, long n)
2081*43f728cbSDavid du Colombier {
2082*43f728cbSDavid du Colombier 	Ctlr *ctlr;
2083*43f728cbSDavid du Colombier 
2084*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
2085*43f728cbSDavid du Colombier 	if(n >= 5 && memcmp(buf, "reset", 5) == 0){
2086*43f728cbSDavid du Colombier 		ctlr->broken = 1;
2087*43f728cbSDavid du Colombier 		return n;
2088*43f728cbSDavid du Colombier 	}
2089*43f728cbSDavid du Colombier 	if(ctlr->wifi)
2090*43f728cbSDavid du Colombier 		return wifictl(ctlr->wifi, buf, n);
2091*43f728cbSDavid du Colombier 	return 0;
2092*43f728cbSDavid du Colombier }
2093*43f728cbSDavid du Colombier 
2094*43f728cbSDavid du Colombier static long
iwlifstat(Ether * edev,void * buf,long n,ulong off)2095*43f728cbSDavid du Colombier iwlifstat(Ether *edev, void *buf, long n, ulong off)
2096*43f728cbSDavid du Colombier {
2097*43f728cbSDavid du Colombier 	Ctlr *ctlr;
2098*43f728cbSDavid du Colombier 
2099*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
2100*43f728cbSDavid du Colombier 	if(ctlr->wifi)
2101*43f728cbSDavid du Colombier 		return wifistat(ctlr->wifi, buf, n, off);
2102*43f728cbSDavid du Colombier 	return 0;
2103*43f728cbSDavid du Colombier }
2104*43f728cbSDavid du Colombier 
2105*43f728cbSDavid du Colombier static void
setoptions(Ether * edev)2106*43f728cbSDavid du Colombier setoptions(Ether *edev)
2107*43f728cbSDavid du Colombier {
2108*43f728cbSDavid du Colombier 	Ctlr *ctlr;
2109*43f728cbSDavid du Colombier 	int i;
2110*43f728cbSDavid du Colombier 
2111*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
2112*43f728cbSDavid du Colombier 	for(i = 0; i < edev->nopt; i++)
2113*43f728cbSDavid du Colombier 		wificfg(ctlr->wifi, edev->opt[i]);
2114*43f728cbSDavid du Colombier }
2115*43f728cbSDavid du Colombier 
2116*43f728cbSDavid du Colombier static void
iwlpromiscuous(void * arg,int on)2117*43f728cbSDavid du Colombier iwlpromiscuous(void *arg, int on)
2118*43f728cbSDavid du Colombier {
2119*43f728cbSDavid du Colombier 	Ether *edev;
2120*43f728cbSDavid du Colombier 	Ctlr *ctlr;
2121*43f728cbSDavid du Colombier 
2122*43f728cbSDavid du Colombier 	edev = arg;
2123*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
2124*43f728cbSDavid du Colombier 	qlock(ctlr);
2125*43f728cbSDavid du Colombier 	ctlr->prom = on;
2126*43f728cbSDavid du Colombier 	rxon(edev, ctlr->wifi->bss);
2127*43f728cbSDavid du Colombier 	qunlock(ctlr);
2128*43f728cbSDavid du Colombier }
2129*43f728cbSDavid du Colombier 
2130*43f728cbSDavid du Colombier static void
iwlmulticast(void *,uchar *,int)2131*43f728cbSDavid du Colombier iwlmulticast(void *, uchar*, int)
2132*43f728cbSDavid du Colombier {
2133*43f728cbSDavid du Colombier }
2134*43f728cbSDavid du Colombier 
2135*43f728cbSDavid du Colombier static void
iwlrecover(void * arg)2136*43f728cbSDavid du Colombier iwlrecover(void *arg)
2137*43f728cbSDavid du Colombier {
2138*43f728cbSDavid du Colombier 	Ether *edev;
2139*43f728cbSDavid du Colombier 	Ctlr *ctlr;
2140*43f728cbSDavid du Colombier 
2141*43f728cbSDavid du Colombier 	edev = arg;
2142*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
2143*43f728cbSDavid du Colombier 	while(waserror())
2144*43f728cbSDavid du Colombier 		;
2145*43f728cbSDavid du Colombier 	for(;;){
2146*43f728cbSDavid du Colombier 		tsleep(&up->sleep, return0, 0, 4000);
2147*43f728cbSDavid du Colombier 
2148*43f728cbSDavid du Colombier 		qlock(ctlr);
2149*43f728cbSDavid du Colombier 		for(;;){
2150*43f728cbSDavid du Colombier 			if(ctlr->broken == 0)
2151*43f728cbSDavid du Colombier 				break;
2152*43f728cbSDavid du Colombier 
2153*43f728cbSDavid du Colombier 			if(ctlr->power)
2154*43f728cbSDavid du Colombier 				poweroff(ctlr);
2155*43f728cbSDavid du Colombier 
2156*43f728cbSDavid du Colombier 			if((csr32r(ctlr, Gpc) & RfKill) == 0)
2157*43f728cbSDavid du Colombier 				break;
2158*43f728cbSDavid du Colombier 
2159*43f728cbSDavid du Colombier 			if(reset(ctlr) != nil)
2160*43f728cbSDavid du Colombier 				break;
2161*43f728cbSDavid du Colombier 			if(boot(ctlr) != nil)
2162*43f728cbSDavid du Colombier 				break;
2163*43f728cbSDavid du Colombier 
2164*43f728cbSDavid du Colombier 			ctlr->bcastnodeid = -1;
2165*43f728cbSDavid du Colombier 			ctlr->bssnodeid = -1;
2166*43f728cbSDavid du Colombier 			ctlr->aid = 0;
2167*43f728cbSDavid du Colombier 			rxon(edev, ctlr->wifi->bss);
2168*43f728cbSDavid du Colombier 			break;
2169*43f728cbSDavid du Colombier 		}
2170*43f728cbSDavid du Colombier 		qunlock(ctlr);
2171*43f728cbSDavid du Colombier 	}
2172*43f728cbSDavid du Colombier }
2173*43f728cbSDavid du Colombier 
2174*43f728cbSDavid du Colombier static void
iwlattach(Ether * edev)2175*43f728cbSDavid du Colombier iwlattach(Ether *edev)
2176*43f728cbSDavid du Colombier {
2177*43f728cbSDavid du Colombier 	FWImage *fw;
2178*43f728cbSDavid du Colombier 	Ctlr *ctlr;
2179*43f728cbSDavid du Colombier 	char *err;
2180*43f728cbSDavid du Colombier 
2181*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
2182*43f728cbSDavid du Colombier 	qlock(ctlr);
2183*43f728cbSDavid du Colombier 	if(waserror()){
2184*43f728cbSDavid du Colombier 		print("#l%d: %s\n", edev->ctlrno, up->errstr);
2185*43f728cbSDavid du Colombier 		if(ctlr->power)
2186*43f728cbSDavid du Colombier 			poweroff(ctlr);
2187*43f728cbSDavid du Colombier 		qunlock(ctlr);
2188*43f728cbSDavid du Colombier 		nexterror();
2189*43f728cbSDavid du Colombier 	}
2190*43f728cbSDavid du Colombier 	if(ctlr->attached == 0){
2191*43f728cbSDavid du Colombier 		if((csr32r(ctlr, Gpc) & RfKill) == 0)
2192*43f728cbSDavid du Colombier 			error("wifi disabled by switch");
2193*43f728cbSDavid du Colombier 
2194*43f728cbSDavid du Colombier 		if(ctlr->wifi == nil){
2195*43f728cbSDavid du Colombier 			ctlr->wifi = wifiattach(edev, transmit);
2196*43f728cbSDavid du Colombier 			/* tested with 2230, it has transmit issues using higher bit rates */
2197*43f728cbSDavid du Colombier 			if(ctlr->type != Type2030)
2198*43f728cbSDavid du Colombier 				ctlr->wifi->rates = iwlrates;
2199*43f728cbSDavid du Colombier 		}
2200*43f728cbSDavid du Colombier 
2201*43f728cbSDavid du Colombier 		if(ctlr->fw == nil){
2202*43f728cbSDavid du Colombier 			char *fn = fwname[ctlr->type];
2203*43f728cbSDavid du Colombier 			if(ctlr->type == Type6005){
2204*43f728cbSDavid du Colombier 				switch(ctlr->pdev->did){
2205*43f728cbSDavid du Colombier 				case 0x0082:	/* Centrino Advanced-N 6205 */
2206*43f728cbSDavid du Colombier 				case 0x0085:	/* Centrino Advanced-N 6205 */
2207*43f728cbSDavid du Colombier 					break;
2208*43f728cbSDavid du Colombier 				default:	/* Centrino Advanced-N 6030, 6235 */
2209*43f728cbSDavid du Colombier 					fn = "iwn-6030";
2210*43f728cbSDavid du Colombier 				}
2211*43f728cbSDavid du Colombier 			}
2212*43f728cbSDavid du Colombier 			fw = readfirmware(fn);
2213*43f728cbSDavid du Colombier 			print("#l%d: firmware: %s, rev %ux, build %ud, size %ux+%ux+%ux+%ux+%ux\n",
2214*43f728cbSDavid du Colombier 				edev->ctlrno, fn,
2215*43f728cbSDavid du Colombier 				fw->rev, fw->build,
2216*43f728cbSDavid du Colombier 				fw->main.text.size, fw->main.data.size,
2217*43f728cbSDavid du Colombier 				fw->init.text.size, fw->init.data.size,
2218*43f728cbSDavid du Colombier 				fw->boot.text.size);
2219*43f728cbSDavid du Colombier 			ctlr->fw = fw;
2220*43f728cbSDavid du Colombier 		}
2221*43f728cbSDavid du Colombier 
2222*43f728cbSDavid du Colombier 		if((err = reset(ctlr)) != nil)
2223*43f728cbSDavid du Colombier 			error(err);
2224*43f728cbSDavid du Colombier 		if((err = boot(ctlr)) != nil)
2225*43f728cbSDavid du Colombier 			error(err);
2226*43f728cbSDavid du Colombier 
2227*43f728cbSDavid du Colombier 		ctlr->bcastnodeid = -1;
2228*43f728cbSDavid du Colombier 		ctlr->bssnodeid = -1;
2229*43f728cbSDavid du Colombier 		ctlr->channel = 1;
2230*43f728cbSDavid du Colombier 		ctlr->aid = 0;
2231*43f728cbSDavid du Colombier 
2232*43f728cbSDavid du Colombier 		setoptions(edev);
2233*43f728cbSDavid du Colombier 
2234*43f728cbSDavid du Colombier 		ctlr->attached = 1;
2235*43f728cbSDavid du Colombier 
2236*43f728cbSDavid du Colombier 		kproc("iwlrecover", iwlrecover, edev);
2237*43f728cbSDavid du Colombier 	}
2238*43f728cbSDavid du Colombier 	qunlock(ctlr);
2239*43f728cbSDavid du Colombier 	poperror();
2240*43f728cbSDavid du Colombier }
2241*43f728cbSDavid du Colombier 
2242*43f728cbSDavid du Colombier static void
receive(Ctlr * ctlr)2243*43f728cbSDavid du Colombier receive(Ctlr *ctlr)
2244*43f728cbSDavid du Colombier {
2245*43f728cbSDavid du Colombier 	Block *b, *bb;
2246*43f728cbSDavid du Colombier 	uchar *d;
2247*43f728cbSDavid du Colombier 	RXQ *rx;
2248*43f728cbSDavid du Colombier 	TXQ *tx;
2249*43f728cbSDavid du Colombier 	uint hw;
2250*43f728cbSDavid du Colombier 
2251*43f728cbSDavid du Colombier 	rx = &ctlr->rx;
2252*43f728cbSDavid du Colombier 	if(ctlr->broken || rx->s == nil || rx->b == nil)
2253*43f728cbSDavid du Colombier 		return;
2254*43f728cbSDavid du Colombier 
2255*43f728cbSDavid du Colombier 	bb = nil;
2256*43f728cbSDavid du Colombier 	for(hw = get16(rx->s) % Nrx; rx->i != hw; rx->i = (rx->i + 1) % Nrx){
2257*43f728cbSDavid du Colombier 		uchar type, flags, idx, qid;
2258*43f728cbSDavid du Colombier 		u32int len;
2259*43f728cbSDavid du Colombier 
2260*43f728cbSDavid du Colombier 		b = rx->b[rx->i];
2261*43f728cbSDavid du Colombier 		if(b == nil)
2262*43f728cbSDavid du Colombier 			continue;
2263*43f728cbSDavid du Colombier 
2264*43f728cbSDavid du Colombier 		d = b->rp;
2265*43f728cbSDavid du Colombier 		len = get32(d); d += 4;
2266*43f728cbSDavid du Colombier 		type = *d++;
2267*43f728cbSDavid du Colombier 		flags = *d++;
2268*43f728cbSDavid du Colombier 		USED(flags);
2269*43f728cbSDavid du Colombier 		idx = *d++;
2270*43f728cbSDavid du Colombier 		qid = *d++;
2271*43f728cbSDavid du Colombier 
2272*43f728cbSDavid du Colombier 		if(bb != nil){
2273*43f728cbSDavid du Colombier 			freeb(bb);
2274*43f728cbSDavid du Colombier 			bb = nil;
2275*43f728cbSDavid du Colombier 		}
2276*43f728cbSDavid du Colombier 		if((qid & 0x80) == 0 && qid < nelem(ctlr->tx)){
2277*43f728cbSDavid du Colombier 			tx = &ctlr->tx[qid];
2278*43f728cbSDavid du Colombier 			if(tx->n > 0){
2279*43f728cbSDavid du Colombier 				bb = tx->b[idx];
2280*43f728cbSDavid du Colombier 				tx->b[idx] = nil;
2281*43f728cbSDavid du Colombier 				tx->n--;
2282*43f728cbSDavid du Colombier 
2283*43f728cbSDavid du Colombier 				wakeup(tx);
2284*43f728cbSDavid du Colombier 			}
2285*43f728cbSDavid du Colombier 		}
2286*43f728cbSDavid du Colombier 
2287*43f728cbSDavid du Colombier 		len &= 0x3fff;
2288*43f728cbSDavid du Colombier 		if(len < 4 || type == 0)
2289*43f728cbSDavid du Colombier 			continue;
2290*43f728cbSDavid du Colombier 
2291*43f728cbSDavid du Colombier 		len -= 4;
2292*43f728cbSDavid du Colombier 		switch(type){
2293*43f728cbSDavid du Colombier 		case 1:		/* microcontroller ready */
2294*43f728cbSDavid du Colombier 			setfwinfo(ctlr, d, len);
2295*43f728cbSDavid du Colombier 			break;
2296*43f728cbSDavid du Colombier 		case 24:	/* add node done */
2297*43f728cbSDavid du Colombier 			break;
2298*43f728cbSDavid du Colombier 		case 28:	/* tx done */
2299*43f728cbSDavid du Colombier 			if(ctlr->type == Type4965){
2300*43f728cbSDavid du Colombier 				if(len <= 20 || d[20] == 1 || d[20] == 2)
2301*43f728cbSDavid du Colombier 					break;
2302*43f728cbSDavid du Colombier 			} else {
2303*43f728cbSDavid du Colombier 				if(len <= 32 || d[32] == 1 || d[32] == 2)
2304*43f728cbSDavid du Colombier 					break;
2305*43f728cbSDavid du Colombier 			}
2306*43f728cbSDavid du Colombier 			wifitxfail(ctlr->wifi, bb);
2307*43f728cbSDavid du Colombier 			break;
2308*43f728cbSDavid du Colombier 		case 102:	/* calibration result (Type5000 only) */
2309*43f728cbSDavid du Colombier 			if(len < 4)
2310*43f728cbSDavid du Colombier 				break;
2311*43f728cbSDavid du Colombier 			idx = d[0];
2312*43f728cbSDavid du Colombier 			if(idx >= nelem(ctlr->calib.cmd))
2313*43f728cbSDavid du Colombier 				break;
2314*43f728cbSDavid du Colombier 			if(rbplant(ctlr, rx->i) < 0)
2315*43f728cbSDavid du Colombier 				break;
2316*43f728cbSDavid du Colombier 			if(ctlr->calib.cmd[idx] != nil)
2317*43f728cbSDavid du Colombier 				freeb(ctlr->calib.cmd[idx]);
2318*43f728cbSDavid du Colombier 			b->rp = d;
2319*43f728cbSDavid du Colombier 			b->wp = d + len;
2320*43f728cbSDavid du Colombier 			ctlr->calib.cmd[idx] = b;
2321*43f728cbSDavid du Colombier 			continue;
2322*43f728cbSDavid du Colombier 		case 103:	/* calibration done (Type5000 only) */
2323*43f728cbSDavid du Colombier 			ctlr->calib.done = 1;
2324*43f728cbSDavid du Colombier 			break;
2325*43f728cbSDavid du Colombier 		case 130:	/* start scan */
2326*43f728cbSDavid du Colombier 			break;
2327*43f728cbSDavid du Colombier 		case 132:	/* stop scan */
2328*43f728cbSDavid du Colombier 			break;
2329*43f728cbSDavid du Colombier 		case 156:	/* rx statistics */
2330*43f728cbSDavid du Colombier 			break;
2331*43f728cbSDavid du Colombier 		case 157:	/* beacon statistics */
2332*43f728cbSDavid du Colombier 			break;
2333*43f728cbSDavid du Colombier 		case 161:	/* state changed */
2334*43f728cbSDavid du Colombier 			break;
2335*43f728cbSDavid du Colombier 		case 162:	/* beacon missed */
2336*43f728cbSDavid du Colombier 			break;
2337*43f728cbSDavid du Colombier 		case 192:	/* rx phy */
2338*43f728cbSDavid du Colombier 			break;
2339*43f728cbSDavid du Colombier 		case 195:	/* rx done */
2340*43f728cbSDavid du Colombier 			if(d + 2 > b->lim)
2341*43f728cbSDavid du Colombier 				break;
2342*43f728cbSDavid du Colombier 			d += d[1];
2343*43f728cbSDavid du Colombier 			d += 56;
2344*43f728cbSDavid du Colombier 		case 193:	/* mpdu rx done */
2345*43f728cbSDavid du Colombier 			if(d + 4 > b->lim)
2346*43f728cbSDavid du Colombier 				break;
2347*43f728cbSDavid du Colombier 			len = get16(d); d += 4;
2348*43f728cbSDavid du Colombier 			if(d + len + 4 > b->lim)
2349*43f728cbSDavid du Colombier 				break;
2350*43f728cbSDavid du Colombier 			if((get32(d + len) & 3) != 3)
2351*43f728cbSDavid du Colombier 				break;
2352*43f728cbSDavid du Colombier 			if(ctlr->wifi == nil)
2353*43f728cbSDavid du Colombier 				break;
2354*43f728cbSDavid du Colombier 			if(rbplant(ctlr, rx->i) < 0)
2355*43f728cbSDavid du Colombier 				break;
2356*43f728cbSDavid du Colombier 			b->rp = d;
2357*43f728cbSDavid du Colombier 			b->wp = d + len;
2358*43f728cbSDavid du Colombier 			wifiiq(ctlr->wifi, b);
2359*43f728cbSDavid du Colombier 			continue;
2360*43f728cbSDavid du Colombier 		case 197:	/* rx compressed ba */
2361*43f728cbSDavid du Colombier 			break;
2362*43f728cbSDavid du Colombier 		}
2363*43f728cbSDavid du Colombier 	}
2364*43f728cbSDavid du Colombier 	csr32w(ctlr, FhRxWptr, ((hw+Nrx-1) % Nrx) & ~7);
2365*43f728cbSDavid du Colombier 	if(bb != nil)
2366*43f728cbSDavid du Colombier 		freeb(bb);
2367*43f728cbSDavid du Colombier }
2368*43f728cbSDavid du Colombier 
2369*43f728cbSDavid du Colombier static void
iwlinterrupt(Ureg *,void * arg)2370*43f728cbSDavid du Colombier iwlinterrupt(Ureg*, void *arg)
2371*43f728cbSDavid du Colombier {
2372*43f728cbSDavid du Colombier 	u32int isr, fhisr;
2373*43f728cbSDavid du Colombier 	Ether *edev;
2374*43f728cbSDavid du Colombier 	Ctlr *ctlr;
2375*43f728cbSDavid du Colombier 
2376*43f728cbSDavid du Colombier 	edev = arg;
2377*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
2378*43f728cbSDavid du Colombier 	ilock(ctlr);
2379*43f728cbSDavid du Colombier 	csr32w(ctlr, Imr, 0);
2380*43f728cbSDavid du Colombier 	isr = csr32r(ctlr, Isr);
2381*43f728cbSDavid du Colombier 	fhisr = csr32r(ctlr, FhIsr);
2382*43f728cbSDavid du Colombier 	if(isr == 0xffffffff || (isr & 0xfffffff0) == 0xa5a5a5a0){
2383*43f728cbSDavid du Colombier 		iunlock(ctlr);
2384*43f728cbSDavid du Colombier 		return;
2385*43f728cbSDavid du Colombier 	}
2386*43f728cbSDavid du Colombier 	if(isr == 0 && fhisr == 0)
2387*43f728cbSDavid du Colombier 		goto done;
2388*43f728cbSDavid du Colombier 	csr32w(ctlr, Isr, isr);
2389*43f728cbSDavid du Colombier 	csr32w(ctlr, FhIsr, fhisr);
2390*43f728cbSDavid du Colombier 	if((isr & (Iswrx | Ifhrx | Irxperiodic)) || (fhisr & Ifhrx))
2391*43f728cbSDavid du Colombier 		receive(ctlr);
2392*43f728cbSDavid du Colombier 	if(isr & Ierr){
2393*43f728cbSDavid du Colombier 		ctlr->broken = 1;
2394*43f728cbSDavid du Colombier 		print("#l%d: fatal firmware error\n", edev->ctlrno);
2395*43f728cbSDavid du Colombier 		dumpctlr(ctlr);
2396*43f728cbSDavid du Colombier 	}
2397*43f728cbSDavid du Colombier 	ctlr->wait.m |= isr;
2398*43f728cbSDavid du Colombier 	if(ctlr->wait.m & ctlr->wait.w)
2399*43f728cbSDavid du Colombier 		wakeup(&ctlr->wait);
2400*43f728cbSDavid du Colombier done:
2401*43f728cbSDavid du Colombier 	csr32w(ctlr, Imr, ctlr->ie);
2402*43f728cbSDavid du Colombier 	iunlock(ctlr);
2403*43f728cbSDavid du Colombier }
2404*43f728cbSDavid du Colombier 
2405*43f728cbSDavid du Colombier static void
iwlshutdown(Ether * edev)2406*43f728cbSDavid du Colombier iwlshutdown(Ether *edev)
2407*43f728cbSDavid du Colombier {
2408*43f728cbSDavid du Colombier 	Ctlr *ctlr;
2409*43f728cbSDavid du Colombier 
2410*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
2411*43f728cbSDavid du Colombier 	if(ctlr->power)
2412*43f728cbSDavid du Colombier 		poweroff(ctlr);
2413*43f728cbSDavid du Colombier 	ctlr->broken = 0;
2414*43f728cbSDavid du Colombier }
2415*43f728cbSDavid du Colombier 
2416*43f728cbSDavid du Colombier static Ctlr *iwlhead, *iwltail;
2417*43f728cbSDavid du Colombier 
2418*43f728cbSDavid du Colombier static void
iwlpci(void)2419*43f728cbSDavid du Colombier iwlpci(void)
2420*43f728cbSDavid du Colombier {
2421*43f728cbSDavid du Colombier 	Pcidev *pdev;
2422*43f728cbSDavid du Colombier 
2423*43f728cbSDavid du Colombier 	pdev = nil;
2424*43f728cbSDavid du Colombier 	while(pdev = pcimatch(pdev, 0, 0)) {
2425*43f728cbSDavid du Colombier 		Ctlr *ctlr;
2426*43f728cbSDavid du Colombier 		void *mem;
2427*43f728cbSDavid du Colombier 
2428*43f728cbSDavid du Colombier 		if(pdev->ccrb != 2 || pdev->ccru != 0x80)
2429*43f728cbSDavid du Colombier 			continue;
2430*43f728cbSDavid du Colombier 		if(pdev->vid != 0x8086)
2431*43f728cbSDavid du Colombier 			continue;
2432*43f728cbSDavid du Colombier 
2433*43f728cbSDavid du Colombier 		switch(pdev->did){
2434*43f728cbSDavid du Colombier 		default:
2435*43f728cbSDavid du Colombier 			continue;
2436*43f728cbSDavid du Colombier 		case 0x0084:	/* WiFi Link 1000 */
2437*43f728cbSDavid du Colombier 		case 0x4229:	/* WiFi Link 4965 */
2438*43f728cbSDavid du Colombier 		case 0x4230:	/* WiFi Link 4965 */
2439*43f728cbSDavid du Colombier 		case 0x4232:	/* Wifi Link 5100 */
2440*43f728cbSDavid du Colombier 		case 0x4236:	/* WiFi Link 5300 AGN */
2441*43f728cbSDavid du Colombier 		case 0x4237:	/* Wifi Link 5100 AGN */
2442*43f728cbSDavid du Colombier 		case 0x423d:	/* Wifi Link 5150 */
2443*43f728cbSDavid du Colombier 		case 0x423b:	/* PRO/Wireless 5350 AGN */
2444*43f728cbSDavid du Colombier 		case 0x0082:	/* Centrino Advanced-N 6205 */
2445*43f728cbSDavid du Colombier 		case 0x0085:	/* Centrino Advanced-N 6205 */
2446*43f728cbSDavid du Colombier 		case 0x422b:	/* Centrino Ultimate-N 6300 variant 1 */
2447*43f728cbSDavid du Colombier 		case 0x4238:	/* Centrino Ultimate-N 6300 variant 2 */
2448*43f728cbSDavid du Colombier 		case 0x08ae:	/* Centrino Wireless-N 100 */
2449*43f728cbSDavid du Colombier 		case 0x0083:	/* Centrino Wireless-N 1000 */
2450*43f728cbSDavid du Colombier 		case 0x0887:	/* Centrino Wireless-N 2230 */
2451*43f728cbSDavid du Colombier 		case 0x0888:	/* Centrino Wireless-N 2230 */
2452*43f728cbSDavid du Colombier 		case 0x0090:	/* Centrino Advanced-N 6030 */
2453*43f728cbSDavid du Colombier 		case 0x0091:	/* Centrino Advanced-N 6030 */
2454*43f728cbSDavid du Colombier 		case 0x088e:	/* Centrino Advanced-N 6235 */
2455*43f728cbSDavid du Colombier 		case 0x088f:	/* Centrino Advanced-N 6235 */
2456*43f728cbSDavid du Colombier 			break;
2457*43f728cbSDavid du Colombier 		}
2458*43f728cbSDavid du Colombier 
2459*43f728cbSDavid du Colombier 		/* Clear device-specific "PCI retry timeout" register (41h). */
2460*43f728cbSDavid du Colombier 		if(pcicfgr8(pdev, 0x41) != 0)
2461*43f728cbSDavid du Colombier 			pcicfgw8(pdev, 0x41, 0);
2462*43f728cbSDavid du Colombier 
2463*43f728cbSDavid du Colombier 		/* Clear interrupt disable bit. Hardware bug workaround. */
2464*43f728cbSDavid du Colombier 		if(pdev->pcr & 0x400){
2465*43f728cbSDavid du Colombier 			pdev->pcr &= ~0x400;
2466*43f728cbSDavid du Colombier 			pcicfgw16(pdev, PciPCR, pdev->pcr);
2467*43f728cbSDavid du Colombier 		}
2468*43f728cbSDavid du Colombier 
2469*43f728cbSDavid du Colombier 		pcisetbme(pdev);
2470*43f728cbSDavid du Colombier 		pcisetpms(pdev, 0);
2471*43f728cbSDavid du Colombier 
2472*43f728cbSDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
2473*43f728cbSDavid du Colombier 		if(ctlr == nil) {
2474*43f728cbSDavid du Colombier 			print("iwl: unable to alloc Ctlr\n");
2475*43f728cbSDavid du Colombier 			continue;
2476*43f728cbSDavid du Colombier 		}
2477*43f728cbSDavid du Colombier 		ctlr->port = pdev->mem[0].bar & ~0x0F;
2478*43f728cbSDavid du Colombier 		mem = vmap(pdev->mem[0].bar & ~0x0F, pdev->mem[0].size);
2479*43f728cbSDavid du Colombier 		if(mem == nil) {
2480*43f728cbSDavid du Colombier 			print("iwl: can't map %8.8luX\n", pdev->mem[0].bar);
2481*43f728cbSDavid du Colombier 			free(ctlr);
2482*43f728cbSDavid du Colombier 			continue;
2483*43f728cbSDavid du Colombier 		}
2484*43f728cbSDavid du Colombier 		ctlr->nic = mem;
2485*43f728cbSDavid du Colombier 		ctlr->pdev = pdev;
2486*43f728cbSDavid du Colombier 		ctlr->type = (csr32r(ctlr, Rev) >> 4) & 0x1F;
2487*43f728cbSDavid du Colombier 
2488*43f728cbSDavid du Colombier 		if(fwname[ctlr->type] == nil){
2489*43f728cbSDavid du Colombier 			print("iwl: unsupported controller type %d\n", ctlr->type);
2490*43f728cbSDavid du Colombier 			vunmap(mem, pdev->mem[0].size);
2491*43f728cbSDavid du Colombier 			free(ctlr);
2492*43f728cbSDavid du Colombier 			continue;
2493*43f728cbSDavid du Colombier 		}
2494*43f728cbSDavid du Colombier 
2495*43f728cbSDavid du Colombier 		if(iwlhead != nil)
2496*43f728cbSDavid du Colombier 			iwltail->link = ctlr;
2497*43f728cbSDavid du Colombier 		else
2498*43f728cbSDavid du Colombier 			iwlhead = ctlr;
2499*43f728cbSDavid du Colombier 		iwltail = ctlr;
2500*43f728cbSDavid du Colombier 	}
2501*43f728cbSDavid du Colombier }
2502*43f728cbSDavid du Colombier 
2503*43f728cbSDavid du Colombier static int
iwlpnp(Ether * edev)2504*43f728cbSDavid du Colombier iwlpnp(Ether* edev)
2505*43f728cbSDavid du Colombier {
2506*43f728cbSDavid du Colombier 	Ctlr *ctlr;
2507*43f728cbSDavid du Colombier 
2508*43f728cbSDavid du Colombier 	if(iwlhead == nil)
2509*43f728cbSDavid du Colombier 		iwlpci();
2510*43f728cbSDavid du Colombier again:
2511*43f728cbSDavid du Colombier 	for(ctlr = iwlhead; ctlr != nil; ctlr = ctlr->link){
2512*43f728cbSDavid du Colombier 		if(ctlr->active)
2513*43f728cbSDavid du Colombier 			continue;
2514*43f728cbSDavid du Colombier 		if(edev->port == 0 || edev->port == ctlr->port){
2515*43f728cbSDavid du Colombier 			ctlr->active = 1;
2516*43f728cbSDavid du Colombier 			break;
2517*43f728cbSDavid du Colombier 		}
2518*43f728cbSDavid du Colombier 	}
2519*43f728cbSDavid du Colombier 
2520*43f728cbSDavid du Colombier 	if(ctlr == nil)
2521*43f728cbSDavid du Colombier 		return -1;
2522*43f728cbSDavid du Colombier 
2523*43f728cbSDavid du Colombier 	edev->ctlr = ctlr;
2524*43f728cbSDavid du Colombier 	edev->port = ctlr->port;
2525*43f728cbSDavid du Colombier 	edev->irq = ctlr->pdev->intl;
2526*43f728cbSDavid du Colombier 	edev->tbdf = ctlr->pdev->tbdf;
2527*43f728cbSDavid du Colombier 	edev->arg = edev;
2528*43f728cbSDavid du Colombier 	edev->interrupt = iwlinterrupt;
2529*43f728cbSDavid du Colombier 	edev->attach = iwlattach;
2530*43f728cbSDavid du Colombier 	edev->ifstat = iwlifstat;
2531*43f728cbSDavid du Colombier 	edev->ctl = iwlctl;
2532*43f728cbSDavid du Colombier 	edev->shutdown = iwlshutdown;
2533*43f728cbSDavid du Colombier 	edev->promiscuous = iwlpromiscuous;
2534*43f728cbSDavid du Colombier 	edev->multicast = iwlmulticast;
2535*43f728cbSDavid du Colombier 	edev->mbps = 54;
2536*43f728cbSDavid du Colombier 
2537*43f728cbSDavid du Colombier 	if(iwlinit(edev) < 0){
2538*43f728cbSDavid du Colombier 		edev->ctlr = nil;
2539*43f728cbSDavid du Colombier 		goto again;
2540*43f728cbSDavid du Colombier 	}
2541*43f728cbSDavid du Colombier 
2542*43f728cbSDavid du Colombier 	return 0;
2543*43f728cbSDavid du Colombier }
2544*43f728cbSDavid du Colombier 
2545*43f728cbSDavid du Colombier void
etheriwllink(void)2546*43f728cbSDavid du Colombier etheriwllink(void)
2547*43f728cbSDavid du Colombier {
2548*43f728cbSDavid du Colombier 	addethercard("iwl", iwlpnp);
2549*43f728cbSDavid du Colombier }
2550