xref: /plan9-contrib/sys/src/9/pc/etherrt2860.c (revision 43f728cba48694b0a42c538a38fdf975a0ae5433)
1*43f728cbSDavid du Colombier /*
2*43f728cbSDavid du Colombier  * Ralink RT2860 driver
3*43f728cbSDavid du Colombier  *
4*43f728cbSDavid du Colombier  * Written without any documentation but Damien Bergaminis
5*43f728cbSDavid du Colombier  * OpenBSD ral(4) driver sources. Requires ralink firmware
6*43f728cbSDavid du Colombier  * to be present in /lib/firmware/ral-rt2860 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 /* for consistency */
22*43f728cbSDavid du Colombier typedef signed char s8int;
23*43f728cbSDavid du Colombier 
24*43f728cbSDavid du Colombier enum {
25*43f728cbSDavid du Colombier 	/* PCI registers */
26*43f728cbSDavid du Colombier 	PciCfg = 0x0000,
27*43f728cbSDavid du Colombier 		PciCfgUsb = (1 << 17),
28*43f728cbSDavid du Colombier 		PciCfgPci = (1 << 16),
29*43f728cbSDavid du Colombier 	PciEectrl = 0x0004,
30*43f728cbSDavid du Colombier 		EectrlC = (1 << 0),
31*43f728cbSDavid du Colombier 		EectrlS = (1 << 1),
32*43f728cbSDavid du Colombier 		EectrlD = (1 << 2),
33*43f728cbSDavid du Colombier 		EectrlShiftD = 2,
34*43f728cbSDavid du Colombier 		EectrlQ = (1 << 3),
35*43f728cbSDavid du Colombier 		EectrlShiftQ = 3,
36*43f728cbSDavid du Colombier 	PciMcuctrl = 0x0008,
37*43f728cbSDavid du Colombier 	PciSysctrl = 0x000c,
38*43f728cbSDavid du Colombier 	PcieJtag = 0x0010,
39*43f728cbSDavid du Colombier 
40*43f728cbSDavid du Colombier 	Rt3090AuxCtrl = 0x010c,
41*43f728cbSDavid du Colombier 
42*43f728cbSDavid du Colombier 	Rt3070Opt14 = 0x0114,
43*43f728cbSDavid du Colombier };
44*43f728cbSDavid du Colombier 
45*43f728cbSDavid du Colombier enum {
46*43f728cbSDavid du Colombier 	/* SCH/DMA registers */
47*43f728cbSDavid du Colombier 	IntStatus = 0x0200,
48*43f728cbSDavid du Colombier 		/* flags for registers IntStatus/IntMask */
49*43f728cbSDavid du Colombier 		TxCoherent = (1 << 17),
50*43f728cbSDavid du Colombier 		RxCoherent = (1 << 16),
51*43f728cbSDavid du Colombier 		MacInt4 = (1 << 15),
52*43f728cbSDavid du Colombier 		MacInt3 = (1 << 14),
53*43f728cbSDavid du Colombier 		MacInt2 = (1 << 13),
54*43f728cbSDavid du Colombier 		MacInt1 = (1 << 12),
55*43f728cbSDavid du Colombier 		MacInt0 = (1 << 11),
56*43f728cbSDavid du Colombier 		TxRxCoherent = (1 << 10),
57*43f728cbSDavid du Colombier 		McuCmdInt = (1 << 9),
58*43f728cbSDavid du Colombier 		TxDoneInt5 = (1 << 8),
59*43f728cbSDavid du Colombier 		TxDoneInt4 = (1 << 7),
60*43f728cbSDavid du Colombier 		TxDoneInt3 = (1 << 6),
61*43f728cbSDavid du Colombier 		TxDoneInt2 = (1 << 5),
62*43f728cbSDavid du Colombier 		TxDoneInt1 = (1 << 4),
63*43f728cbSDavid du Colombier 		TxDoneInt0 = (1 << 3),
64*43f728cbSDavid du Colombier 		RxDoneInt = (1 << 2),
65*43f728cbSDavid du Colombier 		TxDlyInt = (1 << 1),
66*43f728cbSDavid du Colombier 		RxDlyInt = (1 << 0),
67*43f728cbSDavid du Colombier 	IntMask = 0x0204,
68*43f728cbSDavid du Colombier 	WpdmaGloCfg = 0x0208,
69*43f728cbSDavid du Colombier 		HdrSegLenShift = 8,
70*43f728cbSDavid du Colombier 		BigEndian = (1 << 7),
71*43f728cbSDavid du Colombier 		TxWbDdone = (1 << 6),
72*43f728cbSDavid du Colombier 		WpdmaBtSizeShift = 4,
73*43f728cbSDavid du Colombier 		WpdmaBtSize16 = 0,
74*43f728cbSDavid du Colombier 		WpdmaBtSize32 = 1,
75*43f728cbSDavid du Colombier 		WpdmaBtSize64 = 2,
76*43f728cbSDavid du Colombier 		WpdmaBtSize128 = 3,
77*43f728cbSDavid du Colombier 		RxDmaBusy = (1 << 3),
78*43f728cbSDavid du Colombier 		RxDmaEn = (1 << 2),
79*43f728cbSDavid du Colombier 		TxDmaBusy = (1 << 1),
80*43f728cbSDavid du Colombier 		TxDmaEn = (1 << 0),
81*43f728cbSDavid du Colombier 	WpdmaRstIdx = 0x020c,
82*43f728cbSDavid du Colombier 	DelayIntCfg = 0x0210,
83*43f728cbSDavid du Colombier 		TxdlyIntEn = (1 << 31),
84*43f728cbSDavid du Colombier 		TxmaxPintShift = 24,
85*43f728cbSDavid du Colombier 		TxmaxPtimeShift = 16,
86*43f728cbSDavid du Colombier 		RxdlyIntEn = (1 << 15),
87*43f728cbSDavid du Colombier 		RxmaxPintShift = 8,
88*43f728cbSDavid du Colombier 		RxmaxPtimeShift = 0,
89*43f728cbSDavid du Colombier 	WmmAifsnCfg = 0x0214,
90*43f728cbSDavid du Colombier 	WmmCwminCfg = 0x0218,
91*43f728cbSDavid du Colombier 	WmmCwmaxCfg = 0x021c,
92*43f728cbSDavid du Colombier 	WmmTxop0Cfg = 0x0220,
93*43f728cbSDavid du Colombier 	WmmTxop1Cfg = 0x0224,
94*43f728cbSDavid du Colombier 	GpioCtrl = 0x0228,
95*43f728cbSDavid du Colombier 		GpioDShift = 8,
96*43f728cbSDavid du Colombier 		GpioOShift = 0,
97*43f728cbSDavid du Colombier 	McuCmdReg = 0x022c,
98*43f728cbSDavid du Colombier #define TxBasePtr(qid) (0x0230 + (qid) * 16)
99*43f728cbSDavid du Colombier #define TxMaxCnt(qid) (0x0234 + (qid) * 16)
100*43f728cbSDavid du Colombier #define TxCtxIdx(qid) (0x0238 + (qid) * 16)
101*43f728cbSDavid du Colombier #define TxDtxIdx(qid) (0x023c + (qid) * 16)
102*43f728cbSDavid du Colombier 	RxBasePtr = 0x0290,
103*43f728cbSDavid du Colombier 	RxMaxCnt = 0x0294,
104*43f728cbSDavid du Colombier 	RxCalcIdx = 0x0298,
105*43f728cbSDavid du Colombier 	FsDrxIdx = 0x029c,
106*43f728cbSDavid du Colombier 	UsbDmaCfg = 0x02a0 /* RT2870 only */,
107*43f728cbSDavid du Colombier 		UsbTxBusy = (1 << 31),
108*43f728cbSDavid du Colombier 		UsbRxBusy = (1 << 30),
109*43f728cbSDavid du Colombier 		UsbEpoutVldShift = 24,
110*43f728cbSDavid du Colombier 		UsbTxEn = (1 << 23),
111*43f728cbSDavid du Colombier 		UsbRxEn = (1 << 22),
112*43f728cbSDavid du Colombier 		UsbRxAggEn = (1 << 21),
113*43f728cbSDavid du Colombier 		UsbTxopHalt = (1 << 20),
114*43f728cbSDavid du Colombier 		UsbTxClear = (1 << 19),
115*43f728cbSDavid du Colombier 		UsbPhyWdEn = (1 << 16),
116*43f728cbSDavid du Colombier 		UsbPhyManRst = (1 << 15),
117*43f728cbSDavid du Colombier #define UsbRxAggLmt(x) ((x) << 8) /* in unit of 1KB */
118*43f728cbSDavid du Colombier #define UsbRxAggTo(x) ((x) & 0xff) /* in unit of 33ns */
119*43f728cbSDavid du Colombier 	UsCycCnt = 0x02a4,
120*43f728cbSDavid du Colombier 		TestEn = (1 << 24),
121*43f728cbSDavid du Colombier 		TestSelShift = 16,
122*43f728cbSDavid du Colombier 		BtModeEn = (1 << 8),
123*43f728cbSDavid du Colombier 		UsCycCntShift = 0,
124*43f728cbSDavid du Colombier };
125*43f728cbSDavid du Colombier 
126*43f728cbSDavid du Colombier enum {
127*43f728cbSDavid du Colombier 	/* PBF registers */
128*43f728cbSDavid du Colombier 	SysCtrl = 0x0400,
129*43f728cbSDavid du Colombier 		HstPmSel = (1 << 16),
130*43f728cbSDavid du Colombier 		CapMode = (1 << 14),
131*43f728cbSDavid du Colombier 		PmeOen = (1 << 13),
132*43f728cbSDavid du Colombier 		Clkselect = (1 << 12),
133*43f728cbSDavid du Colombier 		PbfClkEn = (1 << 11),
134*43f728cbSDavid du Colombier 		MacClkEn = (1 << 10),
135*43f728cbSDavid du Colombier 		DmaClkEn = (1 << 9),
136*43f728cbSDavid du Colombier 		McuReady = (1 << 7),
137*43f728cbSDavid du Colombier 		AsyReset = (1 << 4),
138*43f728cbSDavid du Colombier 		PbfReset = (1 << 3),
139*43f728cbSDavid du Colombier 		MacReset = (1 << 2),
140*43f728cbSDavid du Colombier 		DmaReset = (1 << 1),
141*43f728cbSDavid du Colombier 		McuReset = (1 << 0),
142*43f728cbSDavid du Colombier 	HostCmd = 0x0404,
143*43f728cbSDavid du Colombier 		McuCmdSleep = 0x30,
144*43f728cbSDavid du Colombier 		McuCmdWakeup = 0x31,
145*43f728cbSDavid du Colombier 		McuCmdLeds = 0x50,
146*43f728cbSDavid du Colombier 			LedRadio = (1 << 13),
147*43f728cbSDavid du Colombier 			LedLink2ghz = (1 << 14),
148*43f728cbSDavid du Colombier 			LedLink5ghz = (1 << 15),
149*43f728cbSDavid du Colombier 		McuCmdLedRssi = 0x51,
150*43f728cbSDavid du Colombier 		McuCmdLed1 = 0x52,
151*43f728cbSDavid du Colombier 		McuCmdLed2 = 0x53,
152*43f728cbSDavid du Colombier 		McuCmdLed3 = 0x54,
153*43f728cbSDavid du Colombier 		McuCmdRfreset = 0x72,
154*43f728cbSDavid du Colombier 		McuCmdAntsel = 0x73,
155*43f728cbSDavid du Colombier 		McuCmdBbp = 0x80,
156*43f728cbSDavid du Colombier 		McuCmdPslevel = 0x83,
157*43f728cbSDavid du Colombier 	PbfCfg = 0x0408,
158*43f728cbSDavid du Colombier 		Tx1qNumShift = 21,
159*43f728cbSDavid du Colombier 		Tx2qNumShift = 16,
160*43f728cbSDavid du Colombier 		Null0Mode = (1 << 15),
161*43f728cbSDavid du Colombier 		Null1Mode = (1 << 14),
162*43f728cbSDavid du Colombier 		RxDropMode = (1 << 13),
163*43f728cbSDavid du Colombier 		Tx0qManual = (1 << 12),
164*43f728cbSDavid du Colombier 		Tx1qManual = (1 << 11),
165*43f728cbSDavid du Colombier 		Tx2qManual = (1 << 10),
166*43f728cbSDavid du Colombier 		Rx0qManual = (1 << 9),
167*43f728cbSDavid du Colombier 		HccaEn = (1 << 8),
168*43f728cbSDavid du Colombier 		Tx0qEn = (1 << 4),
169*43f728cbSDavid du Colombier 		Tx1qEn = (1 << 3),
170*43f728cbSDavid du Colombier 		Tx2qEn = (1 << 2),
171*43f728cbSDavid du Colombier 		Rx0qEn = (1 << 1),
172*43f728cbSDavid du Colombier 	MaxPcnt = 0x040c,
173*43f728cbSDavid du Colombier 	BufCtrl = 0x0410,
174*43f728cbSDavid du Colombier #define WriteTxq(qid) (1 << (11 - (qid)))
175*43f728cbSDavid du Colombier 		Null0Kick = (1 << 7),
176*43f728cbSDavid du Colombier 		Null1Kick = (1 << 6),
177*43f728cbSDavid du Colombier 		BufReset = (1 << 5),
178*43f728cbSDavid du Colombier #define ReadTxq(qid) = (1 << (3 - (qid))
179*43f728cbSDavid du Colombier 		ReadRx0q = (1 << 0),
180*43f728cbSDavid du Colombier 	McuIntSta = 0x0414,
181*43f728cbSDavid du Colombier 		/* flags for registers McuIntSta/McuIntEna */
182*43f728cbSDavid du Colombier 		McuMacInt8 = (1 << 24),
183*43f728cbSDavid du Colombier 		McuMacInt7 = (1 << 23),
184*43f728cbSDavid du Colombier 		McuMacInt6 = (1 << 22),
185*43f728cbSDavid du Colombier 		McuMacInt4 = (1 << 20),
186*43f728cbSDavid du Colombier 		McuMacInt3 = (1 << 19),
187*43f728cbSDavid du Colombier 		McuMacInt2 = (1 << 18),
188*43f728cbSDavid du Colombier 		McuMacInt1 = (1 << 17),
189*43f728cbSDavid du Colombier 		McuMacInt0 = (1 << 16),
190*43f728cbSDavid du Colombier 		Dtx0Int = (1 << 11),
191*43f728cbSDavid du Colombier 		Dtx1Int = (1 << 10),
192*43f728cbSDavid du Colombier 		Dtx2Int = (1 << 9),
193*43f728cbSDavid du Colombier 		Drx0Int = (1 << 8),
194*43f728cbSDavid du Colombier 		HcmdInt = (1 << 7),
195*43f728cbSDavid du Colombier 		N0txInt = (1 << 6),
196*43f728cbSDavid du Colombier 		N1txInt = (1 << 5),
197*43f728cbSDavid du Colombier 		BcntxInt = (1 << 4),
198*43f728cbSDavid du Colombier 		Mtx0Int = (1 << 3),
199*43f728cbSDavid du Colombier 		Mtx1Int = (1 << 2),
200*43f728cbSDavid du Colombier 		Mtx2Int = (1 << 1),
201*43f728cbSDavid du Colombier 		Mrx0Int = (1 << 0),
202*43f728cbSDavid du Colombier 	McuIntEna = 0x0418,
203*43f728cbSDavid du Colombier #define TxqIo(qid) (0x041c + (qid) * 4)
204*43f728cbSDavid du Colombier 	Rx0qIo = 0x0424,
205*43f728cbSDavid du Colombier 	BcnOffset0 = 0x042c,
206*43f728cbSDavid du Colombier 	BcnOffset1 = 0x0430,
207*43f728cbSDavid du Colombier 	TxrxqSta = 0x0434,
208*43f728cbSDavid du Colombier 	TxrxqPcnt = 0x0438,
209*43f728cbSDavid du Colombier 		Rx0qPcntMask = 0xff000000,
210*43f728cbSDavid du Colombier 		Tx2qPcntMask = 0x00ff0000,
211*43f728cbSDavid du Colombier 		Tx1qPcntMask = 0x0000ff00,
212*43f728cbSDavid du Colombier 		Tx0qPcntMask = 0x000000ff,
213*43f728cbSDavid du Colombier 	PbfDbg = 0x043c,
214*43f728cbSDavid du Colombier 	CapCtrl = 0x0440,
215*43f728cbSDavid du Colombier 		CapAdcFeq = (1 << 31),
216*43f728cbSDavid du Colombier 		CapStart = (1 << 30),
217*43f728cbSDavid du Colombier 		ManTrig = (1 << 29),
218*43f728cbSDavid du Colombier 		TrigOffsetShift = 16,
219*43f728cbSDavid du Colombier 		StartAddrShift = 0,
220*43f728cbSDavid du Colombier };
221*43f728cbSDavid du Colombier 
222*43f728cbSDavid du Colombier enum {
223*43f728cbSDavid du Colombier 	/* RT3070 registers */
224*43f728cbSDavid du Colombier 	Rt3070RfCsrCfg = 0x0500,
225*43f728cbSDavid du Colombier 		Rt3070RfKick = (1 << 17),
226*43f728cbSDavid du Colombier 		Rt3070RfWrite = (1 << 16),
227*43f728cbSDavid du Colombier 	Rt3070EfuseCtrl = 0x0580,
228*43f728cbSDavid du Colombier 		Rt3070SelEfuse = (1 << 31),
229*43f728cbSDavid du Colombier 		Rt3070EfsromKick = (1 << 30),
230*43f728cbSDavid du Colombier 		Rt3070EfsromAinMask = 0x03ff0000,
231*43f728cbSDavid du Colombier 		Rt3070EfsromAinShift = 16,
232*43f728cbSDavid du Colombier 		Rt3070EfsromModeMask = 0x000000c0,
233*43f728cbSDavid du Colombier 		Rt3070EfuseAoutMask = 0x0000003f,
234*43f728cbSDavid du Colombier 	Rt3070EfuseData0 = 0x0590,
235*43f728cbSDavid du Colombier 	Rt3070EfuseData1 = 0x0594,
236*43f728cbSDavid du Colombier 	Rt3070EfuseData2 = 0x0598,
237*43f728cbSDavid du Colombier 	Rt3070EfuseData3 = 0x059c,
238*43f728cbSDavid du Colombier 	Rt3090OscCtrl = 0x05a4,
239*43f728cbSDavid du Colombier 	Rt3070LdoCfg0 = 0x05d4,
240*43f728cbSDavid du Colombier 	Rt3070GpioSwitch = 0x05dc,
241*43f728cbSDavid du Colombier };
242*43f728cbSDavid du Colombier 
243*43f728cbSDavid du Colombier enum {
244*43f728cbSDavid du Colombier 	/* MAC registers */
245*43f728cbSDavid du Colombier 	AsicVerId = 0x1000,
246*43f728cbSDavid du Colombier 	MacSysCtrl = 0x1004,
247*43f728cbSDavid du Colombier 		RxTsEn = (1 << 7),
248*43f728cbSDavid du Colombier 		WlanHaltEn = (1 << 6),
249*43f728cbSDavid du Colombier 		PbfLoopEn = (1 << 5),
250*43f728cbSDavid du Colombier 		ContTxTest = (1 << 4),
251*43f728cbSDavid du Colombier 		MacRxEn = (1 << 3),
252*43f728cbSDavid du Colombier 		MacTxEn = (1 << 2),
253*43f728cbSDavid du Colombier 		BbpHrst = (1 << 1),
254*43f728cbSDavid du Colombier 		MacSrst = (1 << 0),
255*43f728cbSDavid du Colombier 	MacAddrDw0 = 0x1008,
256*43f728cbSDavid du Colombier 	MacAddrDw1 = 0x100c,
257*43f728cbSDavid du Colombier 	MacBssidDw0 = 0x1010,
258*43f728cbSDavid du Colombier 	MacBssidDw1 = 0x1014,
259*43f728cbSDavid du Colombier 		MultiBcnNumShift = 18,
260*43f728cbSDavid du Colombier 		MultiBssidModeShift = 16,
261*43f728cbSDavid du Colombier 	MaxLenCfg = 0x1018,
262*43f728cbSDavid du Colombier 		MinMpduLenShift = 16,
263*43f728cbSDavid du Colombier 		MaxPsduLenShift = 12,
264*43f728cbSDavid du Colombier 		MaxPsduLen8k = 0,
265*43f728cbSDavid du Colombier 		MaxPsduLen16k = 1,
266*43f728cbSDavid du Colombier 		MaxPsduLen32k = 2,
267*43f728cbSDavid du Colombier 		MaxPsduLen64k = 3,
268*43f728cbSDavid du Colombier 		MaxMpduLenShift = 0,
269*43f728cbSDavid du Colombier 	BbpCsrCfg = 0x101c,
270*43f728cbSDavid du Colombier 		BbpRwParallel = (1 << 19),
271*43f728cbSDavid du Colombier 		BbpParDur1125 = (1 << 18),
272*43f728cbSDavid du Colombier 		BbpCsrKick = (1 << 17),
273*43f728cbSDavid du Colombier 		BbpCsrRead = (1 << 16),
274*43f728cbSDavid du Colombier 		BbpAddrShift = 8,
275*43f728cbSDavid du Colombier 		BbpDataShift = 0,
276*43f728cbSDavid du Colombier 	RfCsrCfg0 = 0x1020,
277*43f728cbSDavid du Colombier 		RfRegCtrl = (1 << 31),
278*43f728cbSDavid du Colombier 		RfLeSel1 = (1 << 30),
279*43f728cbSDavid du Colombier 		RfLeStby = (1 << 29),
280*43f728cbSDavid du Colombier 		RfRegWidthShift = 24,
281*43f728cbSDavid du Colombier 		RfReg0Shift = 0,
282*43f728cbSDavid du Colombier 	RfCsrCfg1 = 0x1024,
283*43f728cbSDavid du Colombier 		RfDur5 = (1 << 24),
284*43f728cbSDavid du Colombier 		RfReg1Shift = 0,
285*43f728cbSDavid du Colombier 	RfCsrCfg2 = 0x1028,
286*43f728cbSDavid du Colombier 	LedCfg = 0x102c,
287*43f728cbSDavid du Colombier 		LedPol = (1 << 30),
288*43f728cbSDavid du Colombier 		YLedModeShift = 28,
289*43f728cbSDavid du Colombier 		GLedModeShift = 26,
290*43f728cbSDavid du Colombier 		RLedModeShift = 24,
291*43f728cbSDavid du Colombier 		LedModeOff = 0,
292*43f728cbSDavid du Colombier 		LedModeBlinkTx = 1,
293*43f728cbSDavid du Colombier 		LedModeSlowBlink = 2,
294*43f728cbSDavid du Colombier 		LedModeOn = 3,
295*43f728cbSDavid du Colombier 		SlowBlkTimeShift = 16,
296*43f728cbSDavid du Colombier 		LedOffTimeShift = 8,
297*43f728cbSDavid du Colombier 		LedOnTimeShift = 0,
298*43f728cbSDavid du Colombier };
299*43f728cbSDavid du Colombier 
300*43f728cbSDavid du Colombier enum {
301*43f728cbSDavid du Colombier 	/* undocumented registers */
302*43f728cbSDavid du Colombier 	Debug = 0x10f4,
303*43f728cbSDavid du Colombier };
304*43f728cbSDavid du Colombier 
305*43f728cbSDavid du Colombier enum {
306*43f728cbSDavid du Colombier 	/* MAC Timing control registers */
307*43f728cbSDavid du Colombier 	XifsTimeCfg = 0x1100,
308*43f728cbSDavid du Colombier 		BbRxendEn = (1 << 29),
309*43f728cbSDavid du Colombier 		EifsTimeShift = 20,
310*43f728cbSDavid du Colombier 		OfdmXifsTimeShift = 16,
311*43f728cbSDavid du Colombier 		OfdmSifsTimeShift = 8,
312*43f728cbSDavid du Colombier 		CckSifsTimeShift = 0,
313*43f728cbSDavid du Colombier 	BkoffSlotCfg = 0x1104,
314*43f728cbSDavid du Colombier 		CcDelayTimeShift = 8,
315*43f728cbSDavid du Colombier 		SlotTime = 0,
316*43f728cbSDavid du Colombier 	NavTimeCfg = 0x1108,
317*43f728cbSDavid du Colombier 		NavUpd = (1 << 31),
318*43f728cbSDavid du Colombier 		NavUpdValShift = 16,
319*43f728cbSDavid du Colombier 		NavClrEn = (1 << 15),
320*43f728cbSDavid du Colombier 		NavTimerShift = 0,
321*43f728cbSDavid du Colombier 	ChTimeCfg = 0x110c,
322*43f728cbSDavid du Colombier 		EifsAsChBusy = (1 << 4),
323*43f728cbSDavid du Colombier 		NavAsChBusy = (1 << 3),
324*43f728cbSDavid du Colombier 		RxAsChBusy = (1 << 2),
325*43f728cbSDavid du Colombier 		TxAsChBusy = (1 << 1),
326*43f728cbSDavid du Colombier 		ChStaTimerEn = (1 << 0),
327*43f728cbSDavid du Colombier 	PbfLifeTimer = 0x1110,
328*43f728cbSDavid du Colombier 	BcnTimeCfg = 0x1114,
329*43f728cbSDavid du Colombier 		TsfInsCompShift = 24,
330*43f728cbSDavid du Colombier 		BcnTxEn = (1 << 20),
331*43f728cbSDavid du Colombier 		TbttTimerEn = (1 << 19),
332*43f728cbSDavid du Colombier 		TsfSyncModeShift = 17,
333*43f728cbSDavid du Colombier 		TsfSyncModeDis = 0,
334*43f728cbSDavid du Colombier 		TsfSyncModeSta = 1,
335*43f728cbSDavid du Colombier 		TsfSyncModeIbss = 2,
336*43f728cbSDavid du Colombier 		TsfSyncModeHostap = 3,
337*43f728cbSDavid du Colombier 		TsfTimerEn = (1 << 16),
338*43f728cbSDavid du Colombier 		BcnIntvalShift = 0,
339*43f728cbSDavid du Colombier 	TbttSyncCfg = 0x1118,
340*43f728cbSDavid du Colombier 		BcnCwminShift = 20,
341*43f728cbSDavid du Colombier 		BcnAifsnShift = 16,
342*43f728cbSDavid du Colombier 		BcnExpWinShift = 8,
343*43f728cbSDavid du Colombier 		TbttAdjustShift = 0,
344*43f728cbSDavid du Colombier 	TsfTimerDw0 = 0x111c,
345*43f728cbSDavid du Colombier 	TsfTimerDw1 = 0x1120,
346*43f728cbSDavid du Colombier 	TbttTimer = 0x1124,
347*43f728cbSDavid du Colombier 	IntTimerCfg = 0x1128,
348*43f728cbSDavid du Colombier 		GpTimerShift = 16,
349*43f728cbSDavid du Colombier 		PreTbttTimerShift = 0,
350*43f728cbSDavid du Colombier 	IntTimerEn = 0x112c,
351*43f728cbSDavid du Colombier 		GpTimerEn = (1 << 1),
352*43f728cbSDavid du Colombier 		PreTbttIntEn = (1 << 0),
353*43f728cbSDavid du Colombier 	ChIdleTime = 0x1130,
354*43f728cbSDavid du Colombier };
355*43f728cbSDavid du Colombier 
356*43f728cbSDavid du Colombier enum {
357*43f728cbSDavid du Colombier 	/* MAC Power Save configuration registers */
358*43f728cbSDavid du Colombier 	MacStatusReg = 0x1200,
359*43f728cbSDavid du Colombier 		RxStatusBusy = (1 << 1),
360*43f728cbSDavid du Colombier 		TxStatusBusy = (1 << 0),
361*43f728cbSDavid du Colombier 	PwrPinCfg = 0x1204,
362*43f728cbSDavid du Colombier 		IoAddaPd = (1 << 3),
363*43f728cbSDavid du Colombier 		IoPllPd = (1 << 2),
364*43f728cbSDavid du Colombier 		IoRaPe = (1 << 1),
365*43f728cbSDavid du Colombier 		IoRfPe = (1 << 0),
366*43f728cbSDavid du Colombier 	AutoWakeupCfg = 0x1208,
367*43f728cbSDavid du Colombier 		AutoWakeupEn = (1 << 15),
368*43f728cbSDavid du Colombier 		SleepTbttNumShift = 8,
369*43f728cbSDavid du Colombier 		WakeupLeadTimeShift = 0,
370*43f728cbSDavid du Colombier };
371*43f728cbSDavid du Colombier 
372*43f728cbSDavid du Colombier enum {
373*43f728cbSDavid du Colombier 	/* MAC TX configuration registers */
374*43f728cbSDavid du Colombier #define EdcaAcCfg(aci) (0x1300 + (aci) * 4)
375*43f728cbSDavid du Colombier 	EdcaTidAcMap = 0x1310,
376*43f728cbSDavid du Colombier #define TxPwrCfg(ridx) (0x1314 + (ridx) * 4)
377*43f728cbSDavid du Colombier 	TxPinCfg = 0x1328,
378*43f728cbSDavid du Colombier 		Rt3593LnaPeG2Pol = (1 << 31),
379*43f728cbSDavid du Colombier 		Rt3593LnaPeA2Pol = (1 << 30),
380*43f728cbSDavid du Colombier 		Rt3593LnaPeG2En = (1 << 29),
381*43f728cbSDavid du Colombier 		Rt3593LnaPeA2En = (1 << 28),
382*43f728cbSDavid du Colombier 		Rt3593LnaPe2En = (Rt3593LnaPeA2En | Rt3593LnaPeG2En),
383*43f728cbSDavid du Colombier 		Rt3593PaPeG2Pol = (1 << 27),
384*43f728cbSDavid du Colombier 		Rt3593PaPeA2Pol = (1 << 26),
385*43f728cbSDavid du Colombier 		Rt3593PaPeG2En = (1 << 25),
386*43f728cbSDavid du Colombier 		Rt3593PaPeA2En = (1 << 24),
387*43f728cbSDavid du Colombier 		TrswPol = (1 << 19),
388*43f728cbSDavid du Colombier 		TrswEn = (1 << 18),
389*43f728cbSDavid du Colombier 		RftrPol = (1 << 17),
390*43f728cbSDavid du Colombier 		RftrEn = (1 << 16),
391*43f728cbSDavid du Colombier 		LnaPeG1Pol = (1 << 15),
392*43f728cbSDavid du Colombier 		LnaPeA1Pol = (1 << 14),
393*43f728cbSDavid du Colombier 		LnaPeG0Pol = (1 << 13),
394*43f728cbSDavid du Colombier 		LnaPeA0Pol = (1 << 12),
395*43f728cbSDavid du Colombier 		LnaPeG1En = (1 << 11),
396*43f728cbSDavid du Colombier 		LnaPeA1En = (1 << 10),
397*43f728cbSDavid du Colombier 		LnaPe1En = (LnaPeA1En | LnaPeG1En),
398*43f728cbSDavid du Colombier 		LnaPeG0En = (1 << 9),
399*43f728cbSDavid du Colombier 		LnaPeA0En = (1 << 8),
400*43f728cbSDavid du Colombier 		LnaPe0En = (LnaPeA0En | LnaPeG0En),
401*43f728cbSDavid du Colombier 		PaPeG1Pol = (1 << 7),
402*43f728cbSDavid du Colombier 		PaPeA1Pol = (1 << 6),
403*43f728cbSDavid du Colombier 		PaPeG0Pol = (1 << 5),
404*43f728cbSDavid du Colombier 		PaPeA0Pol = (1 << 4),
405*43f728cbSDavid du Colombier 		PaPeG1En = (1 << 3),
406*43f728cbSDavid du Colombier 		PaPeA1En = (1 << 2),
407*43f728cbSDavid du Colombier 		PaPeG0En = (1 << 1),
408*43f728cbSDavid du Colombier 		PaPeA0En = (1 << 0),
409*43f728cbSDavid du Colombier 	TxBandCfg = 0x132c,
410*43f728cbSDavid du Colombier 		Tx5gBandSelN = (1 << 2),
411*43f728cbSDavid du Colombier 		Tx5gBandSelP = (1 << 1),
412*43f728cbSDavid du Colombier 		TxBandSel = (1 << 0),
413*43f728cbSDavid du Colombier 	TxSwCfg0 = 0x1330,
414*43f728cbSDavid du Colombier 		DlyRftrEnShift = 24,
415*43f728cbSDavid du Colombier 		DlyTrswEnShift = 16,
416*43f728cbSDavid du Colombier 		DlyPapeEnShift = 8,
417*43f728cbSDavid du Colombier 		DlyTxpeEnShift = 0,
418*43f728cbSDavid du Colombier 	TxSwCfg1 = 0x1334,
419*43f728cbSDavid du Colombier 		DlyRftrDisShift = 16,
420*43f728cbSDavid du Colombier 		DlyTrswDisShift = 8,
421*43f728cbSDavid du Colombier 		DlyPapeDisShift = 0,
422*43f728cbSDavid du Colombier 	TxSwCfg2 = 0x1338,
423*43f728cbSDavid du Colombier 		DlyLnaEnShift = 24,
424*43f728cbSDavid du Colombier 		DlyLnaDisShift = 16,
425*43f728cbSDavid du Colombier 		DlyDacEnShift = 8,
426*43f728cbSDavid du Colombier 		DlyDacDisShift = 0,
427*43f728cbSDavid du Colombier 	TxopThresCfg = 0x133c,
428*43f728cbSDavid du Colombier 		TxopRemThresShift = 24,
429*43f728cbSDavid du Colombier 		CfEndThresShift = 16,
430*43f728cbSDavid du Colombier 		RdgInThres = 8,
431*43f728cbSDavid du Colombier 		RdgOutThres = 0,
432*43f728cbSDavid du Colombier 	TxopCtrlCfg = 0x1340,
433*43f728cbSDavid du Colombier 		ExtCwMinShift = 16,
434*43f728cbSDavid du Colombier 		ExtCcaDlyShift = 8,
435*43f728cbSDavid du Colombier 		ExtCcaEn = (1 << 7),
436*43f728cbSDavid du Colombier 		LsigTxopEn = (1 << 6),
437*43f728cbSDavid du Colombier 		TxopTrunEnMimops = (1 << 4),
438*43f728cbSDavid du Colombier 		TxopTrunEnTxop = (1 << 3),
439*43f728cbSDavid du Colombier 		TxopTrunEnRate = (1 << 2),
440*43f728cbSDavid du Colombier 		TxopTrunEnAc = (1 << 1),
441*43f728cbSDavid du Colombier 		TxopTrunEnTimeout = (1 << 0),
442*43f728cbSDavid du Colombier 	TxRtsCfg = 0x1344,
443*43f728cbSDavid du Colombier 		RtsFbkEn = (1 << 24),
444*43f728cbSDavid du Colombier 		RtsThresShift = 8,
445*43f728cbSDavid du Colombier 		RtsRtyLimitShift = 0,
446*43f728cbSDavid du Colombier 	TxTimeoutCfg = 0x1348,
447*43f728cbSDavid du Colombier 		TxopTimeoutShift = 16,
448*43f728cbSDavid du Colombier 		RxAckTimeoutShift = 8,
449*43f728cbSDavid du Colombier 		MpduLifeTimeShift = 4,
450*43f728cbSDavid du Colombier 	TxRtyCfg = 0x134c,
451*43f728cbSDavid du Colombier 		TxAutofbEn = (1 << 30),
452*43f728cbSDavid du Colombier 		AggRtyModeTimer = (1 << 29),
453*43f728cbSDavid du Colombier 		NagRtyModeTimer = (1 << 28),
454*43f728cbSDavid du Colombier 		LongRtyThresShift = 16,
455*43f728cbSDavid du Colombier 		LongRtyLimitShift = 8,
456*43f728cbSDavid du Colombier 		ShortRtyLimitShift = 0,
457*43f728cbSDavid du Colombier 	TxLinkCfg = 0x1350,
458*43f728cbSDavid du Colombier 		RemoteMfsShift = 24,
459*43f728cbSDavid du Colombier 		RemoteMfbShift = 16,
460*43f728cbSDavid du Colombier 		TxCfackEn = (1 << 12),
461*43f728cbSDavid du Colombier 		TxRdgEn = (1 << 11),
462*43f728cbSDavid du Colombier 		TxMrqEn = (1 << 10),
463*43f728cbSDavid du Colombier 		RemoteUmfsEn = (1 << 9),
464*43f728cbSDavid du Colombier 		TxMfbEn = (1 << 8),
465*43f728cbSDavid du Colombier 		RemoteMfbLtShift = 0,
466*43f728cbSDavid du Colombier 	HtFbkCfg0 = 0x1354,
467*43f728cbSDavid du Colombier 	HtFbkCfg1 = 0x1358,
468*43f728cbSDavid du Colombier 	LgFbkCfg0 = 0x135c,
469*43f728cbSDavid du Colombier 	LgFbkCfg1 = 0x1360,
470*43f728cbSDavid du Colombier 	CckProtCfg = 0x1364,
471*43f728cbSDavid du Colombier 		/* possible flags for registers *ProtCfg */
472*43f728cbSDavid du Colombier 		RtsthEn = (1 << 26),
473*43f728cbSDavid du Colombier 		TxopAllowGf40 = (1 << 25),
474*43f728cbSDavid du Colombier 		TxopAllowGf20 = (1 << 24),
475*43f728cbSDavid du Colombier 		TxopAllowMm40 = (1 << 23),
476*43f728cbSDavid du Colombier 		TxopAllowMm20 = (1 << 22),
477*43f728cbSDavid du Colombier 		TxopAllowOfdm = (1 << 21),
478*43f728cbSDavid du Colombier 		TxopAllowCck = (1 << 20),
479*43f728cbSDavid du Colombier 		TxopAllowAll = (0x3f << 20),
480*43f728cbSDavid du Colombier 		ProtNavShort = (1 << 18),
481*43f728cbSDavid du Colombier 		ProtNavLong = (2 << 18),
482*43f728cbSDavid du Colombier 		ProtCtrlRtsCts = (1 << 16),
483*43f728cbSDavid du Colombier 		ProtCtrlCts = (2 << 16),
484*43f728cbSDavid du Colombier 	OfdmProtCfg = 0x1368,
485*43f728cbSDavid du Colombier 	Mm20ProtCfg = 0x136c,
486*43f728cbSDavid du Colombier 	Mm40ProtCfg = 0x1370,
487*43f728cbSDavid du Colombier 	Gf20ProtCfg = 0x1374,
488*43f728cbSDavid du Colombier 	Gf40ProtCfg = 0x1378,
489*43f728cbSDavid du Colombier 	ExpCtsTime = 0x137c,
490*43f728cbSDavid du Colombier 		/* possible flags for registers EXP_{CTS,ACK}_TIME */
491*43f728cbSDavid du Colombier 		ExpOfdmTimeShift = 16,
492*43f728cbSDavid du Colombier 		ExpCckTimeShift = 0,
493*43f728cbSDavid du Colombier 	ExpAckTime = 0x1380,
494*43f728cbSDavid du Colombier };
495*43f728cbSDavid du Colombier 
496*43f728cbSDavid du Colombier enum {
497*43f728cbSDavid du Colombier 	/* MAC RX configuration registers */
498*43f728cbSDavid du Colombier 	RxFiltrCfg = 0x1400,
499*43f728cbSDavid du Colombier 		DropCtrlRsv = (1 << 16),
500*43f728cbSDavid du Colombier 		DropBar = (1 << 15),
501*43f728cbSDavid du Colombier 		DropBa = (1 << 14),
502*43f728cbSDavid du Colombier 		DropPspoll = (1 << 13),
503*43f728cbSDavid du Colombier 		DropRts = (1 << 12),
504*43f728cbSDavid du Colombier 		DropCts = (1 << 11),
505*43f728cbSDavid du Colombier 		DropAck = (1 << 10),
506*43f728cbSDavid du Colombier 		DropCfend = (1 << 9),
507*43f728cbSDavid du Colombier 		DropCfack = (1 << 8),
508*43f728cbSDavid du Colombier 		DropDupl = (1 << 7),
509*43f728cbSDavid du Colombier 		DropBc = (1 << 6),
510*43f728cbSDavid du Colombier 		DropMc = (1 << 5),
511*43f728cbSDavid du Colombier 		DropVerErr = (1 << 4),
512*43f728cbSDavid du Colombier 		DropNotMybss = (1 << 3),
513*43f728cbSDavid du Colombier 		DropUcNome = (1 << 2),
514*43f728cbSDavid du Colombier 		DropPhyErr = (1 << 1),
515*43f728cbSDavid du Colombier 		DropCrcErr = (1 << 0),
516*43f728cbSDavid du Colombier 	AutoRspCfg = 0x1404,
517*43f728cbSDavid du Colombier 		CtrlPwrBit = (1 << 7),
518*43f728cbSDavid du Colombier 		BacAckPolicy = (1 << 6),
519*43f728cbSDavid du Colombier 		CckShortEn = (1 << 4),
520*43f728cbSDavid du Colombier 		Cts40mRefEn = (1 << 3),
521*43f728cbSDavid du Colombier 		Cts40mModeEn = (1 << 2),
522*43f728cbSDavid du Colombier 		BacAckpolicyEn = (1 << 1),
523*43f728cbSDavid du Colombier 		AutoRspEn = (1 << 0),
524*43f728cbSDavid du Colombier 	LegacyBasicRate = 0x1408,
525*43f728cbSDavid du Colombier 	HtBasicRate = 0x140c,
526*43f728cbSDavid du Colombier 	HtCtrlCfg = 0x1410,
527*43f728cbSDavid du Colombier 	SifsCostCfg = 0x1414,
528*43f728cbSDavid du Colombier 		OfdmSifsCostShift = 8,
529*43f728cbSDavid du Colombier 		CckSifsCostShift = 0,
530*43f728cbSDavid du Colombier 	RxParserCfg = 0x1418,
531*43f728cbSDavid du Colombier };
532*43f728cbSDavid du Colombier 
533*43f728cbSDavid du Colombier enum {
534*43f728cbSDavid du Colombier 	/* MAC Security configuration registers */
535*43f728cbSDavid du Colombier 	TxSecCnt0 = 0x1500,
536*43f728cbSDavid du Colombier 	RxSecCnt0 = 0x1504,
537*43f728cbSDavid du Colombier 	CcmpFcMute = 0x1508,
538*43f728cbSDavid du Colombier };
539*43f728cbSDavid du Colombier 
540*43f728cbSDavid du Colombier enum {
541*43f728cbSDavid du Colombier 	/* MAC HCCA/PSMP configuration registers */
542*43f728cbSDavid du Colombier 	TxopHldrAddr0 = 0x1600,
543*43f728cbSDavid du Colombier 	TxopHldrAddr1 = 0x1604,
544*43f728cbSDavid du Colombier 	TxopHldrEt = 0x1608,
545*43f728cbSDavid du Colombier 		TxopEtm1En = (1 << 25),
546*43f728cbSDavid du Colombier 		TxopEtm0En = (1 << 24),
547*43f728cbSDavid du Colombier 		TxopEtmThresShift = 16,
548*43f728cbSDavid du Colombier 		TxopEtoEn = (1 << 8),
549*43f728cbSDavid du Colombier 		TxopEtoThresShift = 1,
550*43f728cbSDavid du Colombier 		PerRxRstEn = (1 << 0),
551*43f728cbSDavid du Colombier 	QosCfpollRaDw0 = 0x160c,
552*43f728cbSDavid du Colombier 	QosCfpollA1Dw1 = 0x1610,
553*43f728cbSDavid du Colombier 	QosCfpollQc = 0x1614,
554*43f728cbSDavid du Colombier };
555*43f728cbSDavid du Colombier 
556*43f728cbSDavid du Colombier enum {
557*43f728cbSDavid du Colombier 	/* MAC Statistics Counters */
558*43f728cbSDavid du Colombier 	RxStaCnt0 = 0x1700,
559*43f728cbSDavid du Colombier 	RxStaCnt1 = 0x1704,
560*43f728cbSDavid du Colombier 	RxStaCnt2 = 0x1708,
561*43f728cbSDavid du Colombier 	TxStaCnt0 = 0x170c,
562*43f728cbSDavid du Colombier 	TxStaCnt1 = 0x1710,
563*43f728cbSDavid du Colombier 	TxStaCnt2 = 0x1714,
564*43f728cbSDavid du Colombier 	TxStatFifo = 0x1718,
565*43f728cbSDavid du Colombier 		TxqMcsShift = 16,
566*43f728cbSDavid du Colombier 		TxqWcidShift = 8,
567*43f728cbSDavid du Colombier 		TxqAckreq = (1 << 7),
568*43f728cbSDavid du Colombier 		TxqAgg = (1 << 6),
569*43f728cbSDavid du Colombier 		TxqOk = (1 << 5),
570*43f728cbSDavid du Colombier 		TxqPidShift = 1,
571*43f728cbSDavid du Colombier 		TxqVld = (1 << 0),
572*43f728cbSDavid du Colombier };
573*43f728cbSDavid du Colombier 
574*43f728cbSDavid du Colombier /* RX WCID search table */
575*43f728cbSDavid du Colombier #define WcidEntry(wcid) (0x1800 + (wcid) * 8)
576*43f728cbSDavid du Colombier 
577*43f728cbSDavid du Colombier enum {
578*43f728cbSDavid du Colombier 	FwBase = 0x2000,
579*43f728cbSDavid du Colombier 	Rt2870FwBase = 0x3000,
580*43f728cbSDavid du Colombier };
581*43f728cbSDavid du Colombier 
582*43f728cbSDavid du Colombier /* Pair-wise key table */
583*43f728cbSDavid du Colombier #define Pkey(wcid) (0x4000 + (wcid) * 32)
584*43f728cbSDavid du Colombier 
585*43f728cbSDavid du Colombier /* IV/EIV table */
586*43f728cbSDavid du Colombier #define Iveiv(wcid) (0x6000 + (wcid) * 8)
587*43f728cbSDavid du Colombier 
588*43f728cbSDavid du Colombier /* WCID attribute table */
589*43f728cbSDavid du Colombier #define WcidAttr(wcid) (0x6800 + (wcid) * 4)
590*43f728cbSDavid du Colombier 
591*43f728cbSDavid du Colombier /* possible flags for register WCID_ATTR */
592*43f728cbSDavid du Colombier enum {
593*43f728cbSDavid du Colombier 	ModeNosec = 0,
594*43f728cbSDavid du Colombier 	ModeWep40 = 1,
595*43f728cbSDavid du Colombier 	ModeWep104 = 2,
596*43f728cbSDavid du Colombier 	ModeTkip = 3,
597*43f728cbSDavid du Colombier 	ModeAesCcmp = 4,
598*43f728cbSDavid du Colombier 	ModeCkip40 = 5,
599*43f728cbSDavid du Colombier 	ModeCkip104 = 6,
600*43f728cbSDavid du Colombier 	ModeCkip128 = 7,
601*43f728cbSDavid du Colombier 	RxPkeyEn = (1 << 0),
602*43f728cbSDavid du Colombier };
603*43f728cbSDavid du Colombier 
604*43f728cbSDavid du Colombier /* Shared Key Table */
605*43f728cbSDavid du Colombier #define Skey(vap, kidx) (0x6c00 + (vap) * 128 + (kidx) * 32)
606*43f728cbSDavid du Colombier 
607*43f728cbSDavid du Colombier /* Shared Key Mode */
608*43f728cbSDavid du Colombier enum {
609*43f728cbSDavid du Colombier 	SkeyMode07 = 0x7000,
610*43f728cbSDavid du Colombier 	SkeyMode815 = 0x7004,
611*43f728cbSDavid du Colombier 	SkeyMode1623 = 0x7008,
612*43f728cbSDavid du Colombier 	SkeyMode2431 = 0x700c,
613*43f728cbSDavid du Colombier };
614*43f728cbSDavid du Colombier 
615*43f728cbSDavid du Colombier enum {
616*43f728cbSDavid du Colombier 	/* Shared Memory between MCU and host */
617*43f728cbSDavid du Colombier 	H2mMailbox = 0x7010,
618*43f728cbSDavid du Colombier 		H2mBusy = (1 << 24),
619*43f728cbSDavid du Colombier 		TokenNoIntr = 0xff,
620*43f728cbSDavid du Colombier 	H2mMailboxCid = 0x7014,
621*43f728cbSDavid du Colombier 	H2mMailboxStatus = 0x701c,
622*43f728cbSDavid du Colombier 	H2mBbpagent = 0x7028,
623*43f728cbSDavid du Colombier #define BcnBase(vap) (0x7800 + (vap) * 512)
624*43f728cbSDavid du Colombier };
625*43f728cbSDavid du Colombier 
626*43f728cbSDavid du Colombier /*
627*43f728cbSDavid du Colombier  *	RT2860 TX descriptor
628*43f728cbSDavid du Colombier  *	--------------------
629*43f728cbSDavid du Colombier  *	u32int	sdp0 		Segment Data Pointer 0
630*43f728cbSDavid du Colombier  *	u16int	sdl1 		Segment Data Length 1
631*43f728cbSDavid du Colombier  *	u16int	sdl0		Segment Data Length 0
632*43f728cbSDavid du Colombier  *	u32int	sdp1		Segment Data Pointer 1
633*43f728cbSDavid du Colombier  *	u8int 	reserved[3]
634*43f728cbSDavid du Colombier  *	u8int 	flags
635*43f728cbSDavid du Colombier  */
636*43f728cbSDavid du Colombier 
637*43f728cbSDavid du Colombier enum {
638*43f728cbSDavid du Colombier 	/* sdl1 flags */
639*43f728cbSDavid du Colombier 	TxBurst = (1 << 15),
640*43f728cbSDavid du Colombier 	TxLs1 = (1 << 14) /* SDP1 is the last segment */,
641*43f728cbSDavid du Colombier 	/* sdl0 flags */
642*43f728cbSDavid du Colombier 	TxDdone = (1 << 15),
643*43f728cbSDavid du Colombier 	TxLs0 = (1 << 14) /* SDP0 is the last segment */,
644*43f728cbSDavid du Colombier 	/* flags */
645*43f728cbSDavid du Colombier 	TxQselShift = 1,
646*43f728cbSDavid du Colombier 	TxQselMgmt = (0 << 1),
647*43f728cbSDavid du Colombier 	TxQselHcca = (1 << 1),
648*43f728cbSDavid du Colombier 	TxQselEdca = (2 << 1),
649*43f728cbSDavid du Colombier 	TxWiv = (1 << 0),
650*43f728cbSDavid du Colombier };
651*43f728cbSDavid du Colombier 
652*43f728cbSDavid du Colombier /*
653*43f728cbSDavid du Colombier  *	TX Wireless Information
654*43f728cbSDavid du Colombier  *	-----------------------
655*43f728cbSDavid du Colombier  *	u8int	flags
656*43f728cbSDavid du Colombier  *	u8int	txop
657*43f728cbSDavid du Colombier  *	u16int	phy
658*43f728cbSDavid du Colombier  *	u8int	xflags
659*43f728cbSDavid du Colombier  *	u8int	wcid 	Wireless Client ID
660*43f728cbSDavid du Colombier  *	u16int	len
661*43f728cbSDavid du Colombier  *	u32int	iv
662*43f728cbSDavid du Colombier  *	u32int	eiv
663*43f728cbSDavid du Colombier  */
664*43f728cbSDavid du Colombier 
665*43f728cbSDavid du Colombier enum {
666*43f728cbSDavid du Colombier 	/* flags */
667*43f728cbSDavid du Colombier 	TxMpduDsityShift = 5,
668*43f728cbSDavid du Colombier 	TxAmpdu = (1 << 4),
669*43f728cbSDavid du Colombier 	TxTs = (1 << 3),
670*43f728cbSDavid du Colombier 	TxCfack = (1 << 2),
671*43f728cbSDavid du Colombier 	TxMmps = (1 << 1),
672*43f728cbSDavid du Colombier 	TxFrag = (1 << 0),
673*43f728cbSDavid du Colombier 	/* txop */
674*43f728cbSDavid du Colombier 	TxTxopHt = 0,
675*43f728cbSDavid du Colombier 	TxTxopPifs = 1,
676*43f728cbSDavid du Colombier 	TxTxopSifs = 2,
677*43f728cbSDavid du Colombier 	TxTxopBackoff = 3,
678*43f728cbSDavid du Colombier 	/* phy */
679*43f728cbSDavid du Colombier 	PhyMode = 0xc000,
680*43f728cbSDavid du Colombier 	PhyCck = (0 << 14),
681*43f728cbSDavid du Colombier 	PhyOfdm = (1 << 14),
682*43f728cbSDavid du Colombier 	PhyHt = (2 << 14),
683*43f728cbSDavid du Colombier 	PhyHtGf = (3 << 14),
684*43f728cbSDavid du Colombier 	PhySgi = (1 << 8),
685*43f728cbSDavid du Colombier 	PhyBw40 = (1 << 7),
686*43f728cbSDavid du Colombier 	PhyMcs = 0x7f,
687*43f728cbSDavid du Colombier 	PhyShpre = (1 << 3),
688*43f728cbSDavid du Colombier 	/* xflags */
689*43f728cbSDavid du Colombier 	TxBawinsizeShift = 2,
690*43f728cbSDavid du Colombier 	TxNseq = (1 << 1),
691*43f728cbSDavid du Colombier 	TxAck = (1 << 0),
692*43f728cbSDavid du Colombier 	/* len */
693*43f728cbSDavid du Colombier 	TxPidShift = 12,
694*43f728cbSDavid du Colombier };
695*43f728cbSDavid du Colombier 
696*43f728cbSDavid du Colombier /*
697*43f728cbSDavid du Colombier  *	RT2860 RX descriptor
698*43f728cbSDavid du Colombier  *	--------------------
699*43f728cbSDavid du Colombier  *	u32int	sdp0
700*43f728cbSDavid du Colombier  *	u16int	sdl1 	unused
701*43f728cbSDavid du Colombier  *	u16int  sdl0
702*43f728cbSDavid du Colombier  *	u32int	sdp1	unused
703*43f728cbSDavid du Colombier  *	u32int	flags
704*43f728cbSDavid du Colombier  */
705*43f728cbSDavid du Colombier 
706*43f728cbSDavid du Colombier enum {
707*43f728cbSDavid du Colombier 	/* sdl flags */
708*43f728cbSDavid du Colombier 	RxDdone = (1 << 15),
709*43f728cbSDavid du Colombier 	RxLs0 = (1 << 14),
710*43f728cbSDavid du Colombier 	/* flags */
711*43f728cbSDavid du Colombier 	RxDec = (1 << 16),
712*43f728cbSDavid du Colombier 	RxAmpdu = (1 << 15),
713*43f728cbSDavid du Colombier 	RxL2pad = (1 << 14),
714*43f728cbSDavid du Colombier 	RxRssi = (1 << 13),
715*43f728cbSDavid du Colombier 	RxHtc = (1 << 12),
716*43f728cbSDavid du Colombier 	RxAmsdu = (1 << 11),
717*43f728cbSDavid du Colombier 	RxMicerr = (1 << 10),
718*43f728cbSDavid du Colombier 	RxIcverr = (1 << 9),
719*43f728cbSDavid du Colombier 	RxCrcerr = (1 << 8),
720*43f728cbSDavid du Colombier 	RxMybss = (1 << 7),
721*43f728cbSDavid du Colombier 	RxBc = (1 << 6),
722*43f728cbSDavid du Colombier 	RxMc = (1 << 5),
723*43f728cbSDavid du Colombier 	RxUc2me = (1 << 4),
724*43f728cbSDavid du Colombier 	RxFrag = (1 << 3),
725*43f728cbSDavid du Colombier 	RxNull = (1 << 2),
726*43f728cbSDavid du Colombier 	RxData = (1 << 1),
727*43f728cbSDavid du Colombier 	RxBa = (1 << 0),
728*43f728cbSDavid du Colombier };
729*43f728cbSDavid du Colombier 
730*43f728cbSDavid du Colombier /*
731*43f728cbSDavid du Colombier  *	RX Wireless Information
732*43f728cbSDavid du Colombier  *	-----------------------
733*43f728cbSDavid du Colombier  *	u8int	wcid
734*43f728cbSDavid du Colombier  *	u8int	keyidx
735*43f728cbSDavid du Colombier  *	u16int	len
736*43f728cbSDavid du Colombier  *	u16int	seq
737*43f728cbSDavid du Colombier  *	u16int	phy
738*43f728cbSDavid du Colombier  *	u8int	rssi[3]
739*43f728cbSDavid du Colombier  *	u8int	reserved1
740*43f728cbSDavid du Colombier  *	u8int	snr[2]
741*43f728cbSDavid du Colombier  *	u16int	reserved2
742*43f728cbSDavid du Colombier  */
743*43f728cbSDavid du Colombier 
744*43f728cbSDavid du Colombier enum {
745*43f728cbSDavid du Colombier 	/* keyidx flags */
746*43f728cbSDavid du Colombier 	RxUdfShift = 5,
747*43f728cbSDavid du Colombier 	RxBssIdxShift = 2,
748*43f728cbSDavid du Colombier 	/* len flags */
749*43f728cbSDavid du Colombier 	RxTidShift = 12,
750*43f728cbSDavid du Colombier };
751*43f728cbSDavid du Colombier 
752*43f728cbSDavid du Colombier enum {
753*43f728cbSDavid du Colombier 	WIFIHDRSIZE = 2+2+3*6+2,
754*43f728cbSDavid du Colombier 	Rdscsize = 16,
755*43f728cbSDavid du Colombier 	Tdscsize = 16,
756*43f728cbSDavid du Colombier 	Rbufsize = 4096,
757*43f728cbSDavid du Colombier 	Tbufsize = 4096,
758*43f728cbSDavid du Colombier 	Rxwisize = 16,
759*43f728cbSDavid du Colombier 	Txwisize = 16,
760*43f728cbSDavid du Colombier 	/* first DMA segment contains TXWI + 802.11 header + 32-bit padding */
761*43f728cbSDavid du Colombier 	TxwiDmaSz = Txwisize + WIFIHDRSIZE + 2
762*43f728cbSDavid du Colombier };
763*43f728cbSDavid du Colombier 
764*43f728cbSDavid du Colombier /* RF registers */
765*43f728cbSDavid du Colombier enum {
766*43f728cbSDavid du Colombier 	Rf1 = 0,
767*43f728cbSDavid du Colombier 	Rf2 = 2,
768*43f728cbSDavid du Colombier 	Rf3 = 1,
769*43f728cbSDavid du Colombier 	Rf4 = 3,
770*43f728cbSDavid du Colombier };
771*43f728cbSDavid du Colombier 
772*43f728cbSDavid du Colombier enum {
773*43f728cbSDavid du Colombier 	Rf2820 = 1 /* 2T3R */,
774*43f728cbSDavid du Colombier 	Rf2850 = 2 /* dual-band 2T3R */,
775*43f728cbSDavid du Colombier 	Rf2720 = 3 /* 1T2R */,
776*43f728cbSDavid du Colombier 	Rf2750 = 4 /* dual-band 1T2R */,
777*43f728cbSDavid du Colombier 	Rf3020 = 5 /* 1T1R */,
778*43f728cbSDavid du Colombier 	Rf2020 = 6 /* b/g */,
779*43f728cbSDavid du Colombier 	Rf3021 = 7 /* 1T2R */,
780*43f728cbSDavid du Colombier 	Rf3022 = 8 /* 2T2R */,
781*43f728cbSDavid du Colombier  	Rf3052 = 9 /* dual-band 2T2R */,
782*43f728cbSDavid du Colombier 	Rf3320 = 11 /* 1T1R */,
783*43f728cbSDavid du Colombier 	Rf3053 = 13 /* dual-band 3T3R */,
784*43f728cbSDavid du Colombier };
785*43f728cbSDavid du Colombier 
786*43f728cbSDavid du Colombier enum {
787*43f728cbSDavid du Colombier 	Rt3070RfBlock = (1 << 0),
788*43f728cbSDavid du Colombier 	Rt3070Rx0Pd = (1 << 2),
789*43f728cbSDavid du Colombier 	Rt3070Tx0Pd = (1 << 3),
790*43f728cbSDavid du Colombier 	Rt3070Rx1Pd = (1 << 4),
791*43f728cbSDavid du Colombier 	Rt3070Tx1Pd = (1 << 5),
792*43f728cbSDavid du Colombier 	Rt3070Rx2Pd = (1 << 6),
793*43f728cbSDavid du Colombier 	Rt3070Tx2Pd = (1 << 7),
794*43f728cbSDavid du Colombier 	Rt3070Tune = (1 << 0),
795*43f728cbSDavid du Colombier 	Rt3070TxLo2 = (1 << 3),
796*43f728cbSDavid du Colombier 	Rt3070TxLo1 = (1 << 3),
797*43f728cbSDavid du Colombier 	Rt3070RxLo1 = (1 << 3),
798*43f728cbSDavid du Colombier 	Rt3070RxLo2 = (1 << 3),
799*43f728cbSDavid du Colombier 	Rt3070RxCtb = (1 << 7),
800*43f728cbSDavid du Colombier 	Rt3070BbLoopback = (1 << 0),
801*43f728cbSDavid du Colombier 
802*43f728cbSDavid du Colombier 	Rt3593Vco = (1 << 0),
803*43f728cbSDavid du Colombier 	Rt3593Rescal = (1 << 7),
804*43f728cbSDavid du Colombier 	Rt3593Vcocal = (1 << 7),
805*43f728cbSDavid du Colombier 	Rt3593VcoIc = (1 << 6),
806*43f728cbSDavid du Colombier 	Rt3593LdoPllVcMask = 0x0e,
807*43f728cbSDavid du Colombier 	Rt3593LdoRfVcMask = 0xe0,
808*43f728cbSDavid du Colombier 	Rt3593CpIcMask = 0xe0,
809*43f728cbSDavid du Colombier 	Rt3593CpIcShift = 5,
810*43f728cbSDavid du Colombier 	Rt3593RxCtb = (1 << 5)
811*43f728cbSDavid du Colombier };
812*43f728cbSDavid du Colombier 
813*43f728cbSDavid du Colombier static const char* rfnames[] = {
814*43f728cbSDavid du Colombier 	[Rf2820] "RT2820",
815*43f728cbSDavid du Colombier 	[Rf2850] "RT2850",
816*43f728cbSDavid du Colombier 	[Rf2720] "RT2720",
817*43f728cbSDavid du Colombier 	[Rf2750] "RT2750",
818*43f728cbSDavid du Colombier 	[Rf3020] "RT3020",
819*43f728cbSDavid du Colombier 	[Rf2020] "RT2020",
820*43f728cbSDavid du Colombier 	[Rf3021] "RT3021",
821*43f728cbSDavid du Colombier 	[Rf3022] "RT3022",
822*43f728cbSDavid du Colombier 	[Rf3052] "RT3052",
823*43f728cbSDavid du Colombier 	[Rf3320] "RT3320",
824*43f728cbSDavid du Colombier 	[Rf3053] "RT3053",
825*43f728cbSDavid du Colombier };
826*43f728cbSDavid du Colombier 
827*43f728cbSDavid du Colombier enum {
828*43f728cbSDavid du Colombier 	/* USB commands, RT2870 only */
829*43f728cbSDavid du Colombier 	Rt2870Reset = 1,
830*43f728cbSDavid du Colombier 	Rt2870Write2 = 2,
831*43f728cbSDavid du Colombier 	Rt2870WriteRegion1 = 6,
832*43f728cbSDavid du Colombier 	Rt2870ReadRegion1 = 7,
833*43f728cbSDavid du Colombier 	Rt2870EepromRead = 9,
834*43f728cbSDavid du Colombier };
835*43f728cbSDavid du Colombier 
836*43f728cbSDavid du Colombier enum {
837*43f728cbSDavid du Colombier 	EepromDelay = 1 /* minimum hold time (microsecond) */,
838*43f728cbSDavid du Colombier 
839*43f728cbSDavid du Colombier 	EepromVersion = 0x01,
840*43f728cbSDavid du Colombier 	EepromMac01 = 0x02,
841*43f728cbSDavid du Colombier 	EepromMac23 = 0x03,
842*43f728cbSDavid du Colombier 	EepromMac45 = 0x04,
843*43f728cbSDavid du Colombier 	EepromPciePslevel = 0x11,
844*43f728cbSDavid du Colombier 	EepromRev = 0x12,
845*43f728cbSDavid du Colombier 	EepromAntenna = 0x1a,
846*43f728cbSDavid du Colombier 	EepromConfig = 0x1b,
847*43f728cbSDavid du Colombier 	EepromCountry = 0x1c,
848*43f728cbSDavid du Colombier 	EepromFreqLeds = 0x1d,
849*43f728cbSDavid du Colombier 	EepromLed1 = 0x1e,
850*43f728cbSDavid du Colombier 	EepromLed2 = 0x1f,
851*43f728cbSDavid du Colombier 	EepromLed3 = 0x20,
852*43f728cbSDavid du Colombier 	EepromLna = 0x22,
853*43f728cbSDavid du Colombier 	EepromRssi12ghz = 0x23,
854*43f728cbSDavid du Colombier 	EepromRssi22ghz = 0x24,
855*43f728cbSDavid du Colombier 	EepromRssi15ghz = 0x25,
856*43f728cbSDavid du Colombier 	EepromRssi25ghz = 0x26,
857*43f728cbSDavid du Colombier 	EepromDeltapwr = 0x28,
858*43f728cbSDavid du Colombier 	EepromPwr2ghzBase1 = 0x29,
859*43f728cbSDavid du Colombier 	EepromPwr2ghzBase2 = 0x30,
860*43f728cbSDavid du Colombier 	EepromTssi12ghz = 0x37,
861*43f728cbSDavid du Colombier 	EepromTssi22ghz = 0x38,
862*43f728cbSDavid du Colombier 	EepromTssi32ghz = 0x39,
863*43f728cbSDavid du Colombier 	EepromTssi42ghz = 0x3a,
864*43f728cbSDavid du Colombier 	EepromTssi52ghz = 0x3b,
865*43f728cbSDavid du Colombier 	EepromPwr5ghzBase1 = 0x3c,
866*43f728cbSDavid du Colombier 	EepromPwr5ghzBase2 = 0x53,
867*43f728cbSDavid du Colombier 	EepromTssi15ghz = 0x6a,
868*43f728cbSDavid du Colombier 	EepromTssi25ghz = 0x6b,
869*43f728cbSDavid du Colombier 	EepromTssi35ghz = 0x6c,
870*43f728cbSDavid du Colombier 	EepromTssi45ghz = 0x6d,
871*43f728cbSDavid du Colombier 	EepromTssi55ghz = 0x6e,
872*43f728cbSDavid du Colombier 	EepromRpwr = 0x6f,
873*43f728cbSDavid du Colombier 	EepromBbpBase = 0x78,
874*43f728cbSDavid du Colombier 	Rt3071EepromRfBase = 0x82,
875*43f728cbSDavid du Colombier };
876*43f728cbSDavid du Colombier 
877*43f728cbSDavid du Colombier enum {
878*43f728cbSDavid du Colombier 	RidxCck1 = 0,
879*43f728cbSDavid du Colombier 	RidxCck11 = 3,
880*43f728cbSDavid du Colombier 	RidxOfdm6 = 4,
881*43f728cbSDavid du Colombier 	RidxMax = 11,
882*43f728cbSDavid du Colombier };
883*43f728cbSDavid du Colombier 
884*43f728cbSDavid du Colombier /* ring and pool count */
885*43f728cbSDavid du Colombier enum {
886*43f728cbSDavid du Colombier 	Nrx = 128,
887*43f728cbSDavid du Colombier 	Ntx = 64,
888*43f728cbSDavid du Colombier 	Ntxpool = Ntx * 2
889*43f728cbSDavid du Colombier };
890*43f728cbSDavid du Colombier 
891*43f728cbSDavid du Colombier typedef struct FWImage FWImage;
892*43f728cbSDavid du Colombier typedef struct TXQ TXQ;
893*43f728cbSDavid du Colombier typedef struct RXQ RXQ;
894*43f728cbSDavid du Colombier typedef struct Pool Pool;
895*43f728cbSDavid du Colombier 
896*43f728cbSDavid du Colombier typedef struct Ctlr Ctlr;
897*43f728cbSDavid du Colombier 
898*43f728cbSDavid du Colombier struct FWImage {
899*43f728cbSDavid du Colombier 	uint  size;
900*43f728cbSDavid du Colombier 	uchar *data;
901*43f728cbSDavid du Colombier };
902*43f728cbSDavid du Colombier 
903*43f728cbSDavid du Colombier struct TXQ
904*43f728cbSDavid du Colombier {
905*43f728cbSDavid du Colombier 	uint	n; /* next */
906*43f728cbSDavid du Colombier 	uint	i; /* current */
907*43f728cbSDavid du Colombier 	Block	**b;
908*43f728cbSDavid du Colombier 	u32int	*d; /* descriptors */
909*43f728cbSDavid du Colombier 
910*43f728cbSDavid du Colombier 	Rendez;
911*43f728cbSDavid du Colombier 	QLock;
912*43f728cbSDavid du Colombier };
913*43f728cbSDavid du Colombier 
914*43f728cbSDavid du Colombier struct RXQ
915*43f728cbSDavid du Colombier {
916*43f728cbSDavid du Colombier 	uint	i;
917*43f728cbSDavid du Colombier 	Block	**b;
918*43f728cbSDavid du Colombier 	u32int	*p;
919*43f728cbSDavid du Colombier };
920*43f728cbSDavid du Colombier 
921*43f728cbSDavid du Colombier struct Pool
922*43f728cbSDavid du Colombier {
923*43f728cbSDavid du Colombier 	uint i; /* current */
924*43f728cbSDavid du Colombier 	uchar *p; /* txwi */
925*43f728cbSDavid du Colombier };
926*43f728cbSDavid du Colombier 
927*43f728cbSDavid du Colombier struct Ctlr {
928*43f728cbSDavid du Colombier 	Lock;
929*43f728cbSDavid du Colombier 	QLock;
930*43f728cbSDavid du Colombier 
931*43f728cbSDavid du Colombier 	Ctlr *link;
932*43f728cbSDavid du Colombier 	Pcidev *pdev;
933*43f728cbSDavid du Colombier 	Wifi *wifi;
934*43f728cbSDavid du Colombier 
935*43f728cbSDavid du Colombier 	u16int mac_ver;
936*43f728cbSDavid du Colombier 	u16int mac_rev;
937*43f728cbSDavid du Colombier 	u8int rf_rev;
938*43f728cbSDavid du Colombier 	u8int freq;
939*43f728cbSDavid du Colombier 	u8int ntxchains;
940*43f728cbSDavid du Colombier 	u8int nrxchains;
941*43f728cbSDavid du Colombier 	u8int pslevel;
942*43f728cbSDavid du Colombier 	s8int txpow1[54];
943*43f728cbSDavid du Colombier 	s8int txpow2[54];
944*43f728cbSDavid du Colombier 	s8int rssi_2ghz[3];
945*43f728cbSDavid du Colombier 	s8int rssi_5ghz[3];
946*43f728cbSDavid du Colombier 	u8int lna[4];
947*43f728cbSDavid du Colombier 	u8int rf24_20mhz;
948*43f728cbSDavid du Colombier 	u8int rf24_40mhz;
949*43f728cbSDavid du Colombier 	u8int patch_dac;
950*43f728cbSDavid du Colombier 	u8int rfswitch;
951*43f728cbSDavid du Colombier 	u8int ext_2ghz_lna;
952*43f728cbSDavid du Colombier 	u8int ext_5ghz_lna;
953*43f728cbSDavid du Colombier 	u8int calib_2ghz;
954*43f728cbSDavid du Colombier 	u8int calib_5ghz;
955*43f728cbSDavid du Colombier 	u8int txmixgain_2ghz;
956*43f728cbSDavid du Colombier 	u8int txmixgain_5ghz;
957*43f728cbSDavid du Colombier 	u8int tssi_2ghz[9];
958*43f728cbSDavid du Colombier 	u8int tssi_5ghz[9];
959*43f728cbSDavid du Colombier 	u8int step_2ghz;
960*43f728cbSDavid du Colombier 	u8int step_5ghz;
961*43f728cbSDavid du Colombier 	uint mgtqid;
962*43f728cbSDavid du Colombier 
963*43f728cbSDavid du Colombier 	struct {
964*43f728cbSDavid du Colombier 		u8int	reg;
965*43f728cbSDavid du Colombier 		u8int	val;
966*43f728cbSDavid du Colombier 	} bbp[8], rf[10];
967*43f728cbSDavid du Colombier 	u8int leds;
968*43f728cbSDavid du Colombier 	u16int led[3];
969*43f728cbSDavid du Colombier 	u32int txpow20mhz[5];
970*43f728cbSDavid du Colombier 	u32int txpow40mhz_2ghz[5];
971*43f728cbSDavid du Colombier 	u32int txpow40mhz_5ghz[5];
972*43f728cbSDavid du Colombier 
973*43f728cbSDavid du Colombier 	int flags;
974*43f728cbSDavid du Colombier 
975*43f728cbSDavid du Colombier 	int port;
976*43f728cbSDavid du Colombier 	int power;
977*43f728cbSDavid du Colombier 	int active;
978*43f728cbSDavid du Colombier 	int broken;
979*43f728cbSDavid du Colombier 	int attached;
980*43f728cbSDavid du Colombier 
981*43f728cbSDavid du Colombier 	u32int *nic;
982*43f728cbSDavid du Colombier 
983*43f728cbSDavid du Colombier 	/* assigned node ids in hardware node table or -1 if unassigned */
984*43f728cbSDavid du Colombier 	int bcastnodeid;
985*43f728cbSDavid du Colombier 	int bssnodeid;
986*43f728cbSDavid du Colombier 	u8int wcid;
987*43f728cbSDavid du Colombier 	/* current receiver settings */
988*43f728cbSDavid du Colombier 	uchar bssid[Eaddrlen];
989*43f728cbSDavid du Colombier 	int channel;
990*43f728cbSDavid du Colombier 	int prom;
991*43f728cbSDavid du Colombier 	int aid;
992*43f728cbSDavid du Colombier 
993*43f728cbSDavid du Colombier 	RXQ rx;
994*43f728cbSDavid du Colombier 	TXQ tx[6];
995*43f728cbSDavid du Colombier 	Pool pool;
996*43f728cbSDavid du Colombier 
997*43f728cbSDavid du Colombier 	FWImage *fw;
998*43f728cbSDavid du Colombier };
999*43f728cbSDavid du Colombier 
1000*43f728cbSDavid du Colombier /* controller flags */
1001*43f728cbSDavid du Colombier enum {
1002*43f728cbSDavid du Colombier 	AdvancedPs = 1 << 0,
1003*43f728cbSDavid du Colombier 	ConnPciE   = 1 << 1,
1004*43f728cbSDavid du Colombier };
1005*43f728cbSDavid du Colombier 
1006*43f728cbSDavid du Colombier static const struct rt2860_rate {
1007*43f728cbSDavid du Colombier 	u8int		rate;
1008*43f728cbSDavid du Colombier 	u8int		mcs;
1009*43f728cbSDavid du Colombier 	/*enum		ieee80211_phytype phy;*/
1010*43f728cbSDavid du Colombier 	u8int		ctl_ridx;
1011*43f728cbSDavid du Colombier 	u16int		sp_ack_dur;
1012*43f728cbSDavid du Colombier 	u16int		lp_ack_dur;
1013*43f728cbSDavid du Colombier } rt2860_rates[] = {
1014*43f728cbSDavid du Colombier 	{   2, 0,/* IEEE80211_T_DS,*/   0, 314, 314 },
1015*43f728cbSDavid du Colombier 	{   4, 1,/* IEEE80211_T_DS,*/   1, 258, 162 },
1016*43f728cbSDavid du Colombier 	{  11, 2,/* IEEE80211_T_DS,*/   2, 223, 127 },
1017*43f728cbSDavid du Colombier 	{  22, 3,/* IEEE80211_T_DS,*/   3, 213, 117 },
1018*43f728cbSDavid du Colombier 	{  12, 0,/* IEEE80211_T_OFDM,*/ 4,  60,  60 },
1019*43f728cbSDavid du Colombier 	{  18, 1,/* IEEE80211_T_OFDM,*/ 4,  52,  52 },
1020*43f728cbSDavid du Colombier 	{  24, 2,/* IEEE80211_T_OFDM,*/ 6,  48,  48 },
1021*43f728cbSDavid du Colombier 	{  36, 3,/* IEEE80211_T_OFDM,*/ 6,  44,  44 },
1022*43f728cbSDavid du Colombier 	{  48, 4,/* IEEE80211_T_OFDM,*/ 8,  44,  44 },
1023*43f728cbSDavid du Colombier 	{  72, 5,/* IEEE80211_T_OFDM,*/ 8,  40,  40 },
1024*43f728cbSDavid du Colombier 	{  96, 6,/* IEEE80211_T_OFDM,*/ 8,  40,  40 },
1025*43f728cbSDavid du Colombier 	{ 108, 7,/* IEEE80211_T_OFDM,*/ 8,  40,  40 }
1026*43f728cbSDavid du Colombier };
1027*43f728cbSDavid du Colombier 
1028*43f728cbSDavid du Colombier /*
1029*43f728cbSDavid du Colombier  * Default values for MAC registers; values taken from the reference driver.
1030*43f728cbSDavid du Colombier  */
1031*43f728cbSDavid du Colombier static const struct {
1032*43f728cbSDavid du Colombier 	u32int	reg;
1033*43f728cbSDavid du Colombier 	u32int	val;
1034*43f728cbSDavid du Colombier } rt2860_def_mac[] = {
1035*43f728cbSDavid du Colombier 	{ BcnOffset0,	 	 0xf8f0e8e0 },
1036*43f728cbSDavid du Colombier 	{ LegacyBasicRate,	 0x0000013f },
1037*43f728cbSDavid du Colombier 	{ HtBasicRate,		 0x00008003 },
1038*43f728cbSDavid du Colombier 	{ MacSysCtrl,	 	 0x00000000 },
1039*43f728cbSDavid du Colombier 	{ BkoffSlotCfg,		 0x00000209 },
1040*43f728cbSDavid du Colombier 	{ TxSwCfg0,			 0x00000000 },
1041*43f728cbSDavid du Colombier 	{ TxSwCfg1,			 0x00080606 },
1042*43f728cbSDavid du Colombier 	{ TxLinkCfg,		 0x00001020 },
1043*43f728cbSDavid du Colombier 	{ TxTimeoutCfg,		 0x000a2090 },
1044*43f728cbSDavid du Colombier 	{ LedCfg,			 0x7f031e46 },
1045*43f728cbSDavid du Colombier 	{ WmmAifsnCfg,		 0x00002273 },
1046*43f728cbSDavid du Colombier 	{ WmmCwminCfg,		 0x00002344 },
1047*43f728cbSDavid du Colombier 	{ WmmCwmaxCfg,		 0x000034aa },
1048*43f728cbSDavid du Colombier 	{ MaxPcnt,			 0x1f3fbf9f },
1049*43f728cbSDavid du Colombier 	{ TxRtyCfg,			 0x47d01f0f },
1050*43f728cbSDavid du Colombier 	{ AutoRspCfg,		 0x00000013 },
1051*43f728cbSDavid du Colombier 	{ CckProtCfg,		 0x05740003 },
1052*43f728cbSDavid du Colombier 	{ OfdmProtCfg,		 0x05740003 },
1053*43f728cbSDavid du Colombier 	{ Gf20ProtCfg,		 0x01744004 },
1054*43f728cbSDavid du Colombier 	{ Gf40ProtCfg,		 0x03f44084 },
1055*43f728cbSDavid du Colombier 	{ Mm20ProtCfg,		 0x01744004 },
1056*43f728cbSDavid du Colombier 	{ Mm40ProtCfg,		 0x03f54084 },
1057*43f728cbSDavid du Colombier 	{ TxopCtrlCfg,		 0x0000583f },
1058*43f728cbSDavid du Colombier 	{ TxopHldrEt,		 0x00000002 },
1059*43f728cbSDavid du Colombier 	{ TxRtsCfg,			 0x00092b20 },
1060*43f728cbSDavid du Colombier 	{ ExpAckTime,		 0x002400ca },
1061*43f728cbSDavid du Colombier 	{ XifsTimeCfg,		 0x33a41010 },
1062*43f728cbSDavid du Colombier 	{ PwrPinCfg,		 0x00000003 },
1063*43f728cbSDavid du Colombier };
1064*43f728cbSDavid du Colombier 
1065*43f728cbSDavid du Colombier /*
1066*43f728cbSDavid du Colombier  * Default values for BBP registers; values taken from the reference driver.
1067*43f728cbSDavid du Colombier  */
1068*43f728cbSDavid du Colombier static const struct {
1069*43f728cbSDavid du Colombier 	u8int	reg;
1070*43f728cbSDavid du Colombier 	u8int	val;
1071*43f728cbSDavid du Colombier } rt2860_def_bbp[] = {
1072*43f728cbSDavid du Colombier 	{  65, 0x2c },
1073*43f728cbSDavid du Colombier 	{  66, 0x38 },
1074*43f728cbSDavid du Colombier 	{  69, 0x12 },
1075*43f728cbSDavid du Colombier 	{  70, 0x0a },
1076*43f728cbSDavid du Colombier 	{  73, 0x10 },
1077*43f728cbSDavid du Colombier 	{  81, 0x37 },
1078*43f728cbSDavid du Colombier 	{  82, 0x62 },
1079*43f728cbSDavid du Colombier 	{  83, 0x6a },
1080*43f728cbSDavid du Colombier 	{  84, 0x99 },
1081*43f728cbSDavid du Colombier 	{  86, 0x00 },
1082*43f728cbSDavid du Colombier 	{  91, 0x04 },
1083*43f728cbSDavid du Colombier 	{  92, 0x00 },
1084*43f728cbSDavid du Colombier 	{ 103, 0x00 },
1085*43f728cbSDavid du Colombier 	{ 105, 0x05 },
1086*43f728cbSDavid du Colombier 	{ 106, 0x35 },
1087*43f728cbSDavid du Colombier };
1088*43f728cbSDavid du Colombier 
1089*43f728cbSDavid du Colombier /*
1090*43f728cbSDavid du Colombier  * Default settings for RF registers; values derived from the reference driver.
1091*43f728cbSDavid du Colombier  */
1092*43f728cbSDavid du Colombier static const struct rfprog {
1093*43f728cbSDavid du Colombier 	u8int		chan;
1094*43f728cbSDavid du Colombier 	u32int		r1, r2, r3, r4;
1095*43f728cbSDavid du Colombier } rt2860_rf2850[] = {
1096*43f728cbSDavid du Colombier 	{   1, 0x100bb3, 0x1301e1, 0x05a014, 0x001402 },
1097*43f728cbSDavid du Colombier 	{   2, 0x100bb3, 0x1301e1, 0x05a014, 0x001407 },
1098*43f728cbSDavid du Colombier 	{   3, 0x100bb3, 0x1301e2, 0x05a014, 0x001402 },
1099*43f728cbSDavid du Colombier 	{   4, 0x100bb3, 0x1301e2, 0x05a014, 0x001407 },
1100*43f728cbSDavid du Colombier 	{   5, 0x100bb3, 0x1301e3, 0x05a014, 0x001402 },
1101*43f728cbSDavid du Colombier 	{   6, 0x100bb3, 0x1301e3, 0x05a014, 0x001407 },
1102*43f728cbSDavid du Colombier 	{   7, 0x100bb3, 0x1301e4, 0x05a014, 0x001402 },
1103*43f728cbSDavid du Colombier 	{   8, 0x100bb3, 0x1301e4, 0x05a014, 0x001407 },
1104*43f728cbSDavid du Colombier 	{   9, 0x100bb3, 0x1301e5, 0x05a014, 0x001402 },
1105*43f728cbSDavid du Colombier 	{  10, 0x100bb3, 0x1301e5, 0x05a014, 0x001407 },
1106*43f728cbSDavid du Colombier 	{  11, 0x100bb3, 0x1301e6, 0x05a014, 0x001402 },
1107*43f728cbSDavid du Colombier 	{  12, 0x100bb3, 0x1301e6, 0x05a014, 0x001407 },
1108*43f728cbSDavid du Colombier 	{  13, 0x100bb3, 0x1301e7, 0x05a014, 0x001402 },
1109*43f728cbSDavid du Colombier 	{  14, 0x100bb3, 0x1301e8, 0x05a014, 0x001404 },
1110*43f728cbSDavid du Colombier 	{  36, 0x100bb3, 0x130266, 0x056014, 0x001408 },
1111*43f728cbSDavid du Colombier 	{  38, 0x100bb3, 0x130267, 0x056014, 0x001404 },
1112*43f728cbSDavid du Colombier 	{  40, 0x100bb2, 0x1301a0, 0x056014, 0x001400 },
1113*43f728cbSDavid du Colombier 	{  44, 0x100bb2, 0x1301a0, 0x056014, 0x001408 },
1114*43f728cbSDavid du Colombier 	{  46, 0x100bb2, 0x1301a1, 0x056014, 0x001402 },
1115*43f728cbSDavid du Colombier 	{  48, 0x100bb2, 0x1301a1, 0x056014, 0x001406 },
1116*43f728cbSDavid du Colombier 	{  52, 0x100bb2, 0x1301a2, 0x056014, 0x001404 },
1117*43f728cbSDavid du Colombier 	{  54, 0x100bb2, 0x1301a2, 0x056014, 0x001408 },
1118*43f728cbSDavid du Colombier 	{  56, 0x100bb2, 0x1301a3, 0x056014, 0x001402 },
1119*43f728cbSDavid du Colombier 	{  60, 0x100bb2, 0x1301a4, 0x056014, 0x001400 },
1120*43f728cbSDavid du Colombier 	{  62, 0x100bb2, 0x1301a4, 0x056014, 0x001404 },
1121*43f728cbSDavid du Colombier 	{  64, 0x100bb2, 0x1301a4, 0x056014, 0x001408 },
1122*43f728cbSDavid du Colombier 	{ 100, 0x100bb2, 0x1301ac, 0x05e014, 0x001400 },
1123*43f728cbSDavid du Colombier 	{ 102, 0x100bb2, 0x1701ac, 0x15e014, 0x001404 },
1124*43f728cbSDavid du Colombier 	{ 104, 0x100bb2, 0x1701ac, 0x15e014, 0x001408 },
1125*43f728cbSDavid du Colombier 	{ 108, 0x100bb3, 0x17028c, 0x15e014, 0x001404 },
1126*43f728cbSDavid du Colombier 	{ 110, 0x100bb3, 0x13028d, 0x05e014, 0x001400 },
1127*43f728cbSDavid du Colombier 	{ 112, 0x100bb3, 0x13028d, 0x05e014, 0x001406 },
1128*43f728cbSDavid du Colombier 	{ 116, 0x100bb3, 0x13028e, 0x05e014, 0x001408 },
1129*43f728cbSDavid du Colombier 	{ 118, 0x100bb3, 0x13028f, 0x05e014, 0x001404 },
1130*43f728cbSDavid du Colombier 	{ 120, 0x100bb1, 0x1300e0, 0x05e014, 0x001400 },
1131*43f728cbSDavid du Colombier 	{ 124, 0x100bb1, 0x1300e0, 0x05e014, 0x001404 },
1132*43f728cbSDavid du Colombier 	{ 126, 0x100bb1, 0x1300e0, 0x05e014, 0x001406 },
1133*43f728cbSDavid du Colombier 	{ 128, 0x100bb1, 0x1300e0, 0x05e014, 0x001408 },
1134*43f728cbSDavid du Colombier 	{ 132, 0x100bb1, 0x1300e1, 0x05e014, 0x001402 },
1135*43f728cbSDavid du Colombier 	{ 134, 0x100bb1, 0x1300e1, 0x05e014, 0x001404 },
1136*43f728cbSDavid du Colombier 	{ 136, 0x100bb1, 0x1300e1, 0x05e014, 0x001406 },
1137*43f728cbSDavid du Colombier 	{ 140, 0x100bb1, 0x1300e2, 0x05e014, 0x001400 },
1138*43f728cbSDavid du Colombier 	{ 149, 0x100bb1, 0x1300e2, 0x05e014, 0x001409 },
1139*43f728cbSDavid du Colombier 	{ 151, 0x100bb1, 0x1300e3, 0x05e014, 0x001401 },
1140*43f728cbSDavid du Colombier 	{ 153, 0x100bb1, 0x1300e3, 0x05e014, 0x001403 },
1141*43f728cbSDavid du Colombier 	{ 157, 0x100bb1, 0x1300e3, 0x05e014, 0x001407 },
1142*43f728cbSDavid du Colombier 	{ 159, 0x100bb1, 0x1300e3, 0x05e014, 0x001409 },
1143*43f728cbSDavid du Colombier 	{ 161, 0x100bb1, 0x1300e4, 0x05e014, 0x001401 },
1144*43f728cbSDavid du Colombier 	{ 165, 0x100bb1, 0x1300e4, 0x05e014, 0x001405 },
1145*43f728cbSDavid du Colombier 	{ 167, 0x100bb1, 0x1300f4, 0x05e014, 0x001407 },
1146*43f728cbSDavid du Colombier 	{ 169, 0x100bb1, 0x1300f4, 0x05e014, 0x001409 },
1147*43f728cbSDavid du Colombier 	{ 171, 0x100bb1, 0x1300f5, 0x05e014, 0x001401 },
1148*43f728cbSDavid du Colombier 	{ 173, 0x100bb1, 0x1300f5, 0x05e014, 0x001403 },
1149*43f728cbSDavid du Colombier };
1150*43f728cbSDavid du Colombier 
1151*43f728cbSDavid du Colombier struct {
1152*43f728cbSDavid du Colombier 	u8int n;
1153*43f728cbSDavid du Colombier 	u8int r;
1154*43f728cbSDavid du Colombier 	u8int k;
1155*43f728cbSDavid du Colombier } rt3090_freqs[] = {
1156*43f728cbSDavid du Colombier 	{ 0xf1, 2,  2 },
1157*43f728cbSDavid du Colombier 	{ 0xf1, 2,  7 },
1158*43f728cbSDavid du Colombier 	{ 0xf2, 2,  2 },
1159*43f728cbSDavid du Colombier 	{ 0xf2, 2,  7 },
1160*43f728cbSDavid du Colombier 	{ 0xf3, 2,  2 },
1161*43f728cbSDavid du Colombier 	{ 0xf3, 2,  7 },
1162*43f728cbSDavid du Colombier 	{ 0xf4, 2,  2 },
1163*43f728cbSDavid du Colombier 	{ 0xf4, 2,  7 },
1164*43f728cbSDavid du Colombier 	{ 0xf5, 2,  2 },
1165*43f728cbSDavid du Colombier 	{ 0xf5, 2,  7 },
1166*43f728cbSDavid du Colombier 	{ 0xf6, 2,  2 },
1167*43f728cbSDavid du Colombier 	{ 0xf6, 2,  7 },
1168*43f728cbSDavid du Colombier 	{ 0xf7, 2,  2 },
1169*43f728cbSDavid du Colombier 	{ 0xf8, 2,  4 },
1170*43f728cbSDavid du Colombier 	{ 0x56, 0,  4 },
1171*43f728cbSDavid du Colombier 	{ 0x56, 0,  6 },
1172*43f728cbSDavid du Colombier 	{ 0x56, 0,  8 },
1173*43f728cbSDavid du Colombier 	{ 0x57, 0,  0 },
1174*43f728cbSDavid du Colombier 	{ 0x57, 0,  2 },
1175*43f728cbSDavid du Colombier 	{ 0x57, 0,  4 },
1176*43f728cbSDavid du Colombier 	{ 0x57, 0,  8 },
1177*43f728cbSDavid du Colombier 	{ 0x57, 0, 10 },
1178*43f728cbSDavid du Colombier 	{ 0x58, 0,  0 },
1179*43f728cbSDavid du Colombier 	{ 0x58, 0,  4 },
1180*43f728cbSDavid du Colombier 	{ 0x58, 0,  6 },
1181*43f728cbSDavid du Colombier 	{ 0x58, 0,  8 },
1182*43f728cbSDavid du Colombier 	{ 0x5b, 0,  8 },
1183*43f728cbSDavid du Colombier 	{ 0x5b, 0, 10 },
1184*43f728cbSDavid du Colombier 	{ 0x5c, 0,  0 },
1185*43f728cbSDavid du Colombier 	{ 0x5c, 0,  4 },
1186*43f728cbSDavid du Colombier 	{ 0x5c, 0,  6 },
1187*43f728cbSDavid du Colombier 	{ 0x5c, 0,  8 },
1188*43f728cbSDavid du Colombier 	{ 0x5d, 0,  0 },
1189*43f728cbSDavid du Colombier 	{ 0x5d, 0,  2 },
1190*43f728cbSDavid du Colombier 	{ 0x5d, 0,  4 },
1191*43f728cbSDavid du Colombier 	{ 0x5d, 0,  8 },
1192*43f728cbSDavid du Colombier 	{ 0x5d, 0, 10 },
1193*43f728cbSDavid du Colombier 	{ 0x5e, 0,  0 },
1194*43f728cbSDavid du Colombier 	{ 0x5e, 0,  4 },
1195*43f728cbSDavid du Colombier 	{ 0x5e, 0,  6 },
1196*43f728cbSDavid du Colombier 	{ 0x5e, 0,  8 },
1197*43f728cbSDavid du Colombier 	{ 0x5f, 0,  0 },
1198*43f728cbSDavid du Colombier 	{ 0x5f, 0,  9 },
1199*43f728cbSDavid du Colombier 	{ 0x5f, 0, 11 },
1200*43f728cbSDavid du Colombier 	{ 0x60, 0,  1 },
1201*43f728cbSDavid du Colombier 	{ 0x60, 0,  5 },
1202*43f728cbSDavid du Colombier 	{ 0x60, 0,  7 },
1203*43f728cbSDavid du Colombier 	{ 0x60, 0,  9 },
1204*43f728cbSDavid du Colombier 	{ 0x61, 0,  1 },
1205*43f728cbSDavid du Colombier 	{ 0x61, 0,  3 },
1206*43f728cbSDavid du Colombier 	{ 0x61, 0,  5 },
1207*43f728cbSDavid du Colombier 	{ 0x61, 0,  7 },
1208*43f728cbSDavid du Colombier 	{ 0x61, 0,  9 }
1209*43f728cbSDavid du Colombier };
1210*43f728cbSDavid du Colombier 
1211*43f728cbSDavid du Colombier static const struct {
1212*43f728cbSDavid du Colombier 	u8int reg;
1213*43f728cbSDavid du Colombier 	u8int val;
1214*43f728cbSDavid du Colombier } rt3090_def_rf[] = {
1215*43f728cbSDavid du Colombier 	{  4, 0x40 },
1216*43f728cbSDavid du Colombier 	{  5, 0x03 },
1217*43f728cbSDavid du Colombier 	{  6, 0x02 },
1218*43f728cbSDavid du Colombier 	{  7, 0x70 },
1219*43f728cbSDavid du Colombier 	{  9, 0x0f },
1220*43f728cbSDavid du Colombier 	{ 10, 0x41 },
1221*43f728cbSDavid du Colombier 	{ 11, 0x21 },
1222*43f728cbSDavid du Colombier 	{ 12, 0x7b },
1223*43f728cbSDavid du Colombier 	{ 14, 0x90 },
1224*43f728cbSDavid du Colombier 	{ 15, 0x58 },
1225*43f728cbSDavid du Colombier 	{ 16, 0xb3 },
1226*43f728cbSDavid du Colombier 	{ 17, 0x92 },
1227*43f728cbSDavid du Colombier 	{ 18, 0x2c },
1228*43f728cbSDavid du Colombier 	{ 19, 0x02 },
1229*43f728cbSDavid du Colombier 	{ 20, 0xba },
1230*43f728cbSDavid du Colombier 	{ 21, 0xdb },
1231*43f728cbSDavid du Colombier 	{ 24, 0x16 },
1232*43f728cbSDavid du Colombier 	{ 25, 0x01 },
1233*43f728cbSDavid du Colombier 	{ 29, 0x1f }
1234*43f728cbSDavid du Colombier };
1235*43f728cbSDavid du Colombier 
1236*43f728cbSDavid du Colombier /* vendors */
1237*43f728cbSDavid du Colombier enum {
1238*43f728cbSDavid du Colombier 	Ralink = 0x1814,
1239*43f728cbSDavid du Colombier 	Awt = 0x1a3b,
1240*43f728cbSDavid du Colombier };
1241*43f728cbSDavid du Colombier /* products */
1242*43f728cbSDavid du Colombier enum {
1243*43f728cbSDavid du Colombier 	RalinkRT2890 = 0x0681,
1244*43f728cbSDavid du Colombier 	RalinkRT2790 = 0x0781,
1245*43f728cbSDavid du Colombier 	RalinkRT3090 = 0x3090,
1246*43f728cbSDavid du Colombier 	AwtRT2890 = 0x1059,
1247*43f728cbSDavid du Colombier };
1248*43f728cbSDavid du Colombier 
1249*43f728cbSDavid du Colombier #define csr32r(c, r)	(*((c)->nic+((r)/4)))
1250*43f728cbSDavid du Colombier #define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
1251*43f728cbSDavid du Colombier 
1252*43f728cbSDavid du Colombier static int rbplant(Ctlr*, int);
1253*43f728cbSDavid du Colombier static void setchan(Ctlr*, uint);
1254*43f728cbSDavid du Colombier static void rt3090setchan(Ctlr*, uint);
1255*43f728cbSDavid du Colombier static void selchangroup(Ctlr*, int);
1256*43f728cbSDavid du Colombier static void setleds(Ctlr*, u16int);
1257*43f728cbSDavid du Colombier 
1258*43f728cbSDavid du Colombier static uint
get16(uchar * p)1259*43f728cbSDavid du Colombier get16(uchar *p){
1260*43f728cbSDavid du Colombier 	return *((u16int*)p);
1261*43f728cbSDavid du Colombier }
1262*43f728cbSDavid du Colombier static uint
get32(uchar * p)1263*43f728cbSDavid du Colombier get32(uchar *p){
1264*43f728cbSDavid du Colombier 	return *((u32int*)p);
1265*43f728cbSDavid du Colombier }
1266*43f728cbSDavid du Colombier static void
put32(uchar * p,uint v)1267*43f728cbSDavid du Colombier put32(uchar *p, uint v){
1268*43f728cbSDavid du Colombier 	*((u32int*)p) = v;
1269*43f728cbSDavid du Colombier }
1270*43f728cbSDavid du Colombier static void
put16(uchar * p,uint v)1271*43f728cbSDavid du Colombier put16(uchar *p, uint v){
1272*43f728cbSDavid du Colombier 	*((u16int*)p) = v;
1273*43f728cbSDavid du Colombier };
1274*43f728cbSDavid du Colombier static void
memwrite(Ctlr * ctlr,u32int off,uchar * data,uint size)1275*43f728cbSDavid du Colombier memwrite(Ctlr *ctlr, u32int off, uchar *data, uint size){
1276*43f728cbSDavid du Colombier 	memmove((uchar*)ctlr->nic + off, data, size);
1277*43f728cbSDavid du Colombier }
1278*43f728cbSDavid du Colombier static void
setregion(Ctlr * ctlr,u32int off,uint val,uint size)1279*43f728cbSDavid du Colombier setregion(Ctlr *ctlr, u32int off, uint val, uint size){
1280*43f728cbSDavid du Colombier 	memset((uchar*)ctlr->nic + off, val, size);
1281*43f728cbSDavid du Colombier }
1282*43f728cbSDavid du Colombier 
1283*43f728cbSDavid du Colombier static long
rt2860ctl(Ether * edev,void * buf,long n)1284*43f728cbSDavid du Colombier rt2860ctl(Ether *edev, void *buf, long n)
1285*43f728cbSDavid du Colombier {
1286*43f728cbSDavid du Colombier 	Ctlr *ctlr;
1287*43f728cbSDavid du Colombier 
1288*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
1289*43f728cbSDavid du Colombier 	if(ctlr->wifi)
1290*43f728cbSDavid du Colombier 		return wifictl(ctlr->wifi, buf, n);
1291*43f728cbSDavid du Colombier 	return 0;
1292*43f728cbSDavid du Colombier }
1293*43f728cbSDavid du Colombier 
1294*43f728cbSDavid du Colombier static long
rt2860ifstat(Ether * edev,void * buf,long n,ulong off)1295*43f728cbSDavid du Colombier rt2860ifstat(Ether *edev, void *buf, long n, ulong off)
1296*43f728cbSDavid du Colombier {
1297*43f728cbSDavid du Colombier 	Ctlr *ctlr;
1298*43f728cbSDavid du Colombier 
1299*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
1300*43f728cbSDavid du Colombier 	if(ctlr->wifi)
1301*43f728cbSDavid du Colombier 		return wifistat(ctlr->wifi, buf, n, off);
1302*43f728cbSDavid du Colombier 	return 0;
1303*43f728cbSDavid du Colombier }
1304*43f728cbSDavid du Colombier 
1305*43f728cbSDavid du Colombier static void
setoptions(Ether * edev)1306*43f728cbSDavid du Colombier setoptions(Ether *edev)
1307*43f728cbSDavid du Colombier {
1308*43f728cbSDavid du Colombier 	Ctlr *ctlr;
1309*43f728cbSDavid du Colombier 	int i;
1310*43f728cbSDavid du Colombier 
1311*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
1312*43f728cbSDavid du Colombier 	for(i = 0; i < edev->nopt; i++)
1313*43f728cbSDavid du Colombier 		wificfg(ctlr->wifi, edev->opt[i]);
1314*43f728cbSDavid du Colombier }
1315*43f728cbSDavid du Colombier 
1316*43f728cbSDavid du Colombier static void
rxon(Ether * edev,Wnode * bss)1317*43f728cbSDavid du Colombier rxon(Ether *edev, Wnode *bss)
1318*43f728cbSDavid du Colombier {
1319*43f728cbSDavid du Colombier 	u32int tmp;
1320*43f728cbSDavid du Colombier 	Ctlr *ctlr;
1321*43f728cbSDavid du Colombier 	int cap;
1322*43f728cbSDavid du Colombier 
1323*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
1324*43f728cbSDavid du Colombier 
1325*43f728cbSDavid du Colombier 	if(bss != nil){
1326*43f728cbSDavid du Colombier 		cap = bss->cap;
1327*43f728cbSDavid du Colombier 		ctlr->channel = bss->channel;
1328*43f728cbSDavid du Colombier 		memmove(ctlr->bssid, bss->bssid, Eaddrlen);
1329*43f728cbSDavid du Colombier 		ctlr->aid = bss->aid;
1330*43f728cbSDavid du Colombier 		if(ctlr->aid != 0){
1331*43f728cbSDavid du Colombier 			if(ctlr->wifi->debug)
1332*43f728cbSDavid du Colombier 				print("new assoc!");
1333*43f728cbSDavid du Colombier 			ctlr->bssnodeid = -1;
1334*43f728cbSDavid du Colombier 		}else
1335*43f728cbSDavid du Colombier 			ctlr->bcastnodeid = -1;
1336*43f728cbSDavid du Colombier 	}else{
1337*43f728cbSDavid du Colombier 		cap = 0;
1338*43f728cbSDavid du Colombier 		memmove(ctlr->bssid, edev->bcast, Eaddrlen);
1339*43f728cbSDavid du Colombier 		ctlr->aid = 0;
1340*43f728cbSDavid du Colombier 		ctlr->bcastnodeid = -1;
1341*43f728cbSDavid du Colombier 		ctlr->bssnodeid = -1;
1342*43f728cbSDavid du Colombier 	}
1343*43f728cbSDavid du Colombier 	if(ctlr->aid != 0)
1344*43f728cbSDavid du Colombier 		setleds(ctlr, LedRadio | LedLink2ghz);
1345*43f728cbSDavid du Colombier 	else
1346*43f728cbSDavid du Colombier 		setleds(ctlr, LedRadio);
1347*43f728cbSDavid du Colombier 
1348*43f728cbSDavid du Colombier 	if(ctlr->wifi->debug)
1349*43f728cbSDavid du Colombier 		print("#l%d: rxon: bssid %E, aid %x, channel %d wcid %d\n",
1350*43f728cbSDavid du Colombier 			edev->ctlrno, ctlr->bssid, ctlr->aid, ctlr->channel, ctlr->wcid);
1351*43f728cbSDavid du Colombier 
1352*43f728cbSDavid du Colombier 	/* Set channel */
1353*43f728cbSDavid du Colombier 	if(ctlr->mac_ver >= 0x3071)
1354*43f728cbSDavid du Colombier 		rt3090setchan(ctlr, ctlr->channel);
1355*43f728cbSDavid du Colombier 	else
1356*43f728cbSDavid du Colombier 		setchan(ctlr, ctlr->channel);
1357*43f728cbSDavid du Colombier 	selchangroup(ctlr, 0);
1358*43f728cbSDavid du Colombier 	microdelay(1000);
1359*43f728cbSDavid du Colombier 
1360*43f728cbSDavid du Colombier 	/* enable mrr(?) */
1361*43f728cbSDavid du Colombier #define CCK(mcs)	(mcs)
1362*43f728cbSDavid du Colombier #define OFDM(mcs)	(1 << 3 | (mcs))
1363*43f728cbSDavid du Colombier 	csr32w(ctlr, LgFbkCfg0,
1364*43f728cbSDavid du Colombier 	    OFDM(6) << 28 |	/* 54->48 */
1365*43f728cbSDavid du Colombier 	    OFDM(5) << 24 |	/* 48->36 */
1366*43f728cbSDavid du Colombier 	    OFDM(4) << 20 |	/* 36->24 */
1367*43f728cbSDavid du Colombier 	    OFDM(3) << 16 |	/* 24->18 */
1368*43f728cbSDavid du Colombier 	    OFDM(2) << 12 |	/* 18->12 */
1369*43f728cbSDavid du Colombier 	    OFDM(1) <<  8 |	/* 12-> 9 */
1370*43f728cbSDavid du Colombier 	    OFDM(0) <<  4 |	/*  9-> 6 */
1371*43f728cbSDavid du Colombier 	    OFDM(0));		/*  6-> 6 */
1372*43f728cbSDavid du Colombier 
1373*43f728cbSDavid du Colombier 	csr32w(ctlr, LgFbkCfg1,
1374*43f728cbSDavid du Colombier 	    CCK(2) << 12 |	/* 11->5.5 */
1375*43f728cbSDavid du Colombier 	    CCK(1) <<  8 |	/* 5.5-> 2 */
1376*43f728cbSDavid du Colombier 	    CCK(0) <<  4 |	/*   2-> 1 */
1377*43f728cbSDavid du Colombier 	    CCK(0));		/*   1-> 1 */
1378*43f728cbSDavid du Colombier #undef OFDM
1379*43f728cbSDavid du Colombier #undef CCK
1380*43f728cbSDavid du Colombier 	/* update slot */
1381*43f728cbSDavid du Colombier 	tmp = csr32r(ctlr, BkoffSlotCfg);
1382*43f728cbSDavid du Colombier 	tmp &= ~0xff;
1383*43f728cbSDavid du Colombier 	tmp |= (cap & (1<<10)) ? 9 : 20;
1384*43f728cbSDavid du Colombier 	csr32w(ctlr, BkoffSlotCfg, tmp);
1385*43f728cbSDavid du Colombier 
1386*43f728cbSDavid du Colombier 	/* set TX preamble */
1387*43f728cbSDavid du Colombier 	tmp = csr32r(ctlr, AutoRspCfg);
1388*43f728cbSDavid du Colombier 	tmp &= ~CckShortEn;
1389*43f728cbSDavid du Colombier 	if(cap & (1<<5)) tmp |= CckShortEn;
1390*43f728cbSDavid du Colombier 	csr32w(ctlr, AutoRspCfg, tmp);
1391*43f728cbSDavid du Colombier 
1392*43f728cbSDavid du Colombier 	/* set basic rates */
1393*43f728cbSDavid du Colombier 	csr32w(ctlr, LegacyBasicRate, 0x003); /* 11B */
1394*43f728cbSDavid du Colombier 
1395*43f728cbSDavid du Colombier 	/* Set BSSID */
1396*43f728cbSDavid du Colombier 	csr32w(ctlr, MacBssidDw0,
1397*43f728cbSDavid du Colombier 	    ctlr->bssid[0] | ctlr->bssid[1] << 8 | ctlr->bssid[2] << 16 | ctlr->bssid[3] << 24);
1398*43f728cbSDavid du Colombier 	csr32w(ctlr, MacBssidDw1,
1399*43f728cbSDavid du Colombier 	    ctlr->bssid[4] | ctlr->bssid[5] << 8);
1400*43f728cbSDavid du Colombier 
1401*43f728cbSDavid du Colombier 	if(ctlr->bcastnodeid == -1){
1402*43f728cbSDavid du Colombier 		ctlr->bcastnodeid = 0xff;
1403*43f728cbSDavid du Colombier 		memwrite(ctlr, WcidEntry(ctlr->bcastnodeid), edev->bcast, Eaddrlen);
1404*43f728cbSDavid du Colombier 	}
1405*43f728cbSDavid du Colombier 	if(ctlr->bssnodeid == -1 && bss != nil && ctlr->aid != 0){
1406*43f728cbSDavid du Colombier 		ctlr->bssnodeid = 0;
1407*43f728cbSDavid du Colombier 		memwrite(ctlr, WcidEntry(ctlr->bssnodeid), ctlr->bssid, Eaddrlen);
1408*43f728cbSDavid du Colombier 	}
1409*43f728cbSDavid du Colombier }
1410*43f728cbSDavid du Colombier 
1411*43f728cbSDavid du Colombier static void
rt2860promiscuous(void * arg,int on)1412*43f728cbSDavid du Colombier rt2860promiscuous(void *arg, int on)
1413*43f728cbSDavid du Colombier {
1414*43f728cbSDavid du Colombier 	Ether *edev;
1415*43f728cbSDavid du Colombier 	Ctlr *ctlr;
1416*43f728cbSDavid du Colombier 
1417*43f728cbSDavid du Colombier 	edev = arg;
1418*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
1419*43f728cbSDavid du Colombier 	if(ctlr->attached == 0)
1420*43f728cbSDavid du Colombier 		return;
1421*43f728cbSDavid du Colombier 	qlock(ctlr);
1422*43f728cbSDavid du Colombier 	ctlr->prom = on;
1423*43f728cbSDavid du Colombier 	rxon(edev, ctlr->wifi->bss);
1424*43f728cbSDavid du Colombier 	qunlock(ctlr);
1425*43f728cbSDavid du Colombier }
1426*43f728cbSDavid du Colombier 
1427*43f728cbSDavid du Colombier static void
rt2860multicast(void *,uchar *,int)1428*43f728cbSDavid du Colombier rt2860multicast(void *, uchar*, int)
1429*43f728cbSDavid du Colombier {
1430*43f728cbSDavid du Colombier }
1431*43f728cbSDavid du Colombier 
1432*43f728cbSDavid du Colombier static FWImage*
readfirmware(void)1433*43f728cbSDavid du Colombier readfirmware(void){
1434*43f728cbSDavid du Colombier 	static char name[] = "ral-rt2860";
1435*43f728cbSDavid du Colombier 	uchar dirbuf[sizeof(Dir)+100], *data;
1436*43f728cbSDavid du Colombier 	char buf[128];
1437*43f728cbSDavid du Colombier 	FWImage *fw;
1438*43f728cbSDavid du Colombier 	int n, r;
1439*43f728cbSDavid du Colombier 	Chan *c;
1440*43f728cbSDavid du Colombier 	Dir d;
1441*43f728cbSDavid du Colombier 
1442*43f728cbSDavid du Colombier 	if(!iseve())
1443*43f728cbSDavid du Colombier 		error(Eperm);
1444*43f728cbSDavid du Colombier 	if(!waserror()){
1445*43f728cbSDavid du Colombier 		snprint(buf, sizeof buf, "/boot/%s", name);
1446*43f728cbSDavid du Colombier 		c = namec(buf, Aopen, OREAD, 0);
1447*43f728cbSDavid du Colombier 		poperror();
1448*43f728cbSDavid du Colombier 	}else{
1449*43f728cbSDavid du Colombier 		snprint(buf, sizeof buf, "/lib/firmware/%s", name);
1450*43f728cbSDavid du Colombier 		c = namec(buf, Aopen, OREAD, 0);
1451*43f728cbSDavid du Colombier 	}
1452*43f728cbSDavid du Colombier 	if(waserror()){
1453*43f728cbSDavid du Colombier 		cclose(c);
1454*43f728cbSDavid du Colombier 		nexterror();
1455*43f728cbSDavid du Colombier 	}
1456*43f728cbSDavid du Colombier 	n = devtab[c->type]->stat(c, dirbuf, sizeof dirbuf);
1457*43f728cbSDavid du Colombier 	if(n <= 0)
1458*43f728cbSDavid du Colombier 		error("can't stat firmware");
1459*43f728cbSDavid du Colombier 	convM2D(dirbuf, n, &d, nil);
1460*43f728cbSDavid du Colombier 	fw = malloc(sizeof(*fw));
1461*43f728cbSDavid du Colombier 	fw->size = d.length;
1462*43f728cbSDavid du Colombier 	data = fw->data = smalloc(d.length);
1463*43f728cbSDavid du Colombier 	if(waserror()){
1464*43f728cbSDavid du Colombier 		free(fw);
1465*43f728cbSDavid du Colombier 		nexterror();
1466*43f728cbSDavid du Colombier 	}
1467*43f728cbSDavid du Colombier 	r = 0;
1468*43f728cbSDavid du Colombier 	while(r < d.length){
1469*43f728cbSDavid du Colombier 		n = devtab[c->type]->read(c, data+r, d.length-r, (vlong)r);
1470*43f728cbSDavid du Colombier 		if(n <= 0)
1471*43f728cbSDavid du Colombier 			break;
1472*43f728cbSDavid du Colombier 		r += n;
1473*43f728cbSDavid du Colombier 	}
1474*43f728cbSDavid du Colombier 	poperror();
1475*43f728cbSDavid du Colombier 	poperror();
1476*43f728cbSDavid du Colombier 	cclose(c);
1477*43f728cbSDavid du Colombier 	return fw;
1478*43f728cbSDavid du Colombier }
1479*43f728cbSDavid du Colombier 
1480*43f728cbSDavid du Colombier static char*
boot(Ctlr * ctlr)1481*43f728cbSDavid du Colombier boot(Ctlr *ctlr)
1482*43f728cbSDavid du Colombier {
1483*43f728cbSDavid du Colombier 	int ntries;
1484*43f728cbSDavid du Colombier 
1485*43f728cbSDavid du Colombier 	/* set "host program ram write selection" bit */
1486*43f728cbSDavid du Colombier 	csr32w(ctlr, SysCtrl, HstPmSel);
1487*43f728cbSDavid du Colombier 	/* write microcode image */
1488*43f728cbSDavid du Colombier 	memwrite(ctlr, FwBase, ctlr->fw->data, ctlr->fw->size);
1489*43f728cbSDavid du Colombier 	/* kick microcontroller unit */
1490*43f728cbSDavid du Colombier 	csr32w(ctlr, SysCtrl, 0);
1491*43f728cbSDavid du Colombier 	coherence();
1492*43f728cbSDavid du Colombier 	csr32w(ctlr, SysCtrl, McuReset);
1493*43f728cbSDavid du Colombier 
1494*43f728cbSDavid du Colombier 	csr32w(ctlr, H2mBbpagent, 0);
1495*43f728cbSDavid du Colombier 	csr32w(ctlr, H2mMailbox, 0);
1496*43f728cbSDavid du Colombier 
1497*43f728cbSDavid du Colombier 	/* wait until microcontroller is ready */
1498*43f728cbSDavid du Colombier 	coherence();
1499*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 1000; ntries++){
1500*43f728cbSDavid du Colombier 		if(csr32r(ctlr, SysCtrl) & McuReady)
1501*43f728cbSDavid du Colombier 			break;
1502*43f728cbSDavid du Colombier 		microdelay(1000);
1503*43f728cbSDavid du Colombier 	}
1504*43f728cbSDavid du Colombier 	if(ntries == 1000)
1505*43f728cbSDavid du Colombier 		return "timeout waiting for MCU to initialize";
1506*43f728cbSDavid du Colombier 	return 0;
1507*43f728cbSDavid du Colombier }
1508*43f728cbSDavid du Colombier 
1509*43f728cbSDavid du Colombier /*
1510*43f728cbSDavid du Colombier  * Send a command to the 8051 microcontroller unit.
1511*43f728cbSDavid du Colombier  */
1512*43f728cbSDavid du Colombier static int
mcucmd(Ctlr * ctlr,u8int cmd,u16int arg,int wait)1513*43f728cbSDavid du Colombier mcucmd(Ctlr *ctlr, u8int cmd, u16int arg, int wait)
1514*43f728cbSDavid du Colombier {
1515*43f728cbSDavid du Colombier 	int slot, ntries;
1516*43f728cbSDavid du Colombier 	u32int tmp;
1517*43f728cbSDavid du Colombier 	u8int cid;
1518*43f728cbSDavid du Colombier 
1519*43f728cbSDavid du Colombier 	SET(slot);
1520*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 100; ntries++){
1521*43f728cbSDavid du Colombier 		if(!(csr32r(ctlr, H2mMailbox) & H2mBusy))
1522*43f728cbSDavid du Colombier 			break;
1523*43f728cbSDavid du Colombier 		microdelay(2);
1524*43f728cbSDavid du Colombier 	}
1525*43f728cbSDavid du Colombier 	if(ntries == 100)
1526*43f728cbSDavid du Colombier 		return -1;
1527*43f728cbSDavid du Colombier 
1528*43f728cbSDavid du Colombier 	cid = wait ? cmd : TokenNoIntr;
1529*43f728cbSDavid du Colombier 	csr32w(ctlr, H2mMailbox, H2mBusy | cid << 16 | arg);
1530*43f728cbSDavid du Colombier 	coherence();
1531*43f728cbSDavid du Colombier 	csr32w(ctlr, HostCmd, cmd);
1532*43f728cbSDavid du Colombier 
1533*43f728cbSDavid du Colombier 	if(!wait)
1534*43f728cbSDavid du Colombier 		return 0;
1535*43f728cbSDavid du Colombier 	/* wait for the command to complete */
1536*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 200; ntries++){
1537*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, H2mMailboxCid);
1538*43f728cbSDavid du Colombier 		/* find the command slot */
1539*43f728cbSDavid du Colombier 		for(slot = 0; slot < 4; slot++, tmp >>= 8)
1540*43f728cbSDavid du Colombier 			if((tmp & 0xff) == cid)
1541*43f728cbSDavid du Colombier 				break;
1542*43f728cbSDavid du Colombier 		if(slot < 4)
1543*43f728cbSDavid du Colombier 			break;
1544*43f728cbSDavid du Colombier 		microdelay(100);
1545*43f728cbSDavid du Colombier 	}
1546*43f728cbSDavid du Colombier 	if(ntries == 200){
1547*43f728cbSDavid du Colombier 		/* clear command and status */
1548*43f728cbSDavid du Colombier 		csr32w(ctlr, H2mMailboxStatus, 0xffffffff);
1549*43f728cbSDavid du Colombier 		csr32w(ctlr, H2mMailboxCid, 0xffffffff);
1550*43f728cbSDavid du Colombier 		return -1;
1551*43f728cbSDavid du Colombier 	}
1552*43f728cbSDavid du Colombier 	/* get command status (1 means success) */
1553*43f728cbSDavid du Colombier 	tmp = csr32r(ctlr, H2mMailboxStatus);
1554*43f728cbSDavid du Colombier 	tmp = (tmp >> (slot * 8)) & 0xff;
1555*43f728cbSDavid du Colombier 	/* clear command and status */
1556*43f728cbSDavid du Colombier 	csr32w(ctlr, H2mMailboxStatus, 0xffffffff);
1557*43f728cbSDavid du Colombier 	csr32w(ctlr, H2mMailboxCid, 0xffffffff);
1558*43f728cbSDavid du Colombier 	return (tmp == 1) ? 0 : -1;
1559*43f728cbSDavid du Colombier }
1560*43f728cbSDavid du Colombier 
1561*43f728cbSDavid du Colombier 
1562*43f728cbSDavid du Colombier /*
1563*43f728cbSDavid du Colombier  * Reading and writing from/to the BBP is different from RT2560 and RT2661.
1564*43f728cbSDavid du Colombier  * We access the BBP through the 8051 microcontroller unit which means that
1565*43f728cbSDavid du Colombier  * the microcode must be loaded first.
1566*43f728cbSDavid du Colombier  */
1567*43f728cbSDavid du Colombier static void
bbpwrite(Ctlr * ctlr,u8int reg,u8int val)1568*43f728cbSDavid du Colombier bbpwrite(Ctlr *ctlr, u8int reg, u8int val)
1569*43f728cbSDavid du Colombier {
1570*43f728cbSDavid du Colombier 	int ntries;
1571*43f728cbSDavid du Colombier 
1572*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 100; ntries++){
1573*43f728cbSDavid du Colombier 		if(!(csr32r(ctlr, H2mBbpagent) & BbpCsrKick))
1574*43f728cbSDavid du Colombier 			break;
1575*43f728cbSDavid du Colombier 		microdelay(1);
1576*43f728cbSDavid du Colombier 	}
1577*43f728cbSDavid du Colombier 	if(ntries == 100){
1578*43f728cbSDavid du Colombier 		print("could not write to BBP through MCU\n");
1579*43f728cbSDavid du Colombier 		return;
1580*43f728cbSDavid du Colombier 	}
1581*43f728cbSDavid du Colombier 
1582*43f728cbSDavid du Colombier 	csr32w(ctlr, H2mBbpagent, BbpRwParallel |
1583*43f728cbSDavid du Colombier 	    BbpCsrKick | reg << 8 | val);
1584*43f728cbSDavid du Colombier 	coherence();
1585*43f728cbSDavid du Colombier 
1586*43f728cbSDavid du Colombier 	mcucmd(ctlr, McuCmdBbp, 0, 0);
1587*43f728cbSDavid du Colombier 	microdelay(1000);
1588*43f728cbSDavid du Colombier }
1589*43f728cbSDavid du Colombier 
1590*43f728cbSDavid du Colombier static u8int
bbpread(Ctlr * ctlr,u8int reg)1591*43f728cbSDavid du Colombier bbpread(Ctlr *ctlr, u8int reg)
1592*43f728cbSDavid du Colombier {
1593*43f728cbSDavid du Colombier 	u32int val;
1594*43f728cbSDavid du Colombier 	int ntries;
1595*43f728cbSDavid du Colombier 
1596*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 100; ntries++){
1597*43f728cbSDavid du Colombier 		if(!(csr32r(ctlr, H2mBbpagent) & BbpCsrKick))
1598*43f728cbSDavid du Colombier 			break;
1599*43f728cbSDavid du Colombier 		microdelay(1);
1600*43f728cbSDavid du Colombier 	}
1601*43f728cbSDavid du Colombier 	if(ntries == 100){
1602*43f728cbSDavid du Colombier 		print("could not read from BBP through MCU");
1603*43f728cbSDavid du Colombier 		return 0;
1604*43f728cbSDavid du Colombier 	}
1605*43f728cbSDavid du Colombier 
1606*43f728cbSDavid du Colombier 	csr32w(ctlr, H2mBbpagent, BbpRwParallel |
1607*43f728cbSDavid du Colombier 	    BbpCsrKick | BbpCsrRead | reg << 8);
1608*43f728cbSDavid du Colombier 	coherence();
1609*43f728cbSDavid du Colombier 
1610*43f728cbSDavid du Colombier 	mcucmd(ctlr, McuCmdBbp, 0, 0);
1611*43f728cbSDavid du Colombier 	microdelay(1000);
1612*43f728cbSDavid du Colombier 
1613*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 100; ntries++){
1614*43f728cbSDavid du Colombier 		val = csr32r(ctlr, H2mBbpagent);
1615*43f728cbSDavid du Colombier 		if(!(val & BbpCsrKick))
1616*43f728cbSDavid du Colombier 			return val & 0xff;
1617*43f728cbSDavid du Colombier 		microdelay(1);
1618*43f728cbSDavid du Colombier 	}
1619*43f728cbSDavid du Colombier 	print("could not read from BBP through MCU\n");
1620*43f728cbSDavid du Colombier 
1621*43f728cbSDavid du Colombier 	return 0;
1622*43f728cbSDavid du Colombier }
1623*43f728cbSDavid du Colombier 
1624*43f728cbSDavid du Colombier static char*
bbpinit(Ctlr * ctlr)1625*43f728cbSDavid du Colombier bbpinit(Ctlr *ctlr)
1626*43f728cbSDavid du Colombier {
1627*43f728cbSDavid du Colombier 	int i, ntries;
1628*43f728cbSDavid du Colombier 	char *err;
1629*43f728cbSDavid du Colombier 
1630*43f728cbSDavid du Colombier 	/* wait for BBP to wake up */
1631*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 20; ntries++){
1632*43f728cbSDavid du Colombier 		u8int bbp0 = bbpread(ctlr, 0);
1633*43f728cbSDavid du Colombier 		if(bbp0 != 0 && bbp0 != 0xff)
1634*43f728cbSDavid du Colombier 			break;
1635*43f728cbSDavid du Colombier 	}
1636*43f728cbSDavid du Colombier 	if(ntries == 20){
1637*43f728cbSDavid du Colombier 		err = "timeout waiting for BBP to wake up";
1638*43f728cbSDavid du Colombier 		return err;
1639*43f728cbSDavid du Colombier 	}
1640*43f728cbSDavid du Colombier 
1641*43f728cbSDavid du Colombier 	/* initialize BBP registers to default values */
1642*43f728cbSDavid du Colombier 	for(i = 0; i < nelem(rt2860_def_bbp); i++){
1643*43f728cbSDavid du Colombier 		bbpwrite(ctlr, rt2860_def_bbp[i].reg,
1644*43f728cbSDavid du Colombier 		    rt2860_def_bbp[i].val);
1645*43f728cbSDavid du Colombier 	}
1646*43f728cbSDavid du Colombier 
1647*43f728cbSDavid du Colombier 	/* fix BBP84 for RT2860E */
1648*43f728cbSDavid du Colombier 	if(ctlr->mac_ver == 0x2860 && ctlr->mac_rev != 0x0101)
1649*43f728cbSDavid du Colombier 		bbpwrite(ctlr, 84, 0x19);
1650*43f728cbSDavid du Colombier 
1651*43f728cbSDavid du Colombier 	if(ctlr->mac_ver >= 0x3071){
1652*43f728cbSDavid du Colombier 		bbpwrite(ctlr, 79, 0x13);
1653*43f728cbSDavid du Colombier 		bbpwrite(ctlr, 80, 0x05);
1654*43f728cbSDavid du Colombier 		bbpwrite(ctlr, 81, 0x33);
1655*43f728cbSDavid du Colombier 	}else if(ctlr->mac_ver == 0x2860 && ctlr->mac_rev == 0x0100){
1656*43f728cbSDavid du Colombier 		bbpwrite(ctlr, 69, 0x16);
1657*43f728cbSDavid du Colombier 		bbpwrite(ctlr, 73, 0x12);
1658*43f728cbSDavid du Colombier 	}
1659*43f728cbSDavid du Colombier 
1660*43f728cbSDavid du Colombier 	return nil;
1661*43f728cbSDavid du Colombier 
1662*43f728cbSDavid du Colombier }
1663*43f728cbSDavid du Colombier 
1664*43f728cbSDavid du Colombier static void
setleds(Ctlr * ctlr,u16int which)1665*43f728cbSDavid du Colombier setleds(Ctlr *ctlr, u16int which)
1666*43f728cbSDavid du Colombier {
1667*43f728cbSDavid du Colombier 	mcucmd(ctlr, McuCmdLeds,
1668*43f728cbSDavid du Colombier 	    which | (ctlr->leds & 0x7f), 0);
1669*43f728cbSDavid du Colombier }
1670*43f728cbSDavid du Colombier 
1671*43f728cbSDavid du Colombier static char*
txrxon(Ctlr * ctlr)1672*43f728cbSDavid du Colombier txrxon(Ctlr *ctlr)
1673*43f728cbSDavid du Colombier {
1674*43f728cbSDavid du Colombier 	u32int tmp;
1675*43f728cbSDavid du Colombier 	int ntries;
1676*43f728cbSDavid du Colombier 	char *err;
1677*43f728cbSDavid du Colombier 
1678*43f728cbSDavid du Colombier 	SET(tmp);
1679*43f728cbSDavid du Colombier 	/* enable Tx/Rx DMA engine */
1680*43f728cbSDavid du Colombier 	csr32w(ctlr, MacSysCtrl, MacTxEn);
1681*43f728cbSDavid du Colombier 	coherence();
1682*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 200; ntries++){
1683*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, WpdmaGloCfg);
1684*43f728cbSDavid du Colombier 		if((tmp & (TxDmaBusy | RxDmaBusy)) == 0)
1685*43f728cbSDavid du Colombier 			break;
1686*43f728cbSDavid du Colombier 		microdelay(1000);
1687*43f728cbSDavid du Colombier 	}
1688*43f728cbSDavid du Colombier 	if(ntries == 200){
1689*43f728cbSDavid du Colombier 		err = "timeout waiting for DMA engine";
1690*43f728cbSDavid du Colombier 		return err;
1691*43f728cbSDavid du Colombier 	}
1692*43f728cbSDavid du Colombier 
1693*43f728cbSDavid du Colombier 	microdelay(50);
1694*43f728cbSDavid du Colombier 
1695*43f728cbSDavid du Colombier 	tmp |= RxDmaEn | TxDmaEn |
1696*43f728cbSDavid du Colombier 	    WpdmaBtSize64 << WpdmaBtSizeShift;
1697*43f728cbSDavid du Colombier 	csr32w(ctlr, WpdmaGloCfg, tmp);
1698*43f728cbSDavid du Colombier 
1699*43f728cbSDavid du Colombier 	/* set Rx filter */
1700*43f728cbSDavid du Colombier 	tmp = DropCrcErr | DropPhyErr;
1701*43f728cbSDavid du Colombier 	if(!ctlr->prom){
1702*43f728cbSDavid du Colombier 		tmp |= DropUcNome | DropDupl |
1703*43f728cbSDavid du Colombier 		    DropCts | DropBa | DropAck |
1704*43f728cbSDavid du Colombier 		    DropVerErr | DropCtrlRsv |
1705*43f728cbSDavid du Colombier 		    DropCfack | DropCfend;
1706*43f728cbSDavid du Colombier 		tmp |= DropRts | DropPspoll;
1707*43f728cbSDavid du Colombier 	}
1708*43f728cbSDavid du Colombier 	csr32w(ctlr, RxFiltrCfg, tmp);
1709*43f728cbSDavid du Colombier 
1710*43f728cbSDavid du Colombier 	csr32w(ctlr, MacSysCtrl, MacRxEn | MacTxEn);
1711*43f728cbSDavid du Colombier 
1712*43f728cbSDavid du Colombier 	return 0;
1713*43f728cbSDavid du Colombier }
1714*43f728cbSDavid du Colombier 
1715*43f728cbSDavid du Colombier /*
1716*43f728cbSDavid du Colombier  * Write to one of the 4 programmable 24-bit RF registers.
1717*43f728cbSDavid du Colombier  */
1718*43f728cbSDavid du Colombier static void
rfwrite(Ctlr * ctlr,u8int reg,u32int val)1719*43f728cbSDavid du Colombier rfwrite(Ctlr *ctlr, u8int reg, u32int val)
1720*43f728cbSDavid du Colombier {
1721*43f728cbSDavid du Colombier 	u32int tmp;
1722*43f728cbSDavid du Colombier 	int ntries;
1723*43f728cbSDavid du Colombier 
1724*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 100; ntries++){
1725*43f728cbSDavid du Colombier 		if(!(csr32r(ctlr, RfCsrCfg0) & RfRegCtrl))
1726*43f728cbSDavid du Colombier 			break;
1727*43f728cbSDavid du Colombier 		microdelay(1);
1728*43f728cbSDavid du Colombier 	}
1729*43f728cbSDavid du Colombier 	if(ntries == 100){
1730*43f728cbSDavid du Colombier 		print("could not write to RF\n");
1731*43f728cbSDavid du Colombier 		return;
1732*43f728cbSDavid du Colombier 	}
1733*43f728cbSDavid du Colombier 
1734*43f728cbSDavid du Colombier 	/* RF registers are 24-bit on the RT2860 */
1735*43f728cbSDavid du Colombier 	tmp = RfRegCtrl | 24 << RfRegWidthShift |
1736*43f728cbSDavid du Colombier 	    (val & 0x3fffff) << 2 | (reg & 3);
1737*43f728cbSDavid du Colombier 	csr32w(ctlr, RfCsrCfg0, tmp);
1738*43f728cbSDavid du Colombier }
1739*43f728cbSDavid du Colombier 
1740*43f728cbSDavid du Colombier u8int
rt3090rfread(Ctlr * ctlr,u8int reg)1741*43f728cbSDavid du Colombier rt3090rfread(Ctlr *ctlr, u8int reg)
1742*43f728cbSDavid du Colombier {
1743*43f728cbSDavid du Colombier 	u32int tmp;
1744*43f728cbSDavid du Colombier 	int ntries;
1745*43f728cbSDavid du Colombier 
1746*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 100; ntries++){
1747*43f728cbSDavid du Colombier 		if(!(csr32r(ctlr, Rt3070RfCsrCfg) & Rt3070RfKick))
1748*43f728cbSDavid du Colombier 			break;
1749*43f728cbSDavid du Colombier 		microdelay(1);
1750*43f728cbSDavid du Colombier 	}
1751*43f728cbSDavid du Colombier 	if(ntries == 100){
1752*43f728cbSDavid du Colombier 		print("could not read RF register\n");
1753*43f728cbSDavid du Colombier 		return 0xff;
1754*43f728cbSDavid du Colombier 	}
1755*43f728cbSDavid du Colombier 	tmp = Rt3070RfKick | reg << 8;
1756*43f728cbSDavid du Colombier 	csr32w(ctlr, Rt3070RfCsrCfg, tmp);
1757*43f728cbSDavid du Colombier 
1758*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 100; ntries++){
1759*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, Rt3070RfCsrCfg);
1760*43f728cbSDavid du Colombier 		if(!(tmp & Rt3070RfKick))
1761*43f728cbSDavid du Colombier 			break;
1762*43f728cbSDavid du Colombier 		microdelay(1);
1763*43f728cbSDavid du Colombier 	}
1764*43f728cbSDavid du Colombier 	if(ntries == 100){
1765*43f728cbSDavid du Colombier 		print("could not read RF register\n");
1766*43f728cbSDavid du Colombier 		return 0xff;
1767*43f728cbSDavid du Colombier 	}
1768*43f728cbSDavid du Colombier 	return tmp & 0xff;
1769*43f728cbSDavid du Colombier }
1770*43f728cbSDavid du Colombier 
1771*43f728cbSDavid du Colombier static void
rt3090rfwrite(Ctlr * ctlr,u8int reg,u8int val)1772*43f728cbSDavid du Colombier rt3090rfwrite(Ctlr *ctlr, u8int reg, u8int val)
1773*43f728cbSDavid du Colombier {
1774*43f728cbSDavid du Colombier 	u32int tmp;
1775*43f728cbSDavid du Colombier 	int ntries;
1776*43f728cbSDavid du Colombier 
1777*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 10; ntries++){
1778*43f728cbSDavid du Colombier 		if(!(csr32r(ctlr, Rt3070RfCsrCfg) & Rt3070RfKick))
1779*43f728cbSDavid du Colombier 			break;
1780*43f728cbSDavid du Colombier 		microdelay(10);
1781*43f728cbSDavid du Colombier 	}
1782*43f728cbSDavid du Colombier 	if(ntries == 10){
1783*43f728cbSDavid du Colombier 		print("could not write to RF\n");
1784*43f728cbSDavid du Colombier 		return;
1785*43f728cbSDavid du Colombier 	}
1786*43f728cbSDavid du Colombier 
1787*43f728cbSDavid du Colombier 	tmp = Rt3070RfWrite | Rt3070RfKick | reg << 8 | val;
1788*43f728cbSDavid du Colombier 	csr32w(ctlr, Rt3070RfCsrCfg, tmp);
1789*43f728cbSDavid du Colombier }
1790*43f728cbSDavid du Colombier 
1791*43f728cbSDavid du Colombier static void
selchangroup(Ctlr * ctlr,int group)1792*43f728cbSDavid du Colombier selchangroup(Ctlr *ctlr, int group)
1793*43f728cbSDavid du Colombier {
1794*43f728cbSDavid du Colombier 	u32int tmp;
1795*43f728cbSDavid du Colombier 	u8int agc;
1796*43f728cbSDavid du Colombier 
1797*43f728cbSDavid du Colombier 	bbpwrite(ctlr, 62, 0x37 - ctlr->lna[group]);
1798*43f728cbSDavid du Colombier 	bbpwrite(ctlr, 63, 0x37 - ctlr->lna[group]);
1799*43f728cbSDavid du Colombier 	bbpwrite(ctlr, 64, 0x37 - ctlr->lna[group]);
1800*43f728cbSDavid du Colombier 	bbpwrite(ctlr, 86, 0x00);
1801*43f728cbSDavid du Colombier 
1802*43f728cbSDavid du Colombier 	if(group == 0){
1803*43f728cbSDavid du Colombier 		if(ctlr->ext_2ghz_lna){
1804*43f728cbSDavid du Colombier 			bbpwrite(ctlr, 82, 0x62);
1805*43f728cbSDavid du Colombier 			bbpwrite(ctlr, 75, 0x46);
1806*43f728cbSDavid du Colombier 		}else{
1807*43f728cbSDavid du Colombier 			bbpwrite(ctlr, 82, 0x84);
1808*43f728cbSDavid du Colombier 			bbpwrite(ctlr, 75, 0x50);
1809*43f728cbSDavid du Colombier 		}
1810*43f728cbSDavid du Colombier 	}else{
1811*43f728cbSDavid du Colombier 		if(ctlr->ext_5ghz_lna){
1812*43f728cbSDavid du Colombier 			bbpwrite(ctlr, 82, 0xf2);
1813*43f728cbSDavid du Colombier 			bbpwrite(ctlr, 75, 0x46);
1814*43f728cbSDavid du Colombier 		}else{
1815*43f728cbSDavid du Colombier 			bbpwrite(ctlr, 82, 0xf2);
1816*43f728cbSDavid du Colombier 			bbpwrite(ctlr, 75, 0x50);
1817*43f728cbSDavid du Colombier 		}
1818*43f728cbSDavid du Colombier 	}
1819*43f728cbSDavid du Colombier 
1820*43f728cbSDavid du Colombier 	tmp = csr32r(ctlr, TxBandCfg);
1821*43f728cbSDavid du Colombier 	tmp &= ~(Tx5gBandSelN | Tx5gBandSelP);
1822*43f728cbSDavid du Colombier 	tmp |= (group == 0) ? Tx5gBandSelN : Tx5gBandSelP;
1823*43f728cbSDavid du Colombier 	csr32w(ctlr, TxBandCfg, tmp);
1824*43f728cbSDavid du Colombier 
1825*43f728cbSDavid du Colombier 	/* enable appropriate Power Amplifiers and Low Noise Amplifiers */
1826*43f728cbSDavid du Colombier 	tmp = RftrEn | TrswEn | LnaPe0En;
1827*43f728cbSDavid du Colombier 	if(ctlr->nrxchains > 1)
1828*43f728cbSDavid du Colombier 		tmp |= LnaPe1En;
1829*43f728cbSDavid du Colombier 	if(ctlr->mac_ver == 0x3593 && ctlr->nrxchains > 2)
1830*43f728cbSDavid du Colombier 		tmp |= Rt3593LnaPe2En;
1831*43f728cbSDavid du Colombier 	if(group == 0){	/* 2GHz */
1832*43f728cbSDavid du Colombier 		tmp |= PaPeG0En;
1833*43f728cbSDavid du Colombier 		if(ctlr->ntxchains > 1)
1834*43f728cbSDavid du Colombier 			tmp |= PaPeG1En;
1835*43f728cbSDavid du Colombier 		if(ctlr->mac_ver == 0x3593 && ctlr->ntxchains > 2)
1836*43f728cbSDavid du Colombier 			tmp |= Rt3593PaPeG2En;
1837*43f728cbSDavid du Colombier 	}else{		/* 5GHz */
1838*43f728cbSDavid du Colombier 		tmp |= PaPeA0En;
1839*43f728cbSDavid du Colombier 		if(ctlr->ntxchains > 1)
1840*43f728cbSDavid du Colombier 			tmp |= PaPeA1En;
1841*43f728cbSDavid du Colombier 		if(ctlr->mac_ver == 0x3593 && ctlr->ntxchains > 2)
1842*43f728cbSDavid du Colombier 			tmp |= Rt3593PaPeA2En;
1843*43f728cbSDavid du Colombier 	}
1844*43f728cbSDavid du Colombier 	csr32w(ctlr, TxPinCfg, tmp);
1845*43f728cbSDavid du Colombier 
1846*43f728cbSDavid du Colombier 	if(ctlr->mac_ver == 0x3593){
1847*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, GpioCtrl);
1848*43f728cbSDavid du Colombier 		if(ctlr->flags & ConnPciE){
1849*43f728cbSDavid du Colombier 			tmp &= ~0x01010000;
1850*43f728cbSDavid du Colombier 			if(group == 0)
1851*43f728cbSDavid du Colombier 				tmp |= 0x00010000;
1852*43f728cbSDavid du Colombier 		}else{
1853*43f728cbSDavid du Colombier 			tmp &= ~0x00008080;
1854*43f728cbSDavid du Colombier 			if(group == 0)
1855*43f728cbSDavid du Colombier 				tmp |= 0x00000080;
1856*43f728cbSDavid du Colombier 		}
1857*43f728cbSDavid du Colombier 		tmp = (tmp & ~0x00001000) | 0x00000010;
1858*43f728cbSDavid du Colombier 		csr32w(ctlr, GpioCtrl, tmp);
1859*43f728cbSDavid du Colombier 	}
1860*43f728cbSDavid du Colombier 
1861*43f728cbSDavid du Colombier 	/* set initial AGC value */
1862*43f728cbSDavid du Colombier 	if(group == 0){	/* 2GHz band */
1863*43f728cbSDavid du Colombier 		if(ctlr->mac_ver >= 0x3071)
1864*43f728cbSDavid du Colombier 			agc = 0x1c + ctlr->lna[0] * 2;
1865*43f728cbSDavid du Colombier 		else
1866*43f728cbSDavid du Colombier 			agc = 0x2e + ctlr->lna[0];
1867*43f728cbSDavid du Colombier 	}else{		/* 5GHz band */
1868*43f728cbSDavid du Colombier 		agc = 0x32 + (ctlr->lna[group] * 5) / 3;
1869*43f728cbSDavid du Colombier 	}
1870*43f728cbSDavid du Colombier 	bbpwrite(ctlr, 66, agc);
1871*43f728cbSDavid du Colombier 
1872*43f728cbSDavid du Colombier 	microdelay(1000);
1873*43f728cbSDavid du Colombier 
1874*43f728cbSDavid du Colombier }
1875*43f728cbSDavid du Colombier 
1876*43f728cbSDavid du Colombier static void
setchan(Ctlr * ctlr,uint chan)1877*43f728cbSDavid du Colombier setchan(Ctlr *ctlr, uint chan)
1878*43f728cbSDavid du Colombier {
1879*43f728cbSDavid du Colombier 	const struct rfprog *rfprog = rt2860_rf2850;
1880*43f728cbSDavid du Colombier 	u32int r2, r3, r4;
1881*43f728cbSDavid du Colombier 	s8int txpow1, txpow2;
1882*43f728cbSDavid du Colombier 	uint i;
1883*43f728cbSDavid du Colombier 
1884*43f728cbSDavid du Colombier 	/* find the settings for this channel (we know it exists) */
1885*43f728cbSDavid du Colombier 	for(i = 0; rfprog[i].chan != chan; i++);
1886*43f728cbSDavid du Colombier 
1887*43f728cbSDavid du Colombier 	r2 = rfprog[i].r2;
1888*43f728cbSDavid du Colombier 	if(ctlr->ntxchains == 1)
1889*43f728cbSDavid du Colombier 		r2 |= 1 << 12;		/* 1T: disable Tx chain 2 */
1890*43f728cbSDavid du Colombier 	if(ctlr->nrxchains == 1)
1891*43f728cbSDavid du Colombier 		r2 |= 1 << 15 | 1 << 4;	/* 1R: disable Rx chains 2 & 3 */
1892*43f728cbSDavid du Colombier 	else if(ctlr->nrxchains == 2)
1893*43f728cbSDavid du Colombier 		r2 |= 1 << 4;		/* 2R: disable Rx chain 3 */
1894*43f728cbSDavid du Colombier 
1895*43f728cbSDavid du Colombier 	/* use Tx power values from EEPROM */
1896*43f728cbSDavid du Colombier 	txpow1 = ctlr->txpow1[i];
1897*43f728cbSDavid du Colombier 	txpow2 = ctlr->txpow2[i];
1898*43f728cbSDavid du Colombier 	if(chan > 14){
1899*43f728cbSDavid du Colombier 		if(txpow1 >= 0)
1900*43f728cbSDavid du Colombier 			txpow1 = txpow1 << 1 | 1;
1901*43f728cbSDavid du Colombier 		else
1902*43f728cbSDavid du Colombier 			txpow1 = (7 + txpow1) << 1;
1903*43f728cbSDavid du Colombier 		if(txpow2 >= 0)
1904*43f728cbSDavid du Colombier 			txpow2 = txpow2 << 1 | 1;
1905*43f728cbSDavid du Colombier 		else
1906*43f728cbSDavid du Colombier 			txpow2 = (7 + txpow2) << 1;
1907*43f728cbSDavid du Colombier 	}
1908*43f728cbSDavid du Colombier 	r3 = rfprog[i].r3 | txpow1 << 7;
1909*43f728cbSDavid du Colombier 	r4 = rfprog[i].r4 | ctlr->freq << 13 | txpow2 << 4;
1910*43f728cbSDavid du Colombier 
1911*43f728cbSDavid du Colombier 	rfwrite(ctlr, Rf1, rfprog[i].r1);
1912*43f728cbSDavid du Colombier 	rfwrite(ctlr, Rf2, r2);
1913*43f728cbSDavid du Colombier 	rfwrite(ctlr, Rf3, r3);
1914*43f728cbSDavid du Colombier 	rfwrite(ctlr, Rf4, r4);
1915*43f728cbSDavid du Colombier 
1916*43f728cbSDavid du Colombier 	microdelay(200);
1917*43f728cbSDavid du Colombier 
1918*43f728cbSDavid du Colombier 	rfwrite(ctlr, Rf1, rfprog[i].r1);
1919*43f728cbSDavid du Colombier 	rfwrite(ctlr, Rf2, r2);
1920*43f728cbSDavid du Colombier 	rfwrite(ctlr, Rf3, r3 | 1);
1921*43f728cbSDavid du Colombier 	rfwrite(ctlr, Rf4, r4);
1922*43f728cbSDavid du Colombier 
1923*43f728cbSDavid du Colombier 	microdelay(200);
1924*43f728cbSDavid du Colombier 
1925*43f728cbSDavid du Colombier 	rfwrite(ctlr, Rf1, rfprog[i].r1);
1926*43f728cbSDavid du Colombier 	rfwrite(ctlr, Rf2, r2);
1927*43f728cbSDavid du Colombier 	rfwrite(ctlr, Rf3, r3);
1928*43f728cbSDavid du Colombier 	rfwrite(ctlr, Rf4, r4);
1929*43f728cbSDavid du Colombier }
1930*43f728cbSDavid du Colombier 
1931*43f728cbSDavid du Colombier static void
rt3090setchan(Ctlr * ctlr,uint chan)1932*43f728cbSDavid du Colombier rt3090setchan(Ctlr *ctlr, uint chan)
1933*43f728cbSDavid du Colombier {
1934*43f728cbSDavid du Colombier 	s8int txpow1, txpow2;
1935*43f728cbSDavid du Colombier 	u8int rf;
1936*43f728cbSDavid du Colombier 	int i;
1937*43f728cbSDavid du Colombier 
1938*43f728cbSDavid du Colombier 	assert(chan >= 1 && chan <= 14);	/* RT3090 is 2GHz only */
1939*43f728cbSDavid du Colombier 
1940*43f728cbSDavid du Colombier 	/* find the settings for this channel (we know it exists) */
1941*43f728cbSDavid du Colombier 	for(i = 0; rt2860_rf2850[i].chan != chan; i++);
1942*43f728cbSDavid du Colombier 
1943*43f728cbSDavid du Colombier 	/* use Tx power values from EEPROM */
1944*43f728cbSDavid du Colombier 	txpow1 = ctlr->txpow1[i];
1945*43f728cbSDavid du Colombier 	txpow2 = ctlr->txpow2[i];
1946*43f728cbSDavid du Colombier 
1947*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 2, rt3090_freqs[i].n);
1948*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 3);
1949*43f728cbSDavid du Colombier 	rf = (rf & ~0x0f) | rt3090_freqs[i].k;
1950*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 3, rf);
1951*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 6);
1952*43f728cbSDavid du Colombier 	rf = (rf & ~0x03) | rt3090_freqs[i].r;
1953*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 6, rf);
1954*43f728cbSDavid du Colombier 
1955*43f728cbSDavid du Colombier 	/* set Tx0 power */
1956*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 12);
1957*43f728cbSDavid du Colombier 	rf = (rf & ~0x1f) | txpow1;
1958*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 12, rf);
1959*43f728cbSDavid du Colombier 
1960*43f728cbSDavid du Colombier 	/* set Tx1 power */
1961*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 13);
1962*43f728cbSDavid du Colombier 	rf = (rf & ~0x1f) | txpow2;
1963*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 13, rf);
1964*43f728cbSDavid du Colombier 
1965*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 1);
1966*43f728cbSDavid du Colombier 	rf &= ~0xfc;
1967*43f728cbSDavid du Colombier 	if(ctlr->ntxchains == 1)
1968*43f728cbSDavid du Colombier 		rf |= Rt3070Tx1Pd | Rt3070Tx2Pd;
1969*43f728cbSDavid du Colombier 	else if(ctlr->ntxchains == 2)
1970*43f728cbSDavid du Colombier 		rf |= Rt3070Tx2Pd;
1971*43f728cbSDavid du Colombier 	if(ctlr->nrxchains == 1)
1972*43f728cbSDavid du Colombier 		rf |= Rt3070Rx1Pd | Rt3070Rx2Pd;
1973*43f728cbSDavid du Colombier 	else if(ctlr->nrxchains == 2)
1974*43f728cbSDavid du Colombier 		rf |= Rt3070Rx2Pd;
1975*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 1, rf);
1976*43f728cbSDavid du Colombier 
1977*43f728cbSDavid du Colombier 	/* set RF offset */
1978*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 23);
1979*43f728cbSDavid du Colombier 	rf = (rf & ~0x7f) | ctlr->freq;
1980*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 23, rf);
1981*43f728cbSDavid du Colombier 
1982*43f728cbSDavid du Colombier 	/* program RF filter */
1983*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 24);	/* Tx */
1984*43f728cbSDavid du Colombier 	rf = (rf & ~0x3f) | ctlr->rf24_20mhz;
1985*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 24, rf);
1986*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 31);	/* Rx */
1987*43f728cbSDavid du Colombier 	rf = (rf & ~0x3f) | ctlr->rf24_20mhz;
1988*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 31, rf);
1989*43f728cbSDavid du Colombier 
1990*43f728cbSDavid du Colombier 	/* enable RF tuning */
1991*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 7);
1992*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 7, rf | Rt3070Tune);
1993*43f728cbSDavid du Colombier }
1994*43f728cbSDavid du Colombier 
1995*43f728cbSDavid du Colombier static int
rt3090filtercalib(Ctlr * ctlr,u8int init,u8int target,u8int * val)1996*43f728cbSDavid du Colombier rt3090filtercalib(Ctlr *ctlr, u8int init, u8int target, u8int *val)
1997*43f728cbSDavid du Colombier {
1998*43f728cbSDavid du Colombier 	u8int rf22, rf24;
1999*43f728cbSDavid du Colombier 	u8int bbp55_pb, bbp55_sb, delta;
2000*43f728cbSDavid du Colombier 	int ntries;
2001*43f728cbSDavid du Colombier 
2002*43f728cbSDavid du Colombier 	/* program filter */
2003*43f728cbSDavid du Colombier 	rf24 = rt3090rfread(ctlr, 24);
2004*43f728cbSDavid du Colombier 	rf24 = (rf24 & 0xc0) | init;	/* initial filter value */
2005*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 24, rf24);
2006*43f728cbSDavid du Colombier 
2007*43f728cbSDavid du Colombier 	/* enable baseband loopback mode */
2008*43f728cbSDavid du Colombier 	rf22 = rt3090rfread(ctlr, 22);
2009*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 22, rf22 | Rt3070BbLoopback);
2010*43f728cbSDavid du Colombier 
2011*43f728cbSDavid du Colombier 	/* set power and frequency of passband test tone */
2012*43f728cbSDavid du Colombier 	bbpwrite(ctlr, 24, 0x00);
2013*43f728cbSDavid du Colombier 	for(ntries = 0, bbp55_pb = 0; ntries < 100; ntries++){
2014*43f728cbSDavid du Colombier 		/* transmit test tone */
2015*43f728cbSDavid du Colombier 		bbpwrite(ctlr, 25, 0x90);
2016*43f728cbSDavid du Colombier 		microdelay(1000);
2017*43f728cbSDavid du Colombier 		/* read received power */
2018*43f728cbSDavid du Colombier 		bbp55_pb = bbpread(ctlr, 55);
2019*43f728cbSDavid du Colombier 		if(bbp55_pb != 0)
2020*43f728cbSDavid du Colombier 			break;
2021*43f728cbSDavid du Colombier 	}
2022*43f728cbSDavid du Colombier 	if(ntries == 100)
2023*43f728cbSDavid du Colombier 		return -1;
2024*43f728cbSDavid du Colombier 
2025*43f728cbSDavid du Colombier 	/* set power and frequency of stopband test tone */
2026*43f728cbSDavid du Colombier 	bbpwrite(ctlr, 24, 0x06);
2027*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 100; ntries++){
2028*43f728cbSDavid du Colombier 		/* transmit test tone */
2029*43f728cbSDavid du Colombier 		bbpwrite(ctlr, 25, 0x90);
2030*43f728cbSDavid du Colombier 		microdelay(1000);
2031*43f728cbSDavid du Colombier 		/* read received power */
2032*43f728cbSDavid du Colombier 		bbp55_sb = bbpread(ctlr, 55);
2033*43f728cbSDavid du Colombier 
2034*43f728cbSDavid du Colombier 		delta = bbp55_pb - bbp55_sb;
2035*43f728cbSDavid du Colombier 		if(delta > target)
2036*43f728cbSDavid du Colombier 			break;
2037*43f728cbSDavid du Colombier 
2038*43f728cbSDavid du Colombier 		/* reprogram filter */
2039*43f728cbSDavid du Colombier 		rf24++;
2040*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 24, rf24);
2041*43f728cbSDavid du Colombier 	}
2042*43f728cbSDavid du Colombier 	if(ntries < 100){
2043*43f728cbSDavid du Colombier 		if(rf24 != init)
2044*43f728cbSDavid du Colombier 			rf24--;	/* backtrack */
2045*43f728cbSDavid du Colombier 		*val = rf24;
2046*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 24, rf24);
2047*43f728cbSDavid du Colombier 	}
2048*43f728cbSDavid du Colombier 
2049*43f728cbSDavid du Colombier 	/* restore initial state */
2050*43f728cbSDavid du Colombier 	bbpwrite(ctlr, 24, 0x00);
2051*43f728cbSDavid du Colombier 
2052*43f728cbSDavid du Colombier 	/* disable baseband loopback mode */
2053*43f728cbSDavid du Colombier 	rf22 = rt3090rfread(ctlr, 22);
2054*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 22, rf22 & ~Rt3070BbLoopback);
2055*43f728cbSDavid du Colombier 
2056*43f728cbSDavid du Colombier 	return 0;
2057*43f728cbSDavid du Colombier }
2058*43f728cbSDavid du Colombier 
2059*43f728cbSDavid du Colombier static void
rt3090setrxantenna(Ctlr * ctlr,int aux)2060*43f728cbSDavid du Colombier rt3090setrxantenna(Ctlr *ctlr, int aux)
2061*43f728cbSDavid du Colombier {
2062*43f728cbSDavid du Colombier 	u32int tmp;
2063*43f728cbSDavid du Colombier 
2064*43f728cbSDavid du Colombier 	if(aux){
2065*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, PciEectrl);
2066*43f728cbSDavid du Colombier 		csr32w(ctlr, PciEectrl, tmp & ~EectrlC);
2067*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, GpioCtrl);
2068*43f728cbSDavid du Colombier 		csr32w(ctlr, GpioCtrl, (tmp & ~0x0808) | 0x08);
2069*43f728cbSDavid du Colombier 	}else{
2070*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, PciEectrl);
2071*43f728cbSDavid du Colombier 		csr32w(ctlr, PciEectrl, tmp | EectrlC);
2072*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, GpioCtrl);
2073*43f728cbSDavid du Colombier 		csr32w(ctlr, GpioCtrl, tmp & ~0x0808);
2074*43f728cbSDavid du Colombier 	}
2075*43f728cbSDavid du Colombier }
2076*43f728cbSDavid du Colombier 
2077*43f728cbSDavid du Colombier static int
rt3090rfinit(Ctlr * ctlr)2078*43f728cbSDavid du Colombier rt3090rfinit(Ctlr *ctlr)
2079*43f728cbSDavid du Colombier {
2080*43f728cbSDavid du Colombier 	u32int tmp;
2081*43f728cbSDavid du Colombier 	u8int rf, bbp;
2082*43f728cbSDavid du Colombier 	int i;
2083*43f728cbSDavid du Colombier 
2084*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 30);
2085*43f728cbSDavid du Colombier 	/* toggle RF R30 bit 7 */
2086*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 30, rf | 0x80);
2087*43f728cbSDavid du Colombier 	microdelay(1000);
2088*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 30, rf & ~0x80);
2089*43f728cbSDavid du Colombier 
2090*43f728cbSDavid du Colombier 	tmp = csr32r(ctlr, Rt3070LdoCfg0);
2091*43f728cbSDavid du Colombier 	tmp &= ~0x1f000000;
2092*43f728cbSDavid du Colombier 	if(ctlr->patch_dac && ctlr->mac_rev < 0x0211)
2093*43f728cbSDavid du Colombier 		tmp |= 0x0d000000;	/* 1.35V */
2094*43f728cbSDavid du Colombier 	else
2095*43f728cbSDavid du Colombier 		tmp |= 0x01000000;	/* 1.2V */
2096*43f728cbSDavid du Colombier 	csr32w(ctlr, Rt3070LdoCfg0, tmp);
2097*43f728cbSDavid du Colombier 
2098*43f728cbSDavid du Colombier 	/* patch LNA_PE_G1 */
2099*43f728cbSDavid du Colombier 	tmp = csr32r(ctlr, Rt3070GpioSwitch);
2100*43f728cbSDavid du Colombier 	csr32w(ctlr, Rt3070GpioSwitch, tmp & ~0x20);
2101*43f728cbSDavid du Colombier 
2102*43f728cbSDavid du Colombier 	/* initialize RF registers to default value */
2103*43f728cbSDavid du Colombier 	for(i = 0; i < nelem(rt3090_def_rf); i++){
2104*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, rt3090_def_rf[i].reg,
2105*43f728cbSDavid du Colombier 		    rt3090_def_rf[i].val);
2106*43f728cbSDavid du Colombier 	}
2107*43f728cbSDavid du Colombier 
2108*43f728cbSDavid du Colombier 	/* select 20MHz bandwidth */
2109*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 31, 0x14);
2110*43f728cbSDavid du Colombier 
2111*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 6);
2112*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 6, rf | 0x40);
2113*43f728cbSDavid du Colombier 
2114*43f728cbSDavid du Colombier 	if(ctlr->mac_ver != 0x3593){
2115*43f728cbSDavid du Colombier 		/* calibrate filter for 20MHz bandwidth */
2116*43f728cbSDavid du Colombier 		ctlr->rf24_20mhz = 0x1f;	/* default value */
2117*43f728cbSDavid du Colombier 		rt3090filtercalib(ctlr, 0x07, 0x16, &ctlr->rf24_20mhz);
2118*43f728cbSDavid du Colombier 
2119*43f728cbSDavid du Colombier 		/* select 40MHz bandwidth */
2120*43f728cbSDavid du Colombier 		bbp = bbpread(ctlr, 4);
2121*43f728cbSDavid du Colombier 		bbpwrite(ctlr, 4, (bbp & ~0x08) | 0x10);
2122*43f728cbSDavid du Colombier 		rf = rt3090rfread(ctlr, 31);
2123*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 31, rf | 0x20);
2124*43f728cbSDavid du Colombier 
2125*43f728cbSDavid du Colombier 		/* calibrate filter for 40MHz bandwidth */
2126*43f728cbSDavid du Colombier 		ctlr->rf24_40mhz = 0x2f;	/* default value */
2127*43f728cbSDavid du Colombier 		rt3090filtercalib(ctlr, 0x27, 0x19, &ctlr->rf24_40mhz);
2128*43f728cbSDavid du Colombier 
2129*43f728cbSDavid du Colombier 		/* go back to 20MHz bandwidth */
2130*43f728cbSDavid du Colombier 		bbp = bbpread(ctlr, 4);
2131*43f728cbSDavid du Colombier 		bbpwrite(ctlr, 4, bbp & ~0x18);
2132*43f728cbSDavid du Colombier 	}
2133*43f728cbSDavid du Colombier 	if(ctlr->mac_rev < 0x0211)
2134*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 27, 0x03);
2135*43f728cbSDavid du Colombier 
2136*43f728cbSDavid du Colombier 	tmp = csr32r(ctlr, Rt3070Opt14);
2137*43f728cbSDavid du Colombier 	csr32w(ctlr, Rt3070Opt14, tmp | 1);
2138*43f728cbSDavid du Colombier 
2139*43f728cbSDavid du Colombier 	if(ctlr->rf_rev == Rf3020)
2140*43f728cbSDavid du Colombier 		rt3090setrxantenna(ctlr, 0);
2141*43f728cbSDavid du Colombier 
2142*43f728cbSDavid du Colombier 	bbp = bbpread(ctlr, 138);
2143*43f728cbSDavid du Colombier 	if(ctlr->mac_ver == 0x3593){
2144*43f728cbSDavid du Colombier 		if(ctlr->ntxchains == 1)
2145*43f728cbSDavid du Colombier 			bbp |= 0x60;	/* turn off DAC1 and DAC2 */
2146*43f728cbSDavid du Colombier 		else if(ctlr->ntxchains == 2)
2147*43f728cbSDavid du Colombier 			bbp |= 0x40;	/* turn off DAC2 */
2148*43f728cbSDavid du Colombier 		if(ctlr->nrxchains == 1)
2149*43f728cbSDavid du Colombier 			bbp &= ~0x06;	/* turn off ADC1 and ADC2 */
2150*43f728cbSDavid du Colombier 		else if(ctlr->nrxchains == 2)
2151*43f728cbSDavid du Colombier 			bbp &= ~0x04;	/* turn off ADC2 */
2152*43f728cbSDavid du Colombier 	}else{
2153*43f728cbSDavid du Colombier 		if(ctlr->ntxchains == 1)
2154*43f728cbSDavid du Colombier 			bbp |= 0x20;	/* turn off DAC1 */
2155*43f728cbSDavid du Colombier 		if(ctlr->nrxchains == 1)
2156*43f728cbSDavid du Colombier 			bbp &= ~0x02;	/* turn off ADC1 */
2157*43f728cbSDavid du Colombier 	}
2158*43f728cbSDavid du Colombier 	bbpwrite(ctlr, 138, bbp);
2159*43f728cbSDavid du Colombier 
2160*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 1);
2161*43f728cbSDavid du Colombier 	rf &= ~(Rt3070Rx0Pd | Rt3070Tx0Pd);
2162*43f728cbSDavid du Colombier 	rf |= Rt3070RfBlock | Rt3070Rx1Pd | Rt3070Tx1Pd;
2163*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 1, rf);
2164*43f728cbSDavid du Colombier 
2165*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 15);
2166*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 15, rf & ~Rt3070TxLo2);
2167*43f728cbSDavid du Colombier 
2168*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 17);
2169*43f728cbSDavid du Colombier 	rf &= ~Rt3070TxLo1;
2170*43f728cbSDavid du Colombier 	if(ctlr->mac_rev >= 0x0211 && !ctlr->ext_2ghz_lna)
2171*43f728cbSDavid du Colombier 		rf |= 0x20;	/* fix for long range Rx issue */
2172*43f728cbSDavid du Colombier 	if(ctlr->txmixgain_2ghz >= 2)
2173*43f728cbSDavid du Colombier 		rf = (rf & ~0x7) | ctlr->txmixgain_2ghz;
2174*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 17, rf);
2175*43f728cbSDavid du Colombier 
2176*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 20);
2177*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 20, rf & ~Rt3070RxLo1);
2178*43f728cbSDavid du Colombier 
2179*43f728cbSDavid du Colombier 	rf = rt3090rfread(ctlr, 21);
2180*43f728cbSDavid du Colombier 	rt3090rfwrite(ctlr, 21, rf & ~Rt3070RxLo2);
2181*43f728cbSDavid du Colombier 
2182*43f728cbSDavid du Colombier 	return 0;
2183*43f728cbSDavid du Colombier }
2184*43f728cbSDavid du Colombier 
2185*43f728cbSDavid du Colombier static void
rt3090rfwakeup(Ctlr * ctlr)2186*43f728cbSDavid du Colombier rt3090rfwakeup(Ctlr *ctlr)
2187*43f728cbSDavid du Colombier {
2188*43f728cbSDavid du Colombier 	u32int tmp;
2189*43f728cbSDavid du Colombier 	u8int rf;
2190*43f728cbSDavid du Colombier 
2191*43f728cbSDavid du Colombier 	if(ctlr->mac_ver == 0x3593){
2192*43f728cbSDavid du Colombier 		/* enable VCO */
2193*43f728cbSDavid du Colombier 		rf = rt3090rfread(ctlr, 1);
2194*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 1, rf | Rt3593Vco);
2195*43f728cbSDavid du Colombier 
2196*43f728cbSDavid du Colombier 		/* initiate VCO calibration */
2197*43f728cbSDavid du Colombier 		rf = rt3090rfread(ctlr, 3);
2198*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 3, rf | Rt3593Vcocal);
2199*43f728cbSDavid du Colombier 
2200*43f728cbSDavid du Colombier 		/* enable VCO bias current control */
2201*43f728cbSDavid du Colombier 		rf = rt3090rfread(ctlr, 6);
2202*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 6, rf | Rt3593VcoIc);
2203*43f728cbSDavid du Colombier 
2204*43f728cbSDavid du Colombier 		/* initiate res calibration */
2205*43f728cbSDavid du Colombier 		rf = rt3090rfread(ctlr, 2);
2206*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 2, rf | Rt3593Rescal);
2207*43f728cbSDavid du Colombier 
2208*43f728cbSDavid du Colombier 		/* set reference current control to 0.33 mA */
2209*43f728cbSDavid du Colombier 		rf = rt3090rfread(ctlr, 22);
2210*43f728cbSDavid du Colombier 		rf &= ~Rt3593CpIcMask;
2211*43f728cbSDavid du Colombier 		rf |= 1 << Rt3593CpIcShift;
2212*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 22, rf);
2213*43f728cbSDavid du Colombier 
2214*43f728cbSDavid du Colombier 		/* enable RX CTB */
2215*43f728cbSDavid du Colombier 		rf = rt3090rfread(ctlr, 46);
2216*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 46, rf | Rt3593RxCtb);
2217*43f728cbSDavid du Colombier 
2218*43f728cbSDavid du Colombier 		rf = rt3090rfread(ctlr, 20);
2219*43f728cbSDavid du Colombier 		rf &= ~(Rt3593LdoRfVcMask | Rt3593LdoPllVcMask);
2220*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 20, rf);
2221*43f728cbSDavid du Colombier 	}else{
2222*43f728cbSDavid du Colombier 		/* enable RF block */
2223*43f728cbSDavid du Colombier 		rf = rt3090rfread(ctlr, 1);
2224*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 1, rf | Rt3070RfBlock);
2225*43f728cbSDavid du Colombier 
2226*43f728cbSDavid du Colombier 		/* enable VCO bias current control */
2227*43f728cbSDavid du Colombier 		rf = rt3090rfread(ctlr, 7);
2228*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 7, rf | 0x30);
2229*43f728cbSDavid du Colombier 
2230*43f728cbSDavid du Colombier 		rf = rt3090rfread(ctlr, 9);
2231*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 9, rf | 0x0e);
2232*43f728cbSDavid du Colombier 
2233*43f728cbSDavid du Colombier 		/* enable RX CTB */
2234*43f728cbSDavid du Colombier 		rf = rt3090rfread(ctlr, 21);
2235*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 21, rf | Rt3070RxCtb);
2236*43f728cbSDavid du Colombier 
2237*43f728cbSDavid du Colombier 		/* fix Tx to Rx IQ glitch by raising RF voltage */
2238*43f728cbSDavid du Colombier 		rf = rt3090rfread(ctlr, 27);
2239*43f728cbSDavid du Colombier 		rf &= ~0x77;
2240*43f728cbSDavid du Colombier 		if(ctlr->mac_rev < 0x0211)
2241*43f728cbSDavid du Colombier 			rf |= 0x03;
2242*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, 27, rf);
2243*43f728cbSDavid du Colombier 	}
2244*43f728cbSDavid du Colombier 	if(ctlr->patch_dac && ctlr->mac_rev < 0x0211){
2245*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, Rt3070LdoCfg0);
2246*43f728cbSDavid du Colombier 		tmp = (tmp & ~0x1f000000) | 0x0d000000;
2247*43f728cbSDavid du Colombier 		csr32w(ctlr, Rt3070LdoCfg0, tmp);
2248*43f728cbSDavid du Colombier 	}
2249*43f728cbSDavid du Colombier }
2250*43f728cbSDavid du Colombier 
2251*43f728cbSDavid du Colombier static void
rt3090rfsetup(Ctlr * ctlr)2252*43f728cbSDavid du Colombier rt3090rfsetup(Ctlr *ctlr)
2253*43f728cbSDavid du Colombier {
2254*43f728cbSDavid du Colombier 	u8int bbp;
2255*43f728cbSDavid du Colombier 	int i;
2256*43f728cbSDavid du Colombier 
2257*43f728cbSDavid du Colombier 	if(ctlr->mac_rev >= 0x0211){
2258*43f728cbSDavid du Colombier 		/* enable DC filter */
2259*43f728cbSDavid du Colombier 		bbpwrite(ctlr, 103, 0xc0);
2260*43f728cbSDavid du Colombier 
2261*43f728cbSDavid du Colombier 		/* improve power consumption */
2262*43f728cbSDavid du Colombier 		bbp = bbpread(ctlr, 31);
2263*43f728cbSDavid du Colombier 		bbpwrite(ctlr, 31, bbp & ~0x03);
2264*43f728cbSDavid du Colombier 	}
2265*43f728cbSDavid du Colombier 
2266*43f728cbSDavid du Colombier 	csr32w(ctlr, TxSwCfg1, 0);
2267*43f728cbSDavid du Colombier 	if(ctlr->mac_rev < 0x0211){
2268*43f728cbSDavid du Colombier 		csr32w(ctlr, TxSwCfg2,
2269*43f728cbSDavid du Colombier 		    ctlr->patch_dac ? 0x2c : 0x0f);
2270*43f728cbSDavid du Colombier 	}else
2271*43f728cbSDavid du Colombier 		csr32w(ctlr, TxSwCfg2, 0);
2272*43f728cbSDavid du Colombier 
2273*43f728cbSDavid du Colombier 	/* initialize RF registers from ROM */
2274*43f728cbSDavid du Colombier 	for(i = 0; i < 10; i++){
2275*43f728cbSDavid du Colombier 		if(ctlr->rf[i].reg == 0 || ctlr->rf[i].reg == 0xff)
2276*43f728cbSDavid du Colombier 			continue;
2277*43f728cbSDavid du Colombier 		rt3090rfwrite(ctlr, ctlr->rf[i].reg, ctlr->rf[i].val);
2278*43f728cbSDavid du Colombier 	}
2279*43f728cbSDavid du Colombier }
2280*43f728cbSDavid du Colombier 
2281*43f728cbSDavid du Colombier static void
updateprot(Ctlr * ctlr)2282*43f728cbSDavid du Colombier updateprot(Ctlr *ctlr)
2283*43f728cbSDavid du Colombier {
2284*43f728cbSDavid du Colombier 	u32int tmp;
2285*43f728cbSDavid du Colombier 
2286*43f728cbSDavid du Colombier 	tmp = RtsthEn | ProtNavShort | TxopAllowAll;
2287*43f728cbSDavid du Colombier 	/* setup protection frame rate (MCS code) */
2288*43f728cbSDavid du Colombier 	tmp |= /*(ic->ic_curmode == IEEE80211_MODE_11A) ?
2289*43f728cbSDavid du Colombier 	    rt2860_rates[RT2860_RIDX_OFDM6].mcs :*/
2290*43f728cbSDavid du Colombier 	    rt2860_rates[RidxCck11].mcs;
2291*43f728cbSDavid du Colombier 
2292*43f728cbSDavid du Colombier 	/* CCK frames don't require protection */
2293*43f728cbSDavid du Colombier 	csr32w(ctlr, CckProtCfg, tmp);
2294*43f728cbSDavid du Colombier /* XXX
2295*43f728cbSDavid du Colombier 	if(ic->ic_flags & IEEE80211_F_USEPROT){
2296*43f728cbSDavid du Colombier 		if(ic->ic_protmode == IEEE80211_PROT_RTSCTS)
2297*43f728cbSDavid du Colombier 			tmp |= ProtCtrlRtsCts;
2298*43f728cbSDavid du Colombier 		else if(ic->ic_protmode == IEEE80211_PROT_CTSONLY)
2299*43f728cbSDavid du Colombier 			tmp |= ProtCtrlCts;
2300*43f728cbSDavid du Colombier 	}
2301*43f728cbSDavid du Colombier 	csr32w(ctlr, OfdmProtCfg, tmp); */
2302*43f728cbSDavid du Colombier }
2303*43f728cbSDavid du Colombier 
2304*43f728cbSDavid du Colombier static char*
rt2860start(Ether * edev)2305*43f728cbSDavid du Colombier rt2860start(Ether *edev)
2306*43f728cbSDavid du Colombier {
2307*43f728cbSDavid du Colombier 	u32int tmp;
2308*43f728cbSDavid du Colombier 	u8int bbp1, bbp3;
2309*43f728cbSDavid du Colombier 	int i, qid, ridx, ntries;
2310*43f728cbSDavid du Colombier 	char *err;
2311*43f728cbSDavid du Colombier 	Ctlr *ctlr;
2312*43f728cbSDavid du Colombier 
2313*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
2314*43f728cbSDavid du Colombier 	csr32w(ctlr, PwrPinCfg, IoRaPe);
2315*43f728cbSDavid du Colombier 
2316*43f728cbSDavid du Colombier 	/* disable DMA */
2317*43f728cbSDavid du Colombier 	tmp = csr32r(ctlr, WpdmaGloCfg);
2318*43f728cbSDavid du Colombier 	tmp &= 0xff0;
2319*43f728cbSDavid du Colombier 	csr32w(ctlr, WpdmaGloCfg, tmp);
2320*43f728cbSDavid du Colombier 
2321*43f728cbSDavid du Colombier 	/* PBF hardware reset */
2322*43f728cbSDavid du Colombier 	csr32w(ctlr, SysCtrl, 0xe1f);
2323*43f728cbSDavid du Colombier 	coherence();
2324*43f728cbSDavid du Colombier 	csr32w(ctlr, SysCtrl, 0xe00);
2325*43f728cbSDavid du Colombier 
2326*43f728cbSDavid du Colombier 	if((err = boot(ctlr)) != nil){
2327*43f728cbSDavid du Colombier 		/*XXX: rt2860stop(ifp, 1);*/
2328*43f728cbSDavid du Colombier 		return err;
2329*43f728cbSDavid du Colombier 	}
2330*43f728cbSDavid du Colombier 	/* set MAC address */
2331*43f728cbSDavid du Colombier 	csr32w(ctlr, MacAddrDw0,
2332*43f728cbSDavid du Colombier 	    edev->ea[0] | edev->ea[1] << 8 | edev->ea[2] << 16 | edev->ea[3] << 24);
2333*43f728cbSDavid du Colombier 	csr32w(ctlr, MacAddrDw1,
2334*43f728cbSDavid du Colombier 	    edev->ea[4] | edev->ea[5] << 8 | 0xff << 16);
2335*43f728cbSDavid du Colombier 
2336*43f728cbSDavid du Colombier 	/* init Tx power for all Tx rates (from EEPROM) */
2337*43f728cbSDavid du Colombier 	for(ridx = 0; ridx < 5; ridx++){
2338*43f728cbSDavid du Colombier 		if(ctlr->txpow20mhz[ridx] == 0xffffffff)
2339*43f728cbSDavid du Colombier 			continue;
2340*43f728cbSDavid du Colombier 		csr32w(ctlr, TxPwrCfg(ridx), ctlr->txpow20mhz[ridx]);
2341*43f728cbSDavid du Colombier 	}
2342*43f728cbSDavid du Colombier 
2343*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 100; ntries++){
2344*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, WpdmaGloCfg);
2345*43f728cbSDavid du Colombier 		if((tmp & (TxDmaBusy | RxDmaBusy)) == 0)
2346*43f728cbSDavid du Colombier 			break;
2347*43f728cbSDavid du Colombier 		microdelay(1000);
2348*43f728cbSDavid du Colombier 	}
2349*43f728cbSDavid du Colombier 	if(ntries == 100){
2350*43f728cbSDavid du Colombier 		err = "timeout waiting for DMA engine";
2351*43f728cbSDavid du Colombier 		/*rt2860_stop(ifp, 1);*/
2352*43f728cbSDavid du Colombier 		return err;
2353*43f728cbSDavid du Colombier 	}
2354*43f728cbSDavid du Colombier 	tmp &= 0xff0;
2355*43f728cbSDavid du Colombier 	csr32w(ctlr, WpdmaGloCfg, tmp);
2356*43f728cbSDavid du Colombier 
2357*43f728cbSDavid du Colombier 	/* reset Rx ring and all 6 Tx rings */
2358*43f728cbSDavid du Colombier 	csr32w(ctlr, WpdmaRstIdx, 0x1003f);
2359*43f728cbSDavid du Colombier 
2360*43f728cbSDavid du Colombier 	/* PBF hardware reset */
2361*43f728cbSDavid du Colombier 	csr32w(ctlr, SysCtrl, 0xe1f);
2362*43f728cbSDavid du Colombier 	coherence();
2363*43f728cbSDavid du Colombier 	csr32w(ctlr, SysCtrl, 0xe00);
2364*43f728cbSDavid du Colombier 
2365*43f728cbSDavid du Colombier 	csr32w(ctlr, PwrPinCfg, IoRaPe | IoRfPe);
2366*43f728cbSDavid du Colombier 
2367*43f728cbSDavid du Colombier 	csr32w(ctlr, MacSysCtrl, BbpHrst | MacSrst);
2368*43f728cbSDavid du Colombier 	coherence();
2369*43f728cbSDavid du Colombier 	csr32w(ctlr, MacSysCtrl, 0);
2370*43f728cbSDavid du Colombier 
2371*43f728cbSDavid du Colombier 	for(i = 0; i < nelem(rt2860_def_mac); i++)
2372*43f728cbSDavid du Colombier 		csr32w(ctlr, rt2860_def_mac[i].reg, rt2860_def_mac[i].val);
2373*43f728cbSDavid du Colombier 	if(ctlr->mac_ver >= 0x3071){
2374*43f728cbSDavid du Colombier 		/* set delay of PA_PE assertion to 1us (unit of 0.25us) */
2375*43f728cbSDavid du Colombier 		csr32w(ctlr, TxSwCfg0,
2376*43f728cbSDavid du Colombier 		    4 << DlyPapeEnShift);
2377*43f728cbSDavid du Colombier 	}
2378*43f728cbSDavid du Colombier 
2379*43f728cbSDavid du Colombier 	if(!(csr32r(ctlr, PciCfg) & PciCfgPci)){
2380*43f728cbSDavid du Colombier 		ctlr->flags |= ConnPciE;
2381*43f728cbSDavid du Colombier 		/* PCIe has different clock cycle count than PCI */
2382*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, UsCycCnt);
2383*43f728cbSDavid du Colombier 		tmp = (tmp & ~0xff) | 0x7d;
2384*43f728cbSDavid du Colombier 		csr32w(ctlr, UsCycCnt, tmp);
2385*43f728cbSDavid du Colombier 	}
2386*43f728cbSDavid du Colombier 
2387*43f728cbSDavid du Colombier 	/* wait while MAC is busy */
2388*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 100; ntries++){
2389*43f728cbSDavid du Colombier 		if(!(csr32r(ctlr, MacStatusReg) &
2390*43f728cbSDavid du Colombier 		    (RxStatusBusy | TxStatusBusy)))
2391*43f728cbSDavid du Colombier 			break;
2392*43f728cbSDavid du Colombier 		microdelay(1000);
2393*43f728cbSDavid du Colombier 	}
2394*43f728cbSDavid du Colombier 	if(ntries == 100){
2395*43f728cbSDavid du Colombier 		err = "timeout waiting for MAC";
2396*43f728cbSDavid du Colombier 		/*rt2860_stop(ifp, 1);*/
2397*43f728cbSDavid du Colombier 		return err;
2398*43f728cbSDavid du Colombier 	}
2399*43f728cbSDavid du Colombier 
2400*43f728cbSDavid du Colombier 	/* clear Host to MCU mailbox */
2401*43f728cbSDavid du Colombier 	csr32w(ctlr, H2mBbpagent, 0);
2402*43f728cbSDavid du Colombier 	csr32w(ctlr, H2mMailbox, 0);
2403*43f728cbSDavid du Colombier 
2404*43f728cbSDavid du Colombier 	mcucmd(ctlr, McuCmdRfreset, 0, 0);
2405*43f728cbSDavid du Colombier 	microdelay(1000);
2406*43f728cbSDavid du Colombier 
2407*43f728cbSDavid du Colombier 	if((err = bbpinit(ctlr)) != nil){
2408*43f728cbSDavid du Colombier 		/*rt2860_stop(ifp, 1);*/
2409*43f728cbSDavid du Colombier 		return err;
2410*43f728cbSDavid du Colombier 	}
2411*43f728cbSDavid du Colombier 	/* clear RX WCID search table */
2412*43f728cbSDavid du Colombier 	setregion(ctlr, WcidEntry(0), 0, 512);
2413*43f728cbSDavid du Colombier 	/* clear pairwise key table */
2414*43f728cbSDavid du Colombier 	setregion(ctlr, Pkey(0), 0, 2048);
2415*43f728cbSDavid du Colombier 	/* clear IV/EIV table */
2416*43f728cbSDavid du Colombier 	setregion(ctlr, Iveiv(0), 0, 512);
2417*43f728cbSDavid du Colombier 	/* clear WCID attribute table */
2418*43f728cbSDavid du Colombier 	setregion(ctlr, WcidAttr(0), 0, 256);
2419*43f728cbSDavid du Colombier 	/* clear shared key table */
2420*43f728cbSDavid du Colombier 	setregion(ctlr, Skey(0, 0), 0, 8 * 32);
2421*43f728cbSDavid du Colombier 	/* clear shared key mode */
2422*43f728cbSDavid du Colombier 	setregion(ctlr, SkeyMode07, 0, 4);
2423*43f728cbSDavid du Colombier 
2424*43f728cbSDavid du Colombier 	/* init Tx rings (4 EDCAs + HCCA + Mgt) */
2425*43f728cbSDavid du Colombier 	for(qid = 0; qid < 6; qid++){
2426*43f728cbSDavid du Colombier 		csr32w(ctlr, TxBasePtr(qid), PCIWADDR(ctlr->tx[qid].d));
2427*43f728cbSDavid du Colombier 		csr32w(ctlr, TxMaxCnt(qid), Ntx);
2428*43f728cbSDavid du Colombier 		csr32w(ctlr, TxCtxIdx(qid), 0);
2429*43f728cbSDavid du Colombier 	}
2430*43f728cbSDavid du Colombier 
2431*43f728cbSDavid du Colombier 	/* init Rx ring */
2432*43f728cbSDavid du Colombier 	csr32w(ctlr, RxBasePtr, PCIWADDR(ctlr->rx.p));
2433*43f728cbSDavid du Colombier 	csr32w(ctlr, RxMaxCnt, Nrx);
2434*43f728cbSDavid du Colombier 	csr32w(ctlr, RxCalcIdx, Nrx - 1);
2435*43f728cbSDavid du Colombier 
2436*43f728cbSDavid du Colombier 	/* setup maximum buffer sizes */
2437*43f728cbSDavid du Colombier 	csr32w(ctlr, MaxLenCfg, 1 << 12 |
2438*43f728cbSDavid du Colombier 	    (Rbufsize - Rxwisize - 2));
2439*43f728cbSDavid du Colombier 
2440*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 100; ntries++){
2441*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, WpdmaGloCfg);
2442*43f728cbSDavid du Colombier 		if((tmp & (TxDmaBusy | RxDmaBusy)) == 0)
2443*43f728cbSDavid du Colombier 			break;
2444*43f728cbSDavid du Colombier 		microdelay(1000);
2445*43f728cbSDavid du Colombier 	}
2446*43f728cbSDavid du Colombier 	if(ntries == 100){
2447*43f728cbSDavid du Colombier 		err = "timeout waiting for DMA engine";
2448*43f728cbSDavid du Colombier 		/*rt2860_stop(ifp, 1);*/
2449*43f728cbSDavid du Colombier 		return err;
2450*43f728cbSDavid du Colombier 	}
2451*43f728cbSDavid du Colombier 	tmp &= 0xff0;
2452*43f728cbSDavid du Colombier 	csr32w(ctlr, WpdmaGloCfg, tmp);
2453*43f728cbSDavid du Colombier 
2454*43f728cbSDavid du Colombier 	/* disable interrupts mitigation */
2455*43f728cbSDavid du Colombier 	csr32w(ctlr, DelayIntCfg, 0);
2456*43f728cbSDavid du Colombier 
2457*43f728cbSDavid du Colombier 	/* write vendor-specific BBP values (from EEPROM) */
2458*43f728cbSDavid du Colombier 	for(i = 0; i < 8; i++){
2459*43f728cbSDavid du Colombier 		if(ctlr->bbp[i].reg == 0 || ctlr->bbp[i].reg == 0xff)
2460*43f728cbSDavid du Colombier 			continue;
2461*43f728cbSDavid du Colombier 		bbpwrite(ctlr, ctlr->bbp[i].reg, ctlr->bbp[i].val);
2462*43f728cbSDavid du Colombier 	}
2463*43f728cbSDavid du Colombier 
2464*43f728cbSDavid du Colombier 	/* select Main antenna for 1T1R devices */
2465*43f728cbSDavid du Colombier 	if(ctlr->rf_rev == Rf2020 ||
2466*43f728cbSDavid du Colombier 	    ctlr->rf_rev == Rf3020 ||
2467*43f728cbSDavid du Colombier 	    ctlr->rf_rev == Rf3320)
2468*43f728cbSDavid du Colombier 		rt3090setrxantenna(ctlr, 0);
2469*43f728cbSDavid du Colombier 
2470*43f728cbSDavid du Colombier 	/* send LEDs operating mode to microcontroller */
2471*43f728cbSDavid du Colombier 	mcucmd(ctlr, McuCmdLed1, ctlr->led[0], 0);
2472*43f728cbSDavid du Colombier 	mcucmd(ctlr, McuCmdLed2, ctlr->led[1], 0);
2473*43f728cbSDavid du Colombier 	mcucmd(ctlr, McuCmdLed3, ctlr->led[2], 0);
2474*43f728cbSDavid du Colombier 
2475*43f728cbSDavid du Colombier 	if(ctlr->mac_ver >= 0x3071)
2476*43f728cbSDavid du Colombier 		rt3090rfinit(ctlr);
2477*43f728cbSDavid du Colombier 
2478*43f728cbSDavid du Colombier 	mcucmd(ctlr, McuCmdSleep, 0x02ff, 1);
2479*43f728cbSDavid du Colombier 	mcucmd(ctlr, McuCmdWakeup, 0, 1);
2480*43f728cbSDavid du Colombier 
2481*43f728cbSDavid du Colombier 	if(ctlr->mac_ver >= 0x3071)
2482*43f728cbSDavid du Colombier 		rt3090rfwakeup(ctlr);
2483*43f728cbSDavid du Colombier 
2484*43f728cbSDavid du Colombier 	/* disable non-existing Rx chains */
2485*43f728cbSDavid du Colombier 	bbp3 = bbpread(ctlr, 3);
2486*43f728cbSDavid du Colombier 	bbp3 &= ~(1 << 3 | 1 << 4);
2487*43f728cbSDavid du Colombier 	if(ctlr->nrxchains == 2)
2488*43f728cbSDavid du Colombier 		bbp3 |= 1 << 3;
2489*43f728cbSDavid du Colombier 	else if(ctlr->nrxchains == 3)
2490*43f728cbSDavid du Colombier 		bbp3 |= 1 << 4;
2491*43f728cbSDavid du Colombier 	bbpwrite(ctlr, 3, bbp3);
2492*43f728cbSDavid du Colombier 
2493*43f728cbSDavid du Colombier 	/* disable non-existing Tx chains */
2494*43f728cbSDavid du Colombier 	bbp1 = bbpread(ctlr, 1);
2495*43f728cbSDavid du Colombier 	if(ctlr->ntxchains == 1)
2496*43f728cbSDavid du Colombier 		bbp1 = (bbp1 & ~(1 << 3 | 1 << 4));
2497*43f728cbSDavid du Colombier 	else if(ctlr->mac_ver == 0x3593 && ctlr->ntxchains == 2)
2498*43f728cbSDavid du Colombier 		bbp1 = (bbp1 & ~(1 << 4)) | 1 << 3;
2499*43f728cbSDavid du Colombier 	else if(ctlr->mac_ver == 0x3593 && ctlr->ntxchains == 3)
2500*43f728cbSDavid du Colombier 		bbp1 = (bbp1 & ~(1 << 3)) | 1 << 4;
2501*43f728cbSDavid du Colombier 	bbpwrite(ctlr, 1, bbp1);
2502*43f728cbSDavid du Colombier 
2503*43f728cbSDavid du Colombier 	if(ctlr->mac_ver >= 0x3071)
2504*43f728cbSDavid du Colombier 		rt3090rfsetup(ctlr);
2505*43f728cbSDavid du Colombier 
2506*43f728cbSDavid du Colombier 	/* select default channel */
2507*43f728cbSDavid du Colombier 	if(ctlr->mac_ver >= 0x3071)
2508*43f728cbSDavid du Colombier 		rt3090setchan(ctlr, 3);
2509*43f728cbSDavid du Colombier 	else
2510*43f728cbSDavid du Colombier 		setchan(ctlr, 3);
2511*43f728cbSDavid du Colombier 
2512*43f728cbSDavid du Colombier 	/* reset RF from MCU */
2513*43f728cbSDavid du Colombier 	mcucmd(ctlr, McuCmdRfreset, 0, 0);
2514*43f728cbSDavid du Colombier 
2515*43f728cbSDavid du Colombier 	/* set RTS threshold */
2516*43f728cbSDavid du Colombier 	tmp = csr32r(ctlr, TxRtsCfg);
2517*43f728cbSDavid du Colombier 	tmp &= ~0xffff00;
2518*43f728cbSDavid du Colombier 	tmp |= 1 /* ic->ic_rtsthreshold */ << 8;
2519*43f728cbSDavid du Colombier 	csr32w(ctlr, TxRtsCfg, tmp);
2520*43f728cbSDavid du Colombier 
2521*43f728cbSDavid du Colombier 	/* setup initial protection mode */
2522*43f728cbSDavid du Colombier 	updateprot(ctlr);
2523*43f728cbSDavid du Colombier 
2524*43f728cbSDavid du Colombier 	/* turn radio LED on */
2525*43f728cbSDavid du Colombier 	setleds(ctlr, LedRadio);
2526*43f728cbSDavid du Colombier 
2527*43f728cbSDavid du Colombier 	/* enable Tx/Rx DMA engine */
2528*43f728cbSDavid du Colombier 	if((err = txrxon(ctlr)) != 0){
2529*43f728cbSDavid du Colombier 		/*rt2860_stop(ifp, 1);*/
2530*43f728cbSDavid du Colombier 		return err;
2531*43f728cbSDavid du Colombier 	}
2532*43f728cbSDavid du Colombier 
2533*43f728cbSDavid du Colombier 	/* clear pending interrupts */
2534*43f728cbSDavid du Colombier 	csr32w(ctlr, IntStatus, 0xffffffff);
2535*43f728cbSDavid du Colombier 	/* enable interrupts */
2536*43f728cbSDavid du Colombier 	csr32w(ctlr, IntMask, 0x3fffc);
2537*43f728cbSDavid du Colombier 
2538*43f728cbSDavid du Colombier 	if(ctlr->flags & AdvancedPs)
2539*43f728cbSDavid du Colombier 		mcucmd(ctlr, McuCmdPslevel, ctlr->pslevel, 0);
2540*43f728cbSDavid du Colombier 	return nil;
2541*43f728cbSDavid du Colombier }
2542*43f728cbSDavid du Colombier 
2543*43f728cbSDavid du Colombier /*
2544*43f728cbSDavid du Colombier  * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word.
2545*43f728cbSDavid du Colombier  * Used to adjust per-rate Tx power registers.
2546*43f728cbSDavid du Colombier  */
2547*43f728cbSDavid du Colombier static u32int
b4inc(u32int b32,s8int delta)2548*43f728cbSDavid du Colombier b4inc(u32int b32, s8int delta)
2549*43f728cbSDavid du Colombier {
2550*43f728cbSDavid du Colombier 	s8int i, b4;
2551*43f728cbSDavid du Colombier 
2552*43f728cbSDavid du Colombier 	for(i = 0; i < 8; i++){
2553*43f728cbSDavid du Colombier 		b4 = b32 & 0xf;
2554*43f728cbSDavid du Colombier 		b4 += delta;
2555*43f728cbSDavid du Colombier 		if(b4 < 0)
2556*43f728cbSDavid du Colombier 			b4 = 0;
2557*43f728cbSDavid du Colombier 		else if(b4 > 0xf)
2558*43f728cbSDavid du Colombier 			b4 = 0xf;
2559*43f728cbSDavid du Colombier 		b32 = b32 >> 4 | b4 << 28;
2560*43f728cbSDavid du Colombier 	}
2561*43f728cbSDavid du Colombier 	return b32;
2562*43f728cbSDavid du Colombier }
2563*43f728cbSDavid du Colombier 
2564*43f728cbSDavid du Colombier static void
transmit(Wifi * wifi,Wnode * wn,Block * b)2565*43f728cbSDavid du Colombier transmit(Wifi *wifi, Wnode *wn, Block *b)
2566*43f728cbSDavid du Colombier {
2567*43f728cbSDavid du Colombier 	Ether *edev;
2568*43f728cbSDavid du Colombier 	Ctlr *ctlr;
2569*43f728cbSDavid du Colombier 	Wifipkt *w;
2570*43f728cbSDavid du Colombier 	u8int mcs, qid;
2571*43f728cbSDavid du Colombier 	int ridx, /*ctl_ridx,*/ hdrlen;
2572*43f728cbSDavid du Colombier 	uchar *p;
2573*43f728cbSDavid du Colombier 	int nodeid;
2574*43f728cbSDavid du Colombier 	Block *outb;
2575*43f728cbSDavid du Colombier 	TXQ *tx;
2576*43f728cbSDavid du Colombier 	Pool *pool;
2577*43f728cbSDavid du Colombier 
2578*43f728cbSDavid du Colombier 	edev = wifi->ether;
2579*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
2580*43f728cbSDavid du Colombier 
2581*43f728cbSDavid du Colombier 	qlock(ctlr);
2582*43f728cbSDavid du Colombier 	if(ctlr->attached == 0 || ctlr->broken){
2583*43f728cbSDavid du Colombier 		qunlock(ctlr);
2584*43f728cbSDavid du Colombier 		freeb(b);
2585*43f728cbSDavid du Colombier 		return;
2586*43f728cbSDavid du Colombier 	}
2587*43f728cbSDavid du Colombier 	if((wn->channel != ctlr->channel)
2588*43f728cbSDavid du Colombier 	|| (!ctlr->prom && (wn->aid != ctlr->aid || memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0)))
2589*43f728cbSDavid du Colombier 		rxon(edev, wn);
2590*43f728cbSDavid du Colombier 
2591*43f728cbSDavid du Colombier 	if(b == nil){
2592*43f728cbSDavid du Colombier 		/* association note has no data to transmit */
2593*43f728cbSDavid du Colombier 		qunlock(ctlr);
2594*43f728cbSDavid du Colombier 		return;
2595*43f728cbSDavid du Colombier 	}
2596*43f728cbSDavid du Colombier 
2597*43f728cbSDavid du Colombier 	pool = &ctlr->pool;
2598*43f728cbSDavid du Colombier 	qid = 0; /* for now */
2599*43f728cbSDavid du Colombier 	ridx = 0;
2600*43f728cbSDavid du Colombier 	tx = &ctlr->tx[qid];
2601*43f728cbSDavid du Colombier 
2602*43f728cbSDavid du Colombier 	nodeid = ctlr->bcastnodeid;
2603*43f728cbSDavid du Colombier 	w = (Wifipkt*)b->rp;
2604*43f728cbSDavid du Colombier 	hdrlen = wifihdrlen(w);
2605*43f728cbSDavid du Colombier 
2606*43f728cbSDavid du Colombier 	p = pool->p + pool->i * TxwiDmaSz;
2607*43f728cbSDavid du Colombier 	if((w->a1[0] & 1) == 0){
2608*43f728cbSDavid du Colombier 		*(p+4) = TxAck; /* xflags */
2609*43f728cbSDavid du Colombier 
2610*43f728cbSDavid du Colombier 		if(BLEN(b) > 512-4)
2611*43f728cbSDavid du Colombier 			*(p+1) = TxTxopBackoff; /* txop */
2612*43f728cbSDavid du Colombier 
2613*43f728cbSDavid du Colombier 		if((w->fc[0] & 0x0c) == 0x08 &&	ctlr->bssnodeid != -1){
2614*43f728cbSDavid du Colombier 			nodeid = ctlr->bssnodeid;
2615*43f728cbSDavid du Colombier 			ridx = 2; /* BUG: hardcode 11Mbit */
2616*43f728cbSDavid du Colombier 		}
2617*43f728cbSDavid du Colombier 	}
2618*43f728cbSDavid du Colombier 
2619*43f728cbSDavid du Colombier 	/*ctl_ridx = rt2860_rates[ridx].ctl_ridx;*/
2620*43f728cbSDavid du Colombier 	mcs = rt2860_rates[ridx].mcs;
2621*43f728cbSDavid du Colombier 
2622*43f728cbSDavid du Colombier 	/* setup TX Wireless Information */
2623*43f728cbSDavid du Colombier 	*p = 0; /* flags */
2624*43f728cbSDavid du Colombier 	*(p+2) = PhyCck | mcs; /* phy */
2625*43f728cbSDavid du Colombier 	/* let HW generate seq numbers */
2626*43f728cbSDavid du Colombier 	*(p+4) |= TxNseq; /* xflags */
2627*43f728cbSDavid du Colombier 	put16(p + 6, BLEN(b) | (((mcs+1) & 0xf) << TxPidShift) ); /* length */
2628*43f728cbSDavid du Colombier 
2629*43f728cbSDavid du Colombier /*	put16((uchar*)&w->dur[0], rt2860_rates[ctl_ridx].lp_ack_dur); */
2630*43f728cbSDavid du Colombier 
2631*43f728cbSDavid du Colombier 	*(p+5) = nodeid; /* wcid */
2632*43f728cbSDavid du Colombier 
2633*43f728cbSDavid du Colombier  	/* copy packet header */
2634*43f728cbSDavid du Colombier 	memmove(p + Txwisize, b->rp, hdrlen);
2635*43f728cbSDavid du Colombier 
2636*43f728cbSDavid du Colombier 	/* setup tx descriptor */
2637*43f728cbSDavid du Colombier 	/* first segment is TXWI + 802.11 header */
2638*43f728cbSDavid du Colombier 	p = (uchar*)tx->d + Tdscsize * tx->i;
2639*43f728cbSDavid du Colombier 	put32(p, PCIWADDR(pool->p + pool->i * TxwiDmaSz)); /* sdp0 */
2640*43f728cbSDavid du Colombier 	put16(p + 6, Txwisize + hdrlen); /* sdl0 */
2641*43f728cbSDavid du Colombier 	*(p + 15) = TxQselEdca; /* flags */
2642*43f728cbSDavid du Colombier 
2643*43f728cbSDavid du Colombier 	/* allocate output buffer */
2644*43f728cbSDavid du Colombier 	b->rp += hdrlen;
2645*43f728cbSDavid du Colombier 	tx->b[tx->i] = outb = iallocb(BLEN(b) + 256);
2646*43f728cbSDavid du Colombier 	if(outb == nil){
2647*43f728cbSDavid du Colombier 		print("outb = nil\n");
2648*43f728cbSDavid du Colombier 		return;
2649*43f728cbSDavid du Colombier 	}
2650*43f728cbSDavid du Colombier 	outb->rp = (uchar*)ROUND((uintptr)outb->base, 256);
2651*43f728cbSDavid du Colombier 	memset(outb->rp, 0, BLEN(b));
2652*43f728cbSDavid du Colombier 	memmove(outb->rp, b->rp, BLEN(b));
2653*43f728cbSDavid du Colombier 	outb->wp = outb->rp + BLEN(b);
2654*43f728cbSDavid du Colombier 	freeb(b);
2655*43f728cbSDavid du Colombier 
2656*43f728cbSDavid du Colombier 	/* setup payload segments */
2657*43f728cbSDavid du Colombier 	put32(p + 8, PCIWADDR(outb->rp)); /* sdp1 */
2658*43f728cbSDavid du Colombier 	put16(p + 4, BLEN(outb) | TxLs1); /* sdl1 */
2659*43f728cbSDavid du Colombier 
2660*43f728cbSDavid du Colombier 	p = pool->p + pool->i * TxwiDmaSz;
2661*43f728cbSDavid du Colombier 	w = (Wifipkt*)(p + Txwisize);
2662*43f728cbSDavid du Colombier 	if(ctlr->wifi->debug){
2663*43f728cbSDavid du Colombier 		print("transmit: %E->%E,%E nodeid=%x txq[%d]=%d size=%ld\n", w->a2, w->a1, w->a3, nodeid, qid, ctlr->tx[qid].i, BLEN(outb));
2664*43f728cbSDavid du Colombier 	}
2665*43f728cbSDavid du Colombier 
2666*43f728cbSDavid du Colombier 	tx->i = (tx->i + 1) % Ntx;
2667*43f728cbSDavid du Colombier 	pool->i = (pool->i + 1) % Ntxpool;
2668*43f728cbSDavid du Colombier 
2669*43f728cbSDavid du Colombier 	coherence();
2670*43f728cbSDavid du Colombier 
2671*43f728cbSDavid du Colombier 	/* kick Tx */
2672*43f728cbSDavid du Colombier 	csr32w(ctlr, TxCtxIdx(qid), ctlr->tx[qid].i);
2673*43f728cbSDavid du Colombier 
2674*43f728cbSDavid du Colombier 	qunlock(ctlr);
2675*43f728cbSDavid du Colombier 	return;
2676*43f728cbSDavid du Colombier }
2677*43f728cbSDavid du Colombier 
2678*43f728cbSDavid du Colombier static void
rt2860attach(Ether * edev)2679*43f728cbSDavid du Colombier rt2860attach(Ether *edev)
2680*43f728cbSDavid du Colombier {
2681*43f728cbSDavid du Colombier 	FWImage *fw;
2682*43f728cbSDavid du Colombier 	Ctlr *ctlr;
2683*43f728cbSDavid du Colombier 	char *err;
2684*43f728cbSDavid du Colombier 
2685*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
2686*43f728cbSDavid du Colombier 	qlock(ctlr);
2687*43f728cbSDavid du Colombier 	if(waserror()){
2688*43f728cbSDavid du Colombier 		print("#l%d: %s\n", edev->ctlrno, up->errstr);
2689*43f728cbSDavid du Colombier 		/*if(ctlr->power)
2690*43f728cbSDavid du Colombier 			poweroff(ctlr);*/
2691*43f728cbSDavid du Colombier 		qunlock(ctlr);
2692*43f728cbSDavid du Colombier 		nexterror();
2693*43f728cbSDavid du Colombier 	}
2694*43f728cbSDavid du Colombier 	if(ctlr->attached == 0){
2695*43f728cbSDavid du Colombier 		if(ctlr->wifi == nil)
2696*43f728cbSDavid du Colombier 			ctlr->wifi = wifiattach(edev, transmit);
2697*43f728cbSDavid du Colombier 
2698*43f728cbSDavid du Colombier 		if(ctlr->fw == nil){
2699*43f728cbSDavid du Colombier 			fw = readfirmware();
2700*43f728cbSDavid du Colombier 			ctlr->fw = fw;
2701*43f728cbSDavid du Colombier 		}
2702*43f728cbSDavid du Colombier 		if((err = rt2860start(edev)) != nil){
2703*43f728cbSDavid du Colombier 			error(err);
2704*43f728cbSDavid du Colombier 		}
2705*43f728cbSDavid du Colombier 
2706*43f728cbSDavid du Colombier 		ctlr->bcastnodeid = -1;
2707*43f728cbSDavid du Colombier 		ctlr->bssnodeid = -1;
2708*43f728cbSDavid du Colombier 		ctlr->channel = 1;
2709*43f728cbSDavid du Colombier 		ctlr->aid = 0;
2710*43f728cbSDavid du Colombier 
2711*43f728cbSDavid du Colombier 		setoptions(edev);
2712*43f728cbSDavid du Colombier 
2713*43f728cbSDavid du Colombier 		ctlr->attached = 1;
2714*43f728cbSDavid du Colombier 	}
2715*43f728cbSDavid du Colombier 	qunlock(ctlr);
2716*43f728cbSDavid du Colombier 	poperror();
2717*43f728cbSDavid du Colombier }
2718*43f728cbSDavid du Colombier 
2719*43f728cbSDavid du Colombier static void
receive(Ctlr * ctlr)2720*43f728cbSDavid du Colombier receive(Ctlr *ctlr)
2721*43f728cbSDavid du Colombier {
2722*43f728cbSDavid du Colombier 	u32int hw;
2723*43f728cbSDavid du Colombier 	RXQ *rx;
2724*43f728cbSDavid du Colombier 	Block *b;
2725*43f728cbSDavid du Colombier 	uchar *d;
2726*43f728cbSDavid du Colombier 
2727*43f728cbSDavid du Colombier 	rx = &ctlr->rx;
2728*43f728cbSDavid du Colombier 	if(rx->b == nil){
2729*43f728cbSDavid du Colombier 		print("rx->b == nil!");
2730*43f728cbSDavid du Colombier 		return;
2731*43f728cbSDavid du Colombier 	}
2732*43f728cbSDavid du Colombier 	hw = csr32r(ctlr, FsDrxIdx) & 0xfff;
2733*43f728cbSDavid du Colombier 	while(rx->i != hw){
2734*43f728cbSDavid du Colombier 		u16int sdl0, len;
2735*43f728cbSDavid du Colombier 		u32int flags;
2736*43f728cbSDavid du Colombier 		uchar *p;
2737*43f728cbSDavid du Colombier 		Wifipkt *w;
2738*43f728cbSDavid du Colombier 		int hdrlen;
2739*43f728cbSDavid du Colombier 
2740*43f728cbSDavid du Colombier 		p = (uchar*)rx->p + Rdscsize * rx->i;
2741*43f728cbSDavid du Colombier 		sdl0 = get16(p + 4 /* sdp0 */ + 2 /* sdl1 */);
2742*43f728cbSDavid du Colombier 		if(!(sdl0 & RxDdone)){
2743*43f728cbSDavid du Colombier 			print("rxd ddone bit not set\n");
2744*43f728cbSDavid du Colombier 			break; /* should not happen */
2745*43f728cbSDavid du Colombier 		}
2746*43f728cbSDavid du Colombier 		flags = get32(p + 12);
2747*43f728cbSDavid du Colombier 		if(flags & (RxCrcerr | RxIcverr)){
2748*43f728cbSDavid du Colombier 		/*	print("crc | icv err\n"); */
2749*43f728cbSDavid du Colombier 			goto skip;
2750*43f728cbSDavid du Colombier 		}
2751*43f728cbSDavid du Colombier 
2752*43f728cbSDavid du Colombier 		b = rx->b[rx->i];
2753*43f728cbSDavid du Colombier 		if(b == nil){
2754*43f728cbSDavid du Colombier 			print("no buf\n");
2755*43f728cbSDavid du Colombier 			goto skip;
2756*43f728cbSDavid du Colombier 		}
2757*43f728cbSDavid du Colombier 		d = b->rp;
2758*43f728cbSDavid du Colombier 		if(ctlr->wifi == nil)
2759*43f728cbSDavid du Colombier 			goto skip;
2760*43f728cbSDavid du Colombier 		if(rbplant(ctlr, rx->i) < 0){
2761*43f728cbSDavid du Colombier 			print("can't plant");
2762*43f728cbSDavid du Colombier 			goto skip;
2763*43f728cbSDavid du Colombier 		}
2764*43f728cbSDavid du Colombier 		ctlr->wcid = *b->rp;
2765*43f728cbSDavid du Colombier 		len = get16(b->rp + 2 /* wcid, keyidx */) & 0xfff;
2766*43f728cbSDavid du Colombier 		b->rp = d + Rxwisize;
2767*43f728cbSDavid du Colombier 		b->wp = b->rp + len;
2768*43f728cbSDavid du Colombier 		w = (Wifipkt*)b->rp;
2769*43f728cbSDavid du Colombier 		hdrlen = wifihdrlen(w);
2770*43f728cbSDavid du Colombier 		/* HW may insert 2 padding bytes after 802.11 header */
2771*43f728cbSDavid du Colombier 		if(flags & RxL2pad){
2772*43f728cbSDavid du Colombier 			memmove(b->rp + 2, b->rp, hdrlen);
2773*43f728cbSDavid du Colombier 			b->rp += 2;
2774*43f728cbSDavid du Colombier 		}
2775*43f728cbSDavid du Colombier 		w = (Wifipkt*)b->rp;
2776*43f728cbSDavid du Colombier 		if(ctlr->wifi->debug)
2777*43f728cbSDavid du Colombier 			print("receive: %E->%E,%E wcid 0x%x \n", w->a2, w->a1, w->a3, ctlr->wcid);
2778*43f728cbSDavid du Colombier 		wifiiq(ctlr->wifi, b);
2779*43f728cbSDavid du Colombier skip:
2780*43f728cbSDavid du Colombier 		put16(p + 4 /* sdp0 */ + 2 /* sdl1 */, sdl0 & ~RxDdone);
2781*43f728cbSDavid du Colombier 		rx->i = (rx->i + 1) % Nrx;
2782*43f728cbSDavid du Colombier 	}
2783*43f728cbSDavid du Colombier 	coherence();
2784*43f728cbSDavid du Colombier 	/* tell HW what we have processed */
2785*43f728cbSDavid du Colombier 	csr32w(ctlr, RxCalcIdx, (rx->i - 1) % Nrx);
2786*43f728cbSDavid du Colombier }
2787*43f728cbSDavid du Colombier 
2788*43f728cbSDavid du Colombier static void
stats(Ctlr * ctlr)2789*43f728cbSDavid du Colombier stats(Ctlr *ctlr)
2790*43f728cbSDavid du Colombier {
2791*43f728cbSDavid du Colombier 	u32int stat;
2792*43f728cbSDavid du Colombier 	u8int wcid;
2793*43f728cbSDavid du Colombier 
2794*43f728cbSDavid du Colombier 	while((stat = csr32r(ctlr, TxStatFifo)) & TxqVld){
2795*43f728cbSDavid du Colombier 		wcid = (stat >> TxqWcidShift) & 0xff;
2796*43f728cbSDavid du Colombier 		/* if no ACK was requested, no feedback is available */
2797*43f728cbSDavid du Colombier 		if(!(stat & TxqAckreq) || wcid == 0xff){
2798*43f728cbSDavid du Colombier 			continue;
2799*43f728cbSDavid du Colombier 		}
2800*43f728cbSDavid du Colombier 	}
2801*43f728cbSDavid du Colombier }
2802*43f728cbSDavid du Colombier 
2803*43f728cbSDavid du Colombier static void
rt2860tx(Ctlr * ctlr,u8int q)2804*43f728cbSDavid du Colombier rt2860tx(Ctlr *ctlr, u8int q)
2805*43f728cbSDavid du Colombier {
2806*43f728cbSDavid du Colombier 	u32int hw;
2807*43f728cbSDavid du Colombier 	TXQ *tx;
2808*43f728cbSDavid du Colombier 
2809*43f728cbSDavid du Colombier 	stats(ctlr);
2810*43f728cbSDavid du Colombier 	tx = &ctlr->tx[q];
2811*43f728cbSDavid du Colombier 	hw = csr32r(ctlr, TxDtxIdx(q));
2812*43f728cbSDavid du Colombier 	while(tx->n != hw){
2813*43f728cbSDavid du Colombier 		uchar *p = (uchar*)tx->d + Rdscsize * tx->n;
2814*43f728cbSDavid du Colombier 		u16int sdl0;
2815*43f728cbSDavid du Colombier 
2816*43f728cbSDavid du Colombier 		if(tx->b[tx->n]){
2817*43f728cbSDavid du Colombier 			freeb(tx->b[tx->n]);
2818*43f728cbSDavid du Colombier 			tx->b[tx->n] = nil;
2819*43f728cbSDavid du Colombier 		}
2820*43f728cbSDavid du Colombier 		sdl0 = get16(p + 4 /* sdp0 */ + 2 /* sdl1 */);
2821*43f728cbSDavid du Colombier 		if(!(sdl0 & TxDdone)){
2822*43f728cbSDavid du Colombier 			print("txd ddone bit not set\n");
2823*43f728cbSDavid du Colombier 			break; /* should not happen */
2824*43f728cbSDavid du Colombier 		}
2825*43f728cbSDavid du Colombier 		memset((uchar*)ctlr->pool.p + TxwiDmaSz * tx->n, 0, TxwiDmaSz);
2826*43f728cbSDavid du Colombier 		memset((uchar*)tx->d + Tdscsize * tx->n, 0, Tdscsize);
2827*43f728cbSDavid du Colombier 		// put16(p + 4 /* sdp0 */ + 2 /* sdl1 */, sdl0 & ~TxDdone);
2828*43f728cbSDavid du Colombier 		tx->n = (tx->n + 1) % Ntx;
2829*43f728cbSDavid du Colombier 	}
2830*43f728cbSDavid du Colombier 	coherence();
2831*43f728cbSDavid du Colombier }
2832*43f728cbSDavid du Colombier 
2833*43f728cbSDavid du Colombier static void
rt2860interrupt(Ureg *,void * arg)2834*43f728cbSDavid du Colombier rt2860interrupt(Ureg*, void *arg)
2835*43f728cbSDavid du Colombier {
2836*43f728cbSDavid du Colombier 	u32int r;
2837*43f728cbSDavid du Colombier 	Ether *edev;
2838*43f728cbSDavid du Colombier 	Ctlr *ctlr;
2839*43f728cbSDavid du Colombier 	int debug;
2840*43f728cbSDavid du Colombier 
2841*43f728cbSDavid du Colombier 	edev = arg;
2842*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
2843*43f728cbSDavid du Colombier 	ilock(ctlr);
2844*43f728cbSDavid du Colombier 
2845*43f728cbSDavid du Colombier 	debug = ctlr->wifi->debug;
2846*43f728cbSDavid du Colombier 
2847*43f728cbSDavid du Colombier 	r = csr32r(ctlr, IntStatus);
2848*43f728cbSDavid du Colombier 	if(r == 0xffffffff){
2849*43f728cbSDavid du Colombier 		iunlock(ctlr);
2850*43f728cbSDavid du Colombier 		return;
2851*43f728cbSDavid du Colombier 	}
2852*43f728cbSDavid du Colombier 	if(r == 0){
2853*43f728cbSDavid du Colombier 		iunlock(ctlr);
2854*43f728cbSDavid du Colombier 		return;
2855*43f728cbSDavid du Colombier 	}
2856*43f728cbSDavid du Colombier 
2857*43f728cbSDavid du Colombier 	/* acknowledge interrupts */
2858*43f728cbSDavid du Colombier 	csr32w(ctlr, IntStatus, r);
2859*43f728cbSDavid du Colombier 
2860*43f728cbSDavid du Colombier 	if(r & TxRxCoherent){
2861*43f728cbSDavid du Colombier 		u32int tmp;
2862*43f728cbSDavid du Colombier 		/* DMA finds data coherent event when checking the DDONE bit */
2863*43f728cbSDavid du Colombier 		if(debug)
2864*43f728cbSDavid du Colombier 			print("txrx coherent intr\n");
2865*43f728cbSDavid du Colombier 
2866*43f728cbSDavid du Colombier 		/* restart DMA engine */
2867*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, WpdmaGloCfg);
2868*43f728cbSDavid du Colombier 		tmp &= ~(TxWbDdone | RxDmaEn | TxDmaEn);
2869*43f728cbSDavid du Colombier 		csr32w(ctlr, WpdmaGloCfg, tmp);
2870*43f728cbSDavid du Colombier 
2871*43f728cbSDavid du Colombier 		txrxon(ctlr);
2872*43f728cbSDavid du Colombier 	}
2873*43f728cbSDavid du Colombier 	if(r & MacInt2)
2874*43f728cbSDavid du Colombier 		stats(ctlr);
2875*43f728cbSDavid du Colombier 
2876*43f728cbSDavid du Colombier 	if(r & TxDoneInt5)
2877*43f728cbSDavid du Colombier 		rt2860tx(ctlr, 5);
2878*43f728cbSDavid du Colombier 
2879*43f728cbSDavid du Colombier 	if(r & RxDoneInt)
2880*43f728cbSDavid du Colombier 		receive(ctlr);
2881*43f728cbSDavid du Colombier 
2882*43f728cbSDavid du Colombier 	if(r & TxDoneInt4)
2883*43f728cbSDavid du Colombier 		rt2860tx(ctlr, 4);
2884*43f728cbSDavid du Colombier 
2885*43f728cbSDavid du Colombier 	if(r & TxDoneInt3)
2886*43f728cbSDavid du Colombier 		rt2860tx(ctlr, 3);
2887*43f728cbSDavid du Colombier 
2888*43f728cbSDavid du Colombier 	if(r & TxDoneInt2)
2889*43f728cbSDavid du Colombier 		rt2860tx(ctlr, 2);
2890*43f728cbSDavid du Colombier 
2891*43f728cbSDavid du Colombier 	if(r & TxDoneInt1)
2892*43f728cbSDavid du Colombier 		rt2860tx(ctlr, 1);
2893*43f728cbSDavid du Colombier 
2894*43f728cbSDavid du Colombier 	if(r & TxDoneInt0)
2895*43f728cbSDavid du Colombier 		rt2860tx(ctlr, 0);
2896*43f728cbSDavid du Colombier 
2897*43f728cbSDavid du Colombier 	iunlock(ctlr);
2898*43f728cbSDavid du Colombier }
2899*43f728cbSDavid du Colombier 
2900*43f728cbSDavid du Colombier static void
eepromctl(Ctlr * ctlr,u32int val)2901*43f728cbSDavid du Colombier eepromctl(Ctlr *ctlr, u32int val)
2902*43f728cbSDavid du Colombier {
2903*43f728cbSDavid du Colombier 	csr32w(ctlr, PciEectrl, val);
2904*43f728cbSDavid du Colombier 	coherence();
2905*43f728cbSDavid du Colombier 	microdelay(EepromDelay);
2906*43f728cbSDavid du Colombier }
2907*43f728cbSDavid du Colombier 
2908*43f728cbSDavid du Colombier /*
2909*43f728cbSDavid du Colombier  * Read 16 bits at address 'addr' from the serial EEPROM (either 93C46,
2910*43f728cbSDavid du Colombier  * 93C66 or 93C86).
2911*43f728cbSDavid du Colombier  */
2912*43f728cbSDavid du Colombier static u16int
eeread2(Ctlr * ctlr,u16int addr)2913*43f728cbSDavid du Colombier eeread2(Ctlr *ctlr, u16int addr)
2914*43f728cbSDavid du Colombier {
2915*43f728cbSDavid du Colombier 	u32int tmp;
2916*43f728cbSDavid du Colombier 	u16int val;
2917*43f728cbSDavid du Colombier 	int n;
2918*43f728cbSDavid du Colombier 
2919*43f728cbSDavid du Colombier 	/* clock C once before the first command */
2920*43f728cbSDavid du Colombier 	eepromctl(ctlr, 0);
2921*43f728cbSDavid du Colombier 
2922*43f728cbSDavid du Colombier 	eepromctl(ctlr, EectrlS);
2923*43f728cbSDavid du Colombier 	eepromctl(ctlr, EectrlS | EectrlC);
2924*43f728cbSDavid du Colombier 	eepromctl(ctlr, EectrlS);
2925*43f728cbSDavid du Colombier 
2926*43f728cbSDavid du Colombier 	/* write start bit (1) */
2927*43f728cbSDavid du Colombier 	eepromctl(ctlr, EectrlS | EectrlD);
2928*43f728cbSDavid du Colombier 	eepromctl(ctlr, EectrlS | EectrlD | EectrlC);
2929*43f728cbSDavid du Colombier 
2930*43f728cbSDavid du Colombier 	/* write READ opcode (10) */
2931*43f728cbSDavid du Colombier 	eepromctl(ctlr, EectrlS | EectrlD);
2932*43f728cbSDavid du Colombier 	eepromctl(ctlr, EectrlS | EectrlD | EectrlC);
2933*43f728cbSDavid du Colombier 	eepromctl(ctlr, EectrlS);
2934*43f728cbSDavid du Colombier 	eepromctl(ctlr, EectrlS | EectrlC);
2935*43f728cbSDavid du Colombier 
2936*43f728cbSDavid du Colombier 	/* write address (A5-A0 or A7-A0) */
2937*43f728cbSDavid du Colombier 	n = ((csr32r(ctlr, PciEectrl) & 0x30) == 0) ? 5 : 7;
2938*43f728cbSDavid du Colombier 	for(; n >= 0; n--){
2939*43f728cbSDavid du Colombier 		eepromctl(ctlr, EectrlS |
2940*43f728cbSDavid du Colombier 		    (((addr >> n) & 1) << EectrlShiftD));
2941*43f728cbSDavid du Colombier 		eepromctl(ctlr, EectrlS |
2942*43f728cbSDavid du Colombier 		    (((addr >> n) & 1) << EectrlShiftD) | EectrlC);
2943*43f728cbSDavid du Colombier 	}
2944*43f728cbSDavid du Colombier 
2945*43f728cbSDavid du Colombier 	eepromctl(ctlr, EectrlS);
2946*43f728cbSDavid du Colombier 
2947*43f728cbSDavid du Colombier 	/* read data Q15-Q0 */
2948*43f728cbSDavid du Colombier 	val = 0;
2949*43f728cbSDavid du Colombier 	for(n = 15; n >= 0; n--){
2950*43f728cbSDavid du Colombier 		eepromctl(ctlr, EectrlS | EectrlC);
2951*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, PciEectrl);
2952*43f728cbSDavid du Colombier 		val |= ((tmp & EectrlQ) >> EectrlShiftQ) << n;
2953*43f728cbSDavid du Colombier 		eepromctl(ctlr, EectrlS);
2954*43f728cbSDavid du Colombier 	}
2955*43f728cbSDavid du Colombier 
2956*43f728cbSDavid du Colombier 	eepromctl(ctlr, 0);
2957*43f728cbSDavid du Colombier 
2958*43f728cbSDavid du Colombier 	/* clear Chip Select and clock C */
2959*43f728cbSDavid du Colombier 	eepromctl(ctlr, EectrlS);
2960*43f728cbSDavid du Colombier 	eepromctl(ctlr, 0);
2961*43f728cbSDavid du Colombier 	eepromctl(ctlr, EectrlC);
2962*43f728cbSDavid du Colombier 
2963*43f728cbSDavid du Colombier 	return val;
2964*43f728cbSDavid du Colombier }
2965*43f728cbSDavid du Colombier 
2966*43f728cbSDavid du Colombier /* Read 16-bit from eFUSE ROM (>=RT3071 only.) */
2967*43f728cbSDavid du Colombier static u16int
efuseread2(Ctlr * ctlr,u16int addr)2968*43f728cbSDavid du Colombier efuseread2(Ctlr *ctlr, u16int addr)
2969*43f728cbSDavid du Colombier {
2970*43f728cbSDavid du Colombier 	u32int tmp;
2971*43f728cbSDavid du Colombier 	u16int reg;
2972*43f728cbSDavid du Colombier 	int ntries;
2973*43f728cbSDavid du Colombier 
2974*43f728cbSDavid du Colombier 	addr *= 2;
2975*43f728cbSDavid du Colombier 	/*-
2976*43f728cbSDavid du Colombier 	 * Read one 16-byte block into registers EFUSE_DATA[0-3]:
2977*43f728cbSDavid du Colombier 	 * DATA0: F E D C
2978*43f728cbSDavid du Colombier 	 * DATA1: B A 9 8
2979*43f728cbSDavid du Colombier 	 * DATA2: 7 6 5 4
2980*43f728cbSDavid du Colombier 	 * DATA3: 3 2 1 0
2981*43f728cbSDavid du Colombier 	 */
2982*43f728cbSDavid du Colombier 	tmp = csr32r(ctlr, Rt3070EfuseCtrl);
2983*43f728cbSDavid du Colombier 	tmp &= ~(Rt3070EfsromModeMask | Rt3070EfsromAinMask);
2984*43f728cbSDavid du Colombier 	tmp |= (addr & ~0xf) << Rt3070EfsromAinShift | Rt3070EfsromKick;
2985*43f728cbSDavid du Colombier 	csr32w(ctlr, Rt3070EfuseCtrl, tmp);
2986*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 500; ntries++){
2987*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, Rt3070EfuseCtrl);
2988*43f728cbSDavid du Colombier 		if(!(tmp & Rt3070EfsromKick))
2989*43f728cbSDavid du Colombier 			break;
2990*43f728cbSDavid du Colombier 		microdelay(2);
2991*43f728cbSDavid du Colombier 	}
2992*43f728cbSDavid du Colombier 	if(ntries == 500)
2993*43f728cbSDavid du Colombier 		return 0xffff;
2994*43f728cbSDavid du Colombier 
2995*43f728cbSDavid du Colombier 	if((tmp & Rt3070EfuseAoutMask) == Rt3070EfuseAoutMask)
2996*43f728cbSDavid du Colombier 		return 0xffff;	/* address not found */
2997*43f728cbSDavid du Colombier 
2998*43f728cbSDavid du Colombier 	/* determine to which 32-bit register our 16-bit word belongs */
2999*43f728cbSDavid du Colombier 	reg = Rt3070EfuseData3 - (addr & 0xc);
3000*43f728cbSDavid du Colombier 	tmp = csr32r(ctlr, reg);
3001*43f728cbSDavid du Colombier 
3002*43f728cbSDavid du Colombier 	return (addr & 2) ? tmp >> 16 : tmp & 0xffff;
3003*43f728cbSDavid du Colombier }
3004*43f728cbSDavid du Colombier 
3005*43f728cbSDavid du Colombier static char*
eepromread(Ether * edev)3006*43f728cbSDavid du Colombier eepromread(Ether *edev)
3007*43f728cbSDavid du Colombier {
3008*43f728cbSDavid du Colombier 	s8int delta_2ghz, delta_5ghz;
3009*43f728cbSDavid du Colombier 	u32int tmp;
3010*43f728cbSDavid du Colombier 	u16int val;
3011*43f728cbSDavid du Colombier 	int ridx, ant, i;
3012*43f728cbSDavid du Colombier 	u16int (*rom_read)(Ctlr*, u16int);
3013*43f728cbSDavid du Colombier 	Ctlr *ctlr;
3014*43f728cbSDavid du Colombier 
3015*43f728cbSDavid du Colombier 	enum { DefLna =	10 };
3016*43f728cbSDavid du Colombier 
3017*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
3018*43f728cbSDavid du Colombier 	/* check whether the ROM is eFUSE ROM or EEPROM */
3019*43f728cbSDavid du Colombier 	rom_read = eeread2;
3020*43f728cbSDavid du Colombier 	if(ctlr->mac_ver >= 0x3071){
3021*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, Rt3070EfuseCtrl);
3022*43f728cbSDavid du Colombier 		if(tmp & Rt3070SelEfuse)
3023*43f728cbSDavid du Colombier 			rom_read = efuseread2;
3024*43f728cbSDavid du Colombier 	}
3025*43f728cbSDavid du Colombier 
3026*43f728cbSDavid du Colombier 	/* read MAC address */
3027*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromMac01);
3028*43f728cbSDavid du Colombier 	edev->ea[0] = val & 0xff;
3029*43f728cbSDavid du Colombier 	edev->ea[1] = val >> 8;
3030*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromMac23);
3031*43f728cbSDavid du Colombier 	edev->ea[2] = val & 0xff;
3032*43f728cbSDavid du Colombier 	edev->ea[3] = val >> 8;
3033*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromMac45);
3034*43f728cbSDavid du Colombier 	edev->ea[4] = val & 0xff;
3035*43f728cbSDavid du Colombier 	edev->ea[5] = val >> 8;
3036*43f728cbSDavid du Colombier 
3037*43f728cbSDavid du Colombier 	/* read vendor BBP settings */
3038*43f728cbSDavid du Colombier 	for(i = 0; i < 8; i++){
3039*43f728cbSDavid du Colombier 		val = rom_read(ctlr, EepromBbpBase + i);
3040*43f728cbSDavid du Colombier 		ctlr->bbp[i].val = val & 0xff;
3041*43f728cbSDavid du Colombier 		ctlr->bbp[i].reg = val >> 8;
3042*43f728cbSDavid du Colombier 	}
3043*43f728cbSDavid du Colombier 
3044*43f728cbSDavid du Colombier 	if(ctlr->mac_ver >= 0x3071){
3045*43f728cbSDavid du Colombier 		/* read vendor RF settings */
3046*43f728cbSDavid du Colombier 		for(i = 0; i < 10; i++){
3047*43f728cbSDavid du Colombier 			val = rom_read(ctlr, Rt3071EepromRfBase + i);
3048*43f728cbSDavid du Colombier 			ctlr->rf[i].val = val & 0xff;
3049*43f728cbSDavid du Colombier 			ctlr->rf[i].reg = val >> 8;
3050*43f728cbSDavid du Colombier 		}
3051*43f728cbSDavid du Colombier 	}
3052*43f728cbSDavid du Colombier 
3053*43f728cbSDavid du Colombier 	/* read RF frequency offset from EEPROM */
3054*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromFreqLeds);
3055*43f728cbSDavid du Colombier 	ctlr->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0;
3056*43f728cbSDavid du Colombier 	if((val >> 8) != 0xff){
3057*43f728cbSDavid du Colombier 		/* read LEDs operating mode */
3058*43f728cbSDavid du Colombier 		ctlr->leds = val >> 8;
3059*43f728cbSDavid du Colombier 		ctlr->led[0] = rom_read(ctlr, EepromLed1);
3060*43f728cbSDavid du Colombier 		ctlr->led[1] = rom_read(ctlr, EepromLed2);
3061*43f728cbSDavid du Colombier 		ctlr->led[2] = rom_read(ctlr, EepromLed3);
3062*43f728cbSDavid du Colombier 	}else{
3063*43f728cbSDavid du Colombier 		/* broken EEPROM, use default settings */
3064*43f728cbSDavid du Colombier 		ctlr->leds = 0x01;
3065*43f728cbSDavid du Colombier 		ctlr->led[0] = 0x5555;
3066*43f728cbSDavid du Colombier 		ctlr->led[1] = 0x2221;
3067*43f728cbSDavid du Colombier 		ctlr->led[2] = 0xa9f8;
3068*43f728cbSDavid du Colombier 	}
3069*43f728cbSDavid du Colombier 	/* read RF information */
3070*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromAntenna);
3071*43f728cbSDavid du Colombier 	if(val == 0xffff){
3072*43f728cbSDavid du Colombier 		if(ctlr->mac_ver == 0x3593){
3073*43f728cbSDavid du Colombier 			/* default to RF3053 3T3R */
3074*43f728cbSDavid du Colombier 			ctlr->rf_rev = Rf3053;
3075*43f728cbSDavid du Colombier 			ctlr->ntxchains = 3;
3076*43f728cbSDavid du Colombier 			ctlr->nrxchains = 3;
3077*43f728cbSDavid du Colombier 		}else if(ctlr->mac_ver >= 0x3071){
3078*43f728cbSDavid du Colombier 			/* default to RF3020 1T1R */
3079*43f728cbSDavid du Colombier 			ctlr->rf_rev = Rf3020;
3080*43f728cbSDavid du Colombier 			ctlr->ntxchains = 1;
3081*43f728cbSDavid du Colombier 			ctlr->nrxchains = 1;
3082*43f728cbSDavid du Colombier 		}else{
3083*43f728cbSDavid du Colombier 			/* default to RF2820 1T2R */
3084*43f728cbSDavid du Colombier 			ctlr->rf_rev = Rf2820;
3085*43f728cbSDavid du Colombier 			ctlr->ntxchains = 1;
3086*43f728cbSDavid du Colombier 			ctlr->nrxchains = 2;
3087*43f728cbSDavid du Colombier 		}
3088*43f728cbSDavid du Colombier 	}else{
3089*43f728cbSDavid du Colombier 		ctlr->rf_rev = (val >> 8) & 0xf;
3090*43f728cbSDavid du Colombier 		ctlr->ntxchains = (val >> 4) & 0xf;
3091*43f728cbSDavid du Colombier 		ctlr->nrxchains = val & 0xf;
3092*43f728cbSDavid du Colombier 	}
3093*43f728cbSDavid du Colombier 
3094*43f728cbSDavid du Colombier 	/* check if RF supports automatic Tx access gain control */
3095*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromConfig);
3096*43f728cbSDavid du Colombier 	/* check if driver should patch the DAC issue */
3097*43f728cbSDavid du Colombier 	if((val >> 8) != 0xff)
3098*43f728cbSDavid du Colombier 		ctlr->patch_dac = (val >> 15) & 1;
3099*43f728cbSDavid du Colombier 	if((val & 0xff) != 0xff){
3100*43f728cbSDavid du Colombier 		ctlr->ext_5ghz_lna = (val >> 3) & 1;
3101*43f728cbSDavid du Colombier 		ctlr->ext_2ghz_lna = (val >> 2) & 1;
3102*43f728cbSDavid du Colombier 		/* check if RF supports automatic Tx access gain control */
3103*43f728cbSDavid du Colombier 		ctlr->calib_2ghz = ctlr->calib_5ghz = 0; /* XXX (val >> 1) & 1 */;
3104*43f728cbSDavid du Colombier 		/* check if we have a hardware radio switch */
3105*43f728cbSDavid du Colombier 		ctlr->rfswitch = val & 1;
3106*43f728cbSDavid du Colombier 	}
3107*43f728cbSDavid du Colombier 	if(ctlr->flags & AdvancedPs){
3108*43f728cbSDavid du Colombier 		/* read PCIe power save level */
3109*43f728cbSDavid du Colombier 		val = rom_read(ctlr, EepromPciePslevel);
3110*43f728cbSDavid du Colombier 		if((val & 0xff) != 0xff){
3111*43f728cbSDavid du Colombier 			ctlr->pslevel = val & 0x3;
3112*43f728cbSDavid du Colombier 			val = rom_read(ctlr, EepromRev);
3113*43f728cbSDavid du Colombier 			if((val & 0xff80) != 0x9280)
3114*43f728cbSDavid du Colombier 				ctlr->pslevel = MIN(ctlr->pslevel, 1);
3115*43f728cbSDavid du Colombier 		}
3116*43f728cbSDavid du Colombier 	}
3117*43f728cbSDavid du Colombier 
3118*43f728cbSDavid du Colombier 	/* read power settings for 2GHz channels */
3119*43f728cbSDavid du Colombier 	for(i = 0; i < 14; i += 2){
3120*43f728cbSDavid du Colombier 		val = rom_read(ctlr,
3121*43f728cbSDavid du Colombier 		    EepromPwr2ghzBase1 + i / 2);
3122*43f728cbSDavid du Colombier 		ctlr->txpow1[i + 0] = (s8int)(val & 0xff);
3123*43f728cbSDavid du Colombier 		ctlr->txpow1[i + 1] = (s8int)(val >> 8);
3124*43f728cbSDavid du Colombier 
3125*43f728cbSDavid du Colombier 		val = rom_read(ctlr,
3126*43f728cbSDavid du Colombier 		    EepromPwr2ghzBase2 + i / 2);
3127*43f728cbSDavid du Colombier 		ctlr->txpow2[i + 0] = (s8int)(val & 0xff);
3128*43f728cbSDavid du Colombier 		ctlr->txpow2[i + 1] = (s8int)(val >> 8);
3129*43f728cbSDavid du Colombier 	}
3130*43f728cbSDavid du Colombier 	/* fix broken Tx power entries */
3131*43f728cbSDavid du Colombier 
3132*43f728cbSDavid du Colombier 	for(i = 0; i < 14; i++){
3133*43f728cbSDavid du Colombier 		if(ctlr->txpow1[i] < 0 || ctlr->txpow1[i] > 31)
3134*43f728cbSDavid du Colombier 			ctlr->txpow1[i] = 5;
3135*43f728cbSDavid du Colombier 		if(ctlr->txpow2[i] < 0 || ctlr->txpow2[i] > 31)
3136*43f728cbSDavid du Colombier 			ctlr->txpow2[i] = 5;
3137*43f728cbSDavid du Colombier 	}
3138*43f728cbSDavid du Colombier 	/* read power settings for 5GHz channels */
3139*43f728cbSDavid du Colombier 	for(i = 0; i < 40; i += 2){
3140*43f728cbSDavid du Colombier 		val = rom_read(ctlr,
3141*43f728cbSDavid du Colombier 		    EepromPwr5ghzBase1 + i / 2);
3142*43f728cbSDavid du Colombier 		ctlr->txpow1[i + 14] = (s8int)(val & 0xff);
3143*43f728cbSDavid du Colombier 		ctlr->txpow1[i + 15] = (s8int)(val >> 8);
3144*43f728cbSDavid du Colombier 
3145*43f728cbSDavid du Colombier 		val = rom_read(ctlr,
3146*43f728cbSDavid du Colombier 		    EepromPwr5ghzBase2 + i / 2);
3147*43f728cbSDavid du Colombier 		ctlr->txpow2[i + 14] = (s8int)(val & 0xff);
3148*43f728cbSDavid du Colombier 		ctlr->txpow2[i + 15] = (s8int)(val >> 8);
3149*43f728cbSDavid du Colombier 	}
3150*43f728cbSDavid du Colombier 
3151*43f728cbSDavid du Colombier 	/* fix broken Tx power entries */
3152*43f728cbSDavid du Colombier 	for(i = 0; i < 40; i++){
3153*43f728cbSDavid du Colombier 		if(ctlr->txpow1[14 + i] < -7 || ctlr->txpow1[14 + i] > 15)
3154*43f728cbSDavid du Colombier 			ctlr->txpow1[14 + i] = 5;
3155*43f728cbSDavid du Colombier 		if(ctlr->txpow2[14 + i] < -7 || ctlr->txpow2[14 + i] > 15)
3156*43f728cbSDavid du Colombier 			ctlr->txpow2[14 + i] = 5;
3157*43f728cbSDavid du Colombier 	}
3158*43f728cbSDavid du Colombier 
3159*43f728cbSDavid du Colombier 	/* read Tx power compensation for each Tx rate */
3160*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromDeltapwr);
3161*43f728cbSDavid du Colombier 	delta_2ghz = delta_5ghz = 0;
3162*43f728cbSDavid du Colombier 	if((val & 0xff) != 0xff && (val & 0x80)){
3163*43f728cbSDavid du Colombier 		delta_2ghz = val & 0xf;
3164*43f728cbSDavid du Colombier 		if(!(val & 0x40))	/* negative number */
3165*43f728cbSDavid du Colombier 			delta_2ghz = -delta_2ghz;
3166*43f728cbSDavid du Colombier 	}
3167*43f728cbSDavid du Colombier 	val >>= 8;
3168*43f728cbSDavid du Colombier 	if((val & 0xff) != 0xff && (val & 0x80)){
3169*43f728cbSDavid du Colombier 		delta_5ghz = val & 0xf;
3170*43f728cbSDavid du Colombier 		if(!(val & 0x40))	/* negative number */
3171*43f728cbSDavid du Colombier 			delta_5ghz = -delta_5ghz;
3172*43f728cbSDavid du Colombier 	}
3173*43f728cbSDavid du Colombier 
3174*43f728cbSDavid du Colombier 	for(ridx = 0; ridx < 5; ridx++){
3175*43f728cbSDavid du Colombier 		u32int reg;
3176*43f728cbSDavid du Colombier 
3177*43f728cbSDavid du Colombier 		val = rom_read(ctlr, EepromRpwr + ridx * 2);
3178*43f728cbSDavid du Colombier 		reg = val;
3179*43f728cbSDavid du Colombier 		val = rom_read(ctlr, EepromRpwr + ridx * 2 + 1);
3180*43f728cbSDavid du Colombier 		reg |= (u32int)val << 16;
3181*43f728cbSDavid du Colombier 
3182*43f728cbSDavid du Colombier 		ctlr->txpow20mhz[ridx] = reg;
3183*43f728cbSDavid du Colombier 		ctlr->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz);
3184*43f728cbSDavid du Colombier 		ctlr->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz);
3185*43f728cbSDavid du Colombier 	}
3186*43f728cbSDavid du Colombier 
3187*43f728cbSDavid du Colombier 	/* read factory-calibrated samples for temperature compensation */
3188*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromTssi12ghz);
3189*43f728cbSDavid du Colombier 	ctlr->tssi_2ghz[0] = val & 0xff;	/* [-4] */
3190*43f728cbSDavid du Colombier 	ctlr->tssi_2ghz[1] = val >> 8;	/* [-3] */
3191*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromTssi22ghz);
3192*43f728cbSDavid du Colombier 	ctlr->tssi_2ghz[2] = val & 0xff;	/* [-2] */
3193*43f728cbSDavid du Colombier 	ctlr->tssi_2ghz[3] = val >> 8;	/* [-1] */
3194*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromTssi32ghz);
3195*43f728cbSDavid du Colombier 	ctlr->tssi_2ghz[4] = val & 0xff;	/* [+0] */
3196*43f728cbSDavid du Colombier 	ctlr->tssi_2ghz[5] = val >> 8;	/* [+1] */
3197*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromTssi42ghz);
3198*43f728cbSDavid du Colombier 	ctlr->tssi_2ghz[6] = val & 0xff;	/* [+2] */
3199*43f728cbSDavid du Colombier 	ctlr->tssi_2ghz[7] = val >> 8;	/* [+3] */
3200*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromTssi52ghz);
3201*43f728cbSDavid du Colombier 	ctlr->tssi_2ghz[8] = val & 0xff;	/* [+4] */
3202*43f728cbSDavid du Colombier 	ctlr->step_2ghz = val >> 8;
3203*43f728cbSDavid du Colombier 	/* check that ref value is correct, otherwise disable calibration */
3204*43f728cbSDavid du Colombier 	if(ctlr->tssi_2ghz[4] == 0xff)
3205*43f728cbSDavid du Colombier 		ctlr->calib_2ghz = 0;
3206*43f728cbSDavid du Colombier 
3207*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromTssi15ghz);
3208*43f728cbSDavid du Colombier 	ctlr->tssi_5ghz[0] = val & 0xff;	/* [-4] */
3209*43f728cbSDavid du Colombier 	ctlr->tssi_5ghz[1] = val >> 8;	/* [-3] */
3210*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromTssi25ghz);
3211*43f728cbSDavid du Colombier 	ctlr->tssi_5ghz[2] = val & 0xff;	/* [-2] */
3212*43f728cbSDavid du Colombier 	ctlr->tssi_5ghz[3] = val >> 8;	/* [-1] */
3213*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromTssi35ghz);
3214*43f728cbSDavid du Colombier 	ctlr->tssi_5ghz[4] = val & 0xff;	/* [+0] */
3215*43f728cbSDavid du Colombier 	ctlr->tssi_5ghz[5] = val >> 8;	/* [+1] */
3216*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromTssi45ghz);
3217*43f728cbSDavid du Colombier 	ctlr->tssi_5ghz[6] = val & 0xff;	/* [+2] */
3218*43f728cbSDavid du Colombier 	ctlr->tssi_5ghz[7] = val >> 8;	/* [+3] */
3219*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromTssi55ghz);
3220*43f728cbSDavid du Colombier 	ctlr->tssi_5ghz[8] = val & 0xff;	/* [+4] */
3221*43f728cbSDavid du Colombier 	ctlr->step_5ghz = val >> 8;
3222*43f728cbSDavid du Colombier 	/* check that ref value is correct, otherwise disable calibration */
3223*43f728cbSDavid du Colombier 	if(ctlr->tssi_5ghz[4] == 0xff)
3224*43f728cbSDavid du Colombier 		ctlr->calib_5ghz = 0;
3225*43f728cbSDavid du Colombier 
3226*43f728cbSDavid du Colombier 	/* read RSSI offsets and LNA gains from EEPROM */
3227*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromRssi12ghz);
3228*43f728cbSDavid du Colombier 	ctlr->rssi_2ghz[0] = val & 0xff;	/* Ant A */
3229*43f728cbSDavid du Colombier 	ctlr->rssi_2ghz[1] = val >> 8;	/* Ant B */
3230*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromRssi22ghz);
3231*43f728cbSDavid du Colombier 	if(ctlr->mac_ver >= 0x3071){
3232*43f728cbSDavid du Colombier 		/*
3233*43f728cbSDavid du Colombier 		 * On RT3090 chips (limited to 2 Rx chains), this ROM
3234*43f728cbSDavid du Colombier 		 * field contains the Tx mixer gain for the 2GHz band.
3235*43f728cbSDavid du Colombier 		 */
3236*43f728cbSDavid du Colombier 		if((val & 0xff) != 0xff)
3237*43f728cbSDavid du Colombier 			ctlr->txmixgain_2ghz = val & 0x7;
3238*43f728cbSDavid du Colombier 	}else
3239*43f728cbSDavid du Colombier 		ctlr->rssi_2ghz[2] = val & 0xff;	/* Ant C */
3240*43f728cbSDavid du Colombier 	ctlr->lna[2] = val >> 8;		/* channel group 2 */
3241*43f728cbSDavid du Colombier 
3242*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromRssi15ghz);
3243*43f728cbSDavid du Colombier 	ctlr->rssi_5ghz[0] = val & 0xff;	/* Ant A */
3244*43f728cbSDavid du Colombier 	ctlr->rssi_5ghz[1] = val >> 8;	/* Ant B */
3245*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromRssi25ghz);
3246*43f728cbSDavid du Colombier 	ctlr->rssi_5ghz[2] = val & 0xff;	/* Ant C */
3247*43f728cbSDavid du Colombier 	ctlr->lna[3] = val >> 8;		/* channel group 3 */
3248*43f728cbSDavid du Colombier 
3249*43f728cbSDavid du Colombier 	val = rom_read(ctlr, EepromLna);
3250*43f728cbSDavid du Colombier 	if(ctlr->mac_ver >= 0x3071)
3251*43f728cbSDavid du Colombier 		ctlr->lna[0] = DefLna;
3252*43f728cbSDavid du Colombier 	else				/* channel group 0 */
3253*43f728cbSDavid du Colombier 		ctlr->lna[0] = val & 0xff;
3254*43f728cbSDavid du Colombier 	ctlr->lna[1] = val >> 8;		/* channel group 1 */
3255*43f728cbSDavid du Colombier 
3256*43f728cbSDavid du Colombier 	/* fix broken 5GHz LNA entries */
3257*43f728cbSDavid du Colombier 	if(ctlr->lna[2] == 0 || ctlr->lna[2] == 0xff){
3258*43f728cbSDavid du Colombier 		ctlr->lna[2] = ctlr->lna[1];
3259*43f728cbSDavid du Colombier 	}
3260*43f728cbSDavid du Colombier 	if(ctlr->lna[3] == 0 || ctlr->lna[3] == 0xff){
3261*43f728cbSDavid du Colombier 		ctlr->lna[3] = ctlr->lna[1];
3262*43f728cbSDavid du Colombier 	}
3263*43f728cbSDavid du Colombier 
3264*43f728cbSDavid du Colombier 	/* fix broken RSSI offset entries */
3265*43f728cbSDavid du Colombier 	for(ant = 0; ant < 3; ant++){
3266*43f728cbSDavid du Colombier 		if(ctlr->rssi_2ghz[ant] < -10 || ctlr->rssi_2ghz[ant] > 10){
3267*43f728cbSDavid du Colombier 			ctlr->rssi_2ghz[ant] = 0;
3268*43f728cbSDavid du Colombier 		}
3269*43f728cbSDavid du Colombier 		if(ctlr->rssi_5ghz[ant] < -10 || ctlr->rssi_5ghz[ant] > 10){
3270*43f728cbSDavid du Colombier 			ctlr->rssi_5ghz[ant] = 0;
3271*43f728cbSDavid du Colombier 		}
3272*43f728cbSDavid du Colombier 	}
3273*43f728cbSDavid du Colombier 
3274*43f728cbSDavid du Colombier 	return 0;
3275*43f728cbSDavid du Colombier }
3276*43f728cbSDavid du Colombier 
3277*43f728cbSDavid du Colombier static const char *
getrfname(u8int rev)3278*43f728cbSDavid du Colombier getrfname(u8int rev)
3279*43f728cbSDavid du Colombier {
3280*43f728cbSDavid du Colombier 	if((rev == 0) || (rev >= nelem(rfnames)))
3281*43f728cbSDavid du Colombier 		return "unknown";
3282*43f728cbSDavid du Colombier 	if(rfnames[rev][0] == '\0')
3283*43f728cbSDavid du Colombier 		return "unknown";
3284*43f728cbSDavid du Colombier 	return rfnames[rev];
3285*43f728cbSDavid du Colombier }
3286*43f728cbSDavid du Colombier 
3287*43f728cbSDavid du Colombier static int
rbplant(Ctlr * ctlr,int i)3288*43f728cbSDavid du Colombier rbplant(Ctlr *ctlr, int i)
3289*43f728cbSDavid du Colombier {
3290*43f728cbSDavid du Colombier 	Block *b;
3291*43f728cbSDavid du Colombier 	uchar *p;
3292*43f728cbSDavid du Colombier 
3293*43f728cbSDavid du Colombier 	b = iallocb(Rbufsize + 256);
3294*43f728cbSDavid du Colombier 	if(b == nil)
3295*43f728cbSDavid du Colombier 		return -1;
3296*43f728cbSDavid du Colombier 	b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, 256);
3297*43f728cbSDavid du Colombier 	memset(b->rp, 0, Rbufsize);
3298*43f728cbSDavid du Colombier 	ctlr->rx.b[i] = b;
3299*43f728cbSDavid du Colombier 	p = (uchar*)&ctlr->rx.p[i * 4]; /* sdp0 */
3300*43f728cbSDavid du Colombier 	memset(p, 0, Rdscsize);
3301*43f728cbSDavid du Colombier 	put32(p, PCIWADDR(b->rp));
3302*43f728cbSDavid du Colombier 	p = (uchar*)&ctlr->rx.p[i * 4 + 1]; /* sdl0 */
3303*43f728cbSDavid du Colombier 	p += 2; /* sdl1 */
3304*43f728cbSDavid du Colombier 	put16(p, Rbufsize);;
3305*43f728cbSDavid du Colombier 
3306*43f728cbSDavid du Colombier 	return 0;
3307*43f728cbSDavid du Colombier }
3308*43f728cbSDavid du Colombier 
3309*43f728cbSDavid du Colombier static char*
allocrx(Ctlr * ctlr,RXQ * rx)3310*43f728cbSDavid du Colombier allocrx(Ctlr *ctlr, RXQ *rx)
3311*43f728cbSDavid du Colombier {
3312*43f728cbSDavid du Colombier 	int i;
3313*43f728cbSDavid du Colombier 
3314*43f728cbSDavid du Colombier 	if(rx->b == nil)
3315*43f728cbSDavid du Colombier 		rx->b = malloc(sizeof(Block*) * Nrx);
3316*43f728cbSDavid du Colombier 	if(rx->p == nil) /* Rx descriptors */
3317*43f728cbSDavid du Colombier 		rx->p = mallocalign(Nrx * Rdscsize, 16, 0, 0);
3318*43f728cbSDavid du Colombier 	if(rx->b == nil || rx->p == nil)
3319*43f728cbSDavid du Colombier 		return "no memory for rx ring";
3320*43f728cbSDavid du Colombier 	memset(rx->p, 0, Nrx * Rdscsize);
3321*43f728cbSDavid du Colombier 	for(i=0; i<Nrx; i++){
3322*43f728cbSDavid du Colombier 		if(rx->b[i] != nil){
3323*43f728cbSDavid du Colombier 			freeb(rx->b[i]);
3324*43f728cbSDavid du Colombier 			rx->b[i] = nil;
3325*43f728cbSDavid du Colombier 		}
3326*43f728cbSDavid du Colombier 		if(rbplant(ctlr, i) < 0)
3327*43f728cbSDavid du Colombier 			return "no memory for rx descriptors";
3328*43f728cbSDavid du Colombier 	}
3329*43f728cbSDavid du Colombier 	rx->i = 0;
3330*43f728cbSDavid du Colombier 	return nil;
3331*43f728cbSDavid du Colombier }
3332*43f728cbSDavid du Colombier 
3333*43f728cbSDavid du Colombier static void
freerx(Ctlr *,RXQ * rx)3334*43f728cbSDavid du Colombier freerx(Ctlr *, RXQ *rx)
3335*43f728cbSDavid du Colombier {
3336*43f728cbSDavid du Colombier 	int i;
3337*43f728cbSDavid du Colombier 
3338*43f728cbSDavid du Colombier 	for(i = 0; i < Nrx; i++){
3339*43f728cbSDavid du Colombier 		if(rx->b[i] != nil){
3340*43f728cbSDavid du Colombier 			freeb(rx->b[i]);
3341*43f728cbSDavid du Colombier 			rx->b[i] = nil;
3342*43f728cbSDavid du Colombier 		}
3343*43f728cbSDavid du Colombier 	}
3344*43f728cbSDavid du Colombier 	free(rx->b);
3345*43f728cbSDavid du Colombier 	free(rx->p);
3346*43f728cbSDavid du Colombier 	rx->p = nil;
3347*43f728cbSDavid du Colombier 	rx->b = nil;
3348*43f728cbSDavid du Colombier 	rx->i = 0;
3349*43f728cbSDavid du Colombier }
3350*43f728cbSDavid du Colombier 
3351*43f728cbSDavid du Colombier static char*
alloctx(Ctlr *,TXQ * tx)3352*43f728cbSDavid du Colombier alloctx(Ctlr *, TXQ *tx)
3353*43f728cbSDavid du Colombier {
3354*43f728cbSDavid du Colombier 	if(tx->b == nil)
3355*43f728cbSDavid du Colombier 		tx->b = malloc(sizeof(Block*) * Ntx);
3356*43f728cbSDavid du Colombier 	if(tx->d == nil) /* Tx descriptors */
3357*43f728cbSDavid du Colombier 		tx->d = mallocalign(Ntx * Tdscsize, 16, 0, 0);
3358*43f728cbSDavid du Colombier 	if(tx->b == nil || tx->d == nil)
3359*43f728cbSDavid du Colombier 		return "no memory for tx ring";
3360*43f728cbSDavid du Colombier 	memset(tx->d, 0, Ntx * Tdscsize);
3361*43f728cbSDavid du Colombier 	memset(tx->b, 0, Ntx * sizeof(Block*));
3362*43f728cbSDavid du Colombier 	tx->i = 0;
3363*43f728cbSDavid du Colombier 	return nil;
3364*43f728cbSDavid du Colombier }
3365*43f728cbSDavid du Colombier 
3366*43f728cbSDavid du Colombier static void
freetx(Ctlr *,TXQ * tx)3367*43f728cbSDavid du Colombier freetx(Ctlr *, TXQ *tx)
3368*43f728cbSDavid du Colombier {
3369*43f728cbSDavid du Colombier 	free(tx->b);
3370*43f728cbSDavid du Colombier 	free(tx->d);
3371*43f728cbSDavid du Colombier 	tx->d = nil;
3372*43f728cbSDavid du Colombier 	tx->i = 0;
3373*43f728cbSDavid du Colombier }
3374*43f728cbSDavid du Colombier 
3375*43f728cbSDavid du Colombier static char*
alloctxpool(Ctlr * ctlr)3376*43f728cbSDavid du Colombier alloctxpool(Ctlr *ctlr)
3377*43f728cbSDavid du Colombier {
3378*43f728cbSDavid du Colombier 	Pool *pool;
3379*43f728cbSDavid du Colombier 
3380*43f728cbSDavid du Colombier 	pool = &ctlr->pool;
3381*43f728cbSDavid du Colombier 	if(pool->p == nil)
3382*43f728cbSDavid du Colombier 		pool->p = mallocalign(Ntxpool * TxwiDmaSz, 4096, 0, 0);
3383*43f728cbSDavid du Colombier 	if(pool->p == nil)
3384*43f728cbSDavid du Colombier 		return "no memory for pool";
3385*43f728cbSDavid du Colombier 	memset(pool->p, 0, Ntxpool * TxwiDmaSz);
3386*43f728cbSDavid du Colombier 	pool->i = 0;
3387*43f728cbSDavid du Colombier 	return 0;
3388*43f728cbSDavid du Colombier }
3389*43f728cbSDavid du Colombier 
3390*43f728cbSDavid du Colombier static char*
initring(Ctlr * ctlr)3391*43f728cbSDavid du Colombier initring(Ctlr *ctlr)
3392*43f728cbSDavid du Colombier {
3393*43f728cbSDavid du Colombier 	int qid;
3394*43f728cbSDavid du Colombier 	char *err;
3395*43f728cbSDavid du Colombier 
3396*43f728cbSDavid du Colombier 	/*
3397*43f728cbSDavid du Colombier 	 * Allocate Tx (4 EDCAs + HCCA + Mgt) and Rx rings.
3398*43f728cbSDavid du Colombier 	 */
3399*43f728cbSDavid du Colombier 	for(qid = 0; qid < 6; qid++){
3400*43f728cbSDavid du Colombier 		if((err = alloctx(ctlr, &ctlr->tx[qid])) != nil)
3401*43f728cbSDavid du Colombier 			goto fail1;
3402*43f728cbSDavid du Colombier 	}
3403*43f728cbSDavid du Colombier 	if((err = allocrx(ctlr, &ctlr->rx)) != nil)
3404*43f728cbSDavid du Colombier 		goto fail1;
3405*43f728cbSDavid du Colombier 
3406*43f728cbSDavid du Colombier 	if((err = alloctxpool(ctlr)) != nil)
3407*43f728cbSDavid du Colombier 		goto fail2;
3408*43f728cbSDavid du Colombier 	/* mgmt ring is broken on RT2860C, use EDCA AC VO ring instead */
3409*43f728cbSDavid du Colombier 
3410*43f728cbSDavid du Colombier 	ctlr->mgtqid = (ctlr->mac_ver == 0x2860 && ctlr->mac_rev == 0x0100) ?
3411*43f728cbSDavid du Colombier 	    3 : 5;
3412*43f728cbSDavid du Colombier 
3413*43f728cbSDavid du Colombier 		return nil;
3414*43f728cbSDavid du Colombier fail2:	freerx(ctlr, &ctlr->rx);
3415*43f728cbSDavid du Colombier 		return err;
3416*43f728cbSDavid du Colombier fail1:	while(--qid >= 0)
3417*43f728cbSDavid du Colombier 			freetx(ctlr, &ctlr->tx[qid]);
3418*43f728cbSDavid du Colombier 		return err;
3419*43f728cbSDavid du Colombier }
3420*43f728cbSDavid du Colombier 
3421*43f728cbSDavid du Colombier static int
rt2860init(Ether * edev)3422*43f728cbSDavid du Colombier rt2860init(Ether *edev)
3423*43f728cbSDavid du Colombier {
3424*43f728cbSDavid du Colombier 	Ctlr *ctlr;
3425*43f728cbSDavid du Colombier 	int ntries;
3426*43f728cbSDavid du Colombier 	char *err;
3427*43f728cbSDavid du Colombier 	u32int tmp;
3428*43f728cbSDavid du Colombier 
3429*43f728cbSDavid du Colombier 	SET(tmp);
3430*43f728cbSDavid du Colombier 	ctlr = edev->ctlr;
3431*43f728cbSDavid du Colombier 	/* wait for NIC to initialize */
3432*43f728cbSDavid du Colombier 	for(ntries = 0; ntries < 100; ntries++){
3433*43f728cbSDavid du Colombier 		tmp = csr32r(ctlr, AsicVerId);
3434*43f728cbSDavid du Colombier 		if(tmp != 0 && tmp != 0xffffffff)
3435*43f728cbSDavid du Colombier 			break;
3436*43f728cbSDavid du Colombier 		microdelay(10);
3437*43f728cbSDavid du Colombier 	}
3438*43f728cbSDavid du Colombier 	if(ntries == 100){
3439*43f728cbSDavid du Colombier 		print("timeout waiting for NIC to initialize");
3440*43f728cbSDavid du Colombier 		return -1;
3441*43f728cbSDavid du Colombier 	}
3442*43f728cbSDavid du Colombier 	ctlr->mac_ver = tmp >> 16;
3443*43f728cbSDavid du Colombier 	ctlr->mac_rev = tmp & 0xffff;
3444*43f728cbSDavid du Colombier 
3445*43f728cbSDavid du Colombier 	if(ctlr->mac_ver != 0x2860){
3446*43f728cbSDavid du Colombier 		switch(ctlr->pdev->did){
3447*43f728cbSDavid du Colombier 			default:
3448*43f728cbSDavid du Colombier 					break;
3449*43f728cbSDavid du Colombier 			case RalinkRT2890:
3450*43f728cbSDavid du Colombier 			case RalinkRT2790:
3451*43f728cbSDavid du Colombier 			case RalinkRT3090:
3452*43f728cbSDavid du Colombier 			case AwtRT2890:
3453*43f728cbSDavid du Colombier 					ctlr->flags = AdvancedPs;
3454*43f728cbSDavid du Colombier 					break;
3455*43f728cbSDavid du Colombier 		}
3456*43f728cbSDavid du Colombier 	}
3457*43f728cbSDavid du Colombier 	/* retrieve RF rev. no and various other things from EEPROM */
3458*43f728cbSDavid du Colombier 	eepromread(edev);
3459*43f728cbSDavid du Colombier 
3460*43f728cbSDavid du Colombier 	print("MAC/BBP RT%X (rev 0x%04X), RF %s (MIMO %dT%dR)\n",
3461*43f728cbSDavid du Colombier 	    ctlr->mac_ver, ctlr->mac_rev,
3462*43f728cbSDavid du Colombier 	    getrfname(ctlr->rf_rev), ctlr->ntxchains, ctlr->nrxchains);
3463*43f728cbSDavid du Colombier 	if((err = initring(ctlr)) != nil){
3464*43f728cbSDavid du Colombier 		print("error: %s", err);
3465*43f728cbSDavid du Colombier 		return -1;
3466*43f728cbSDavid du Colombier 	}
3467*43f728cbSDavid du Colombier 
3468*43f728cbSDavid du Colombier 	return 0;
3469*43f728cbSDavid du Colombier }
3470*43f728cbSDavid du Colombier 
3471*43f728cbSDavid du Colombier static Ctlr *rt2860head, *rt2860tail;
3472*43f728cbSDavid du Colombier 
3473*43f728cbSDavid du Colombier static void
rt2860pci(void)3474*43f728cbSDavid du Colombier rt2860pci(void)
3475*43f728cbSDavid du Colombier {
3476*43f728cbSDavid du Colombier 	Pcidev *pdev;
3477*43f728cbSDavid du Colombier 
3478*43f728cbSDavid du Colombier 	pdev = nil;
3479*43f728cbSDavid du Colombier 	while(pdev = pcimatch(pdev, 0, 0)){
3480*43f728cbSDavid du Colombier 		Ctlr *ctlr;
3481*43f728cbSDavid du Colombier 		void *mem;
3482*43f728cbSDavid du Colombier 
3483*43f728cbSDavid du Colombier 		if(pdev->ccrb != 2 || pdev->ccru != 0x80)
3484*43f728cbSDavid du Colombier 			continue;
3485*43f728cbSDavid du Colombier 		if(pdev->vid != 0x1814) /* Ralink */
3486*43f728cbSDavid du Colombier 			continue;
3487*43f728cbSDavid du Colombier 
3488*43f728cbSDavid du Colombier 		switch(pdev->did){
3489*43f728cbSDavid du Colombier 		default:
3490*43f728cbSDavid du Colombier 			continue;
3491*43f728cbSDavid du Colombier 		case RalinkRT2790:
3492*43f728cbSDavid du Colombier 		case RalinkRT3090:
3493*43f728cbSDavid du Colombier 			break;
3494*43f728cbSDavid du Colombier 		}
3495*43f728cbSDavid du Colombier 
3496*43f728cbSDavid du Colombier 		pcisetbme(pdev);
3497*43f728cbSDavid du Colombier 		pcisetpms(pdev, 0);
3498*43f728cbSDavid du Colombier 
3499*43f728cbSDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
3500*43f728cbSDavid du Colombier 		if(ctlr == nil){
3501*43f728cbSDavid du Colombier 			print("rt2860: unable to alloc Ctlr\n");
3502*43f728cbSDavid du Colombier 			continue;
3503*43f728cbSDavid du Colombier 		}
3504*43f728cbSDavid du Colombier 		ctlr->port = pdev->mem[0].bar & ~0x0F;
3505*43f728cbSDavid du Colombier 		mem = vmap(pdev->mem[0].bar & ~0x0F, pdev->mem[0].size);
3506*43f728cbSDavid du Colombier 		if(mem == nil){
3507*43f728cbSDavid du Colombier 			print("rt2860: can't map %8.8luX\n", pdev->mem[0].bar);
3508*43f728cbSDavid du Colombier 			free(ctlr);
3509*43f728cbSDavid du Colombier 			continue;
3510*43f728cbSDavid du Colombier 		}
3511*43f728cbSDavid du Colombier 		ctlr->nic = mem;
3512*43f728cbSDavid du Colombier 		ctlr->pdev = pdev;
3513*43f728cbSDavid du Colombier 
3514*43f728cbSDavid du Colombier 		if(rt2860head != nil)
3515*43f728cbSDavid du Colombier 			rt2860tail->link = ctlr;
3516*43f728cbSDavid du Colombier 		else
3517*43f728cbSDavid du Colombier 			rt2860head = ctlr;
3518*43f728cbSDavid du Colombier 		rt2860tail = ctlr;
3519*43f728cbSDavid du Colombier 	}
3520*43f728cbSDavid du Colombier }
3521*43f728cbSDavid du Colombier 
3522*43f728cbSDavid du Colombier static int
rt2860pnp(Ether * edev)3523*43f728cbSDavid du Colombier rt2860pnp(Ether* edev)
3524*43f728cbSDavid du Colombier {
3525*43f728cbSDavid du Colombier 	Ctlr *ctlr;
3526*43f728cbSDavid du Colombier 
3527*43f728cbSDavid du Colombier 	if(rt2860head == nil)
3528*43f728cbSDavid du Colombier 		rt2860pci();
3529*43f728cbSDavid du Colombier again:
3530*43f728cbSDavid du Colombier 	for(ctlr = rt2860head; ctlr != nil; ctlr = ctlr->link){
3531*43f728cbSDavid du Colombier 		if(ctlr->active)
3532*43f728cbSDavid du Colombier 			continue;
3533*43f728cbSDavid du Colombier 		if(edev->port == 0 || edev->port == ctlr->port){
3534*43f728cbSDavid du Colombier 			ctlr->active = 1;
3535*43f728cbSDavid du Colombier 			break;
3536*43f728cbSDavid du Colombier 		}
3537*43f728cbSDavid du Colombier 	}
3538*43f728cbSDavid du Colombier 
3539*43f728cbSDavid du Colombier 	if(ctlr == nil)
3540*43f728cbSDavid du Colombier 		return -1;
3541*43f728cbSDavid du Colombier 
3542*43f728cbSDavid du Colombier 	edev->ctlr = ctlr;
3543*43f728cbSDavid du Colombier 	edev->port = ctlr->port;
3544*43f728cbSDavid du Colombier 	edev->irq = ctlr->pdev->intl;
3545*43f728cbSDavid du Colombier 	edev->tbdf = ctlr->pdev->tbdf;
3546*43f728cbSDavid du Colombier 	edev->arg = edev;
3547*43f728cbSDavid du Colombier 	edev->interrupt = rt2860interrupt;
3548*43f728cbSDavid du Colombier 	edev->attach = rt2860attach;
3549*43f728cbSDavid du Colombier 	edev->ifstat = rt2860ifstat;
3550*43f728cbSDavid du Colombier 	edev->ctl = rt2860ctl;
3551*43f728cbSDavid du Colombier 	edev->promiscuous = rt2860promiscuous;
3552*43f728cbSDavid du Colombier 	edev->multicast = rt2860multicast;
3553*43f728cbSDavid du Colombier 	edev->mbps = 10;
3554*43f728cbSDavid du Colombier 
3555*43f728cbSDavid du Colombier 	if(rt2860init(edev) < 0){
3556*43f728cbSDavid du Colombier 		edev->ctlr = nil;
3557*43f728cbSDavid du Colombier 		goto again;
3558*43f728cbSDavid du Colombier 	}
3559*43f728cbSDavid du Colombier 	return 0;
3560*43f728cbSDavid du Colombier }
3561*43f728cbSDavid du Colombier 
3562*43f728cbSDavid du Colombier void
etherrt2860link(void)3563*43f728cbSDavid du Colombier etherrt2860link(void)
3564*43f728cbSDavid du Colombier {
3565*43f728cbSDavid du Colombier 	addethercard("rt2860", rt2860pnp);
3566*43f728cbSDavid du Colombier }
3567