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