plan9fox/sys/src/9/pc/etherrt2860.c

3564 lines
77 KiB
C

/*
* Ralink RT2860 driver
*
* Written without any documentation but Damien Bergaminis
* OpenBSD ral(4) driver sources. Requires ralink firmware
* to be present in /lib/firmware/ral-rt2860 on attach.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/netif.h"
#include "../port/etherif.h"
#include "../port/wifi.h"
enum {
/* PCI registers */
PciCfg = 0x0000,
PciCfgUsb = (1 << 17),
PciCfgPci = (1 << 16),
PciEectrl = 0x0004,
EectrlC = (1 << 0),
EectrlS = (1 << 1),
EectrlD = (1 << 2),
EectrlShiftD = 2,
EectrlQ = (1 << 3),
EectrlShiftQ = 3,
PciMcuctrl = 0x0008,
PciSysctrl = 0x000c,
PcieJtag = 0x0010,
Rt3090AuxCtrl = 0x010c,
Rt3070Opt14 = 0x0114,
};
enum {
/* SCH/DMA registers */
IntStatus = 0x0200,
/* flags for registers IntStatus/IntMask */
TxCoherent = (1 << 17),
RxCoherent = (1 << 16),
MacInt4 = (1 << 15),
MacInt3 = (1 << 14),
MacInt2 = (1 << 13),
MacInt1 = (1 << 12),
MacInt0 = (1 << 11),
TxRxCoherent = (1 << 10),
McuCmdInt = (1 << 9),
TxDoneInt5 = (1 << 8),
TxDoneInt4 = (1 << 7),
TxDoneInt3 = (1 << 6),
TxDoneInt2 = (1 << 5),
TxDoneInt1 = (1 << 4),
TxDoneInt0 = (1 << 3),
RxDoneInt = (1 << 2),
TxDlyInt = (1 << 1),
RxDlyInt = (1 << 0),
IntMask = 0x0204,
WpdmaGloCfg = 0x0208,
HdrSegLenShift = 8,
BigEndian = (1 << 7),
TxWbDdone = (1 << 6),
WpdmaBtSizeShift = 4,
WpdmaBtSize16 = 0,
WpdmaBtSize32 = 1,
WpdmaBtSize64 = 2,
WpdmaBtSize128 = 3,
RxDmaBusy = (1 << 3),
RxDmaEn = (1 << 2),
TxDmaBusy = (1 << 1),
TxDmaEn = (1 << 0),
WpdmaRstIdx = 0x020c,
DelayIntCfg = 0x0210,
TxdlyIntEn = (1 << 31),
TxmaxPintShift = 24,
TxmaxPtimeShift = 16,
RxdlyIntEn = (1 << 15),
RxmaxPintShift = 8,
RxmaxPtimeShift = 0,
WmmAifsnCfg = 0x0214,
WmmCwminCfg = 0x0218,
WmmCwmaxCfg = 0x021c,
WmmTxop0Cfg = 0x0220,
WmmTxop1Cfg = 0x0224,
GpioCtrl = 0x0228,
GpioDShift = 8,
GpioOShift = 0,
McuCmdReg = 0x022c,
#define TxBasePtr(qid) (0x0230 + (qid) * 16)
#define TxMaxCnt(qid) (0x0234 + (qid) * 16)
#define TxCtxIdx(qid) (0x0238 + (qid) * 16)
#define TxDtxIdx(qid) (0x023c + (qid) * 16)
RxBasePtr = 0x0290,
RxMaxCnt = 0x0294,
RxCalcIdx = 0x0298,
FsDrxIdx = 0x029c,
UsbDmaCfg = 0x02a0 /* RT2870 only */,
UsbTxBusy = (1 << 31),
UsbRxBusy = (1 << 30),
UsbEpoutVldShift = 24,
UsbTxEn = (1 << 23),
UsbRxEn = (1 << 22),
UsbRxAggEn = (1 << 21),
UsbTxopHalt = (1 << 20),
UsbTxClear = (1 << 19),
UsbPhyWdEn = (1 << 16),
UsbPhyManRst = (1 << 15),
#define UsbRxAggLmt(x) ((x) << 8) /* in unit of 1KB */
#define UsbRxAggTo(x) ((x) & 0xff) /* in unit of 33ns */
UsCycCnt = 0x02a4,
TestEn = (1 << 24),
TestSelShift = 16,
BtModeEn = (1 << 8),
UsCycCntShift = 0,
};
enum {
/* PBF registers */
SysCtrl = 0x0400,
HstPmSel = (1 << 16),
CapMode = (1 << 14),
PmeOen = (1 << 13),
Clkselect = (1 << 12),
PbfClkEn = (1 << 11),
MacClkEn = (1 << 10),
DmaClkEn = (1 << 9),
McuReady = (1 << 7),
AsyReset = (1 << 4),
PbfReset = (1 << 3),
MacReset = (1 << 2),
DmaReset = (1 << 1),
McuReset = (1 << 0),
HostCmd = 0x0404,
McuCmdSleep = 0x30,
McuCmdWakeup = 0x31,
McuCmdLeds = 0x50,
LedRadio = (1 << 13),
LedLink2ghz = (1 << 14),
LedLink5ghz = (1 << 15),
McuCmdLedRssi = 0x51,
McuCmdLed1 = 0x52,
McuCmdLed2 = 0x53,
McuCmdLed3 = 0x54,
McuCmdRfreset = 0x72,
McuCmdAntsel = 0x73,
McuCmdBbp = 0x80,
McuCmdPslevel = 0x83,
PbfCfg = 0x0408,
Tx1qNumShift = 21,
Tx2qNumShift = 16,
Null0Mode = (1 << 15),
Null1Mode = (1 << 14),
RxDropMode = (1 << 13),
Tx0qManual = (1 << 12),
Tx1qManual = (1 << 11),
Tx2qManual = (1 << 10),
Rx0qManual = (1 << 9),
HccaEn = (1 << 8),
Tx0qEn = (1 << 4),
Tx1qEn = (1 << 3),
Tx2qEn = (1 << 2),
Rx0qEn = (1 << 1),
MaxPcnt = 0x040c,
BufCtrl = 0x0410,
#define WriteTxq(qid) (1 << (11 - (qid)))
Null0Kick = (1 << 7),
Null1Kick = (1 << 6),
BufReset = (1 << 5),
#define ReadTxq(qid) = (1 << (3 - (qid))
ReadRx0q = (1 << 0),
McuIntSta = 0x0414,
/* flags for registers McuIntSta/McuIntEna */
McuMacInt8 = (1 << 24),
McuMacInt7 = (1 << 23),
McuMacInt6 = (1 << 22),
McuMacInt4 = (1 << 20),
McuMacInt3 = (1 << 19),
McuMacInt2 = (1 << 18),
McuMacInt1 = (1 << 17),
McuMacInt0 = (1 << 16),
Dtx0Int = (1 << 11),
Dtx1Int = (1 << 10),
Dtx2Int = (1 << 9),
Drx0Int = (1 << 8),
HcmdInt = (1 << 7),
N0txInt = (1 << 6),
N1txInt = (1 << 5),
BcntxInt = (1 << 4),
Mtx0Int = (1 << 3),
Mtx1Int = (1 << 2),
Mtx2Int = (1 << 1),
Mrx0Int = (1 << 0),
McuIntEna = 0x0418,
#define TxqIo(qid) (0x041c + (qid) * 4)
Rx0qIo = 0x0424,
BcnOffset0 = 0x042c,
BcnOffset1 = 0x0430,
TxrxqSta = 0x0434,
TxrxqPcnt = 0x0438,
Rx0qPcntMask = 0xff000000,
Tx2qPcntMask = 0x00ff0000,
Tx1qPcntMask = 0x0000ff00,
Tx0qPcntMask = 0x000000ff,
PbfDbg = 0x043c,
CapCtrl = 0x0440,
CapAdcFeq = (1 << 31),
CapStart = (1 << 30),
ManTrig = (1 << 29),
TrigOffsetShift = 16,
StartAddrShift = 0,
};
enum {
/* RT3070 registers */
Rt3070RfCsrCfg = 0x0500,
Rt3070RfKick = (1 << 17),
Rt3070RfWrite = (1 << 16),
Rt3070EfuseCtrl = 0x0580,
Rt3070SelEfuse = (1 << 31),
Rt3070EfsromKick = (1 << 30),
Rt3070EfsromAinMask = 0x03ff0000,
Rt3070EfsromAinShift = 16,
Rt3070EfsromModeMask = 0x000000c0,
Rt3070EfuseAoutMask = 0x0000003f,
Rt3070EfuseData0 = 0x0590,
Rt3070EfuseData1 = 0x0594,
Rt3070EfuseData2 = 0x0598,
Rt3070EfuseData3 = 0x059c,
Rt3090OscCtrl = 0x05a4,
Rt3070LdoCfg0 = 0x05d4,
Rt3070GpioSwitch = 0x05dc,
};
enum {
/* MAC registers */
AsicVerId = 0x1000,
MacSysCtrl = 0x1004,
RxTsEn = (1 << 7),
WlanHaltEn = (1 << 6),
PbfLoopEn = (1 << 5),
ContTxTest = (1 << 4),
MacRxEn = (1 << 3),
MacTxEn = (1 << 2),
BbpHrst = (1 << 1),
MacSrst = (1 << 0),
MacAddrDw0 = 0x1008,
MacAddrDw1 = 0x100c,
MacBssidDw0 = 0x1010,
MacBssidDw1 = 0x1014,
MultiBcnNumShift = 18,
MultiBssidModeShift = 16,
MaxLenCfg = 0x1018,
MinMpduLenShift = 16,
MaxPsduLenShift = 12,
MaxPsduLen8k = 0,
MaxPsduLen16k = 1,
MaxPsduLen32k = 2,
MaxPsduLen64k = 3,
MaxMpduLenShift = 0,
BbpCsrCfg = 0x101c,
BbpRwParallel = (1 << 19),
BbpParDur1125 = (1 << 18),
BbpCsrKick = (1 << 17),
BbpCsrRead = (1 << 16),
BbpAddrShift = 8,
BbpDataShift = 0,
RfCsrCfg0 = 0x1020,
RfRegCtrl = (1 << 31),
RfLeSel1 = (1 << 30),
RfLeStby = (1 << 29),
RfRegWidthShift = 24,
RfReg0Shift = 0,
RfCsrCfg1 = 0x1024,
RfDur5 = (1 << 24),
RfReg1Shift = 0,
RfCsrCfg2 = 0x1028,
LedCfg = 0x102c,
LedPol = (1 << 30),
YLedModeShift = 28,
GLedModeShift = 26,
RLedModeShift = 24,
LedModeOff = 0,
LedModeBlinkTx = 1,
LedModeSlowBlink = 2,
LedModeOn = 3,
SlowBlkTimeShift = 16,
LedOffTimeShift = 8,
LedOnTimeShift = 0,
};
enum {
/* undocumented registers */
Debug = 0x10f4,
};
enum {
/* MAC Timing control registers */
XifsTimeCfg = 0x1100,
BbRxendEn = (1 << 29),
EifsTimeShift = 20,
OfdmXifsTimeShift = 16,
OfdmSifsTimeShift = 8,
CckSifsTimeShift = 0,
BkoffSlotCfg = 0x1104,
CcDelayTimeShift = 8,
SlotTime = 0,
NavTimeCfg = 0x1108,
NavUpd = (1 << 31),
NavUpdValShift = 16,
NavClrEn = (1 << 15),
NavTimerShift = 0,
ChTimeCfg = 0x110c,
EifsAsChBusy = (1 << 4),
NavAsChBusy = (1 << 3),
RxAsChBusy = (1 << 2),
TxAsChBusy = (1 << 1),
ChStaTimerEn = (1 << 0),
PbfLifeTimer = 0x1110,
BcnTimeCfg = 0x1114,
TsfInsCompShift = 24,
BcnTxEn = (1 << 20),
TbttTimerEn = (1 << 19),
TsfSyncModeShift = 17,
TsfSyncModeDis = 0,
TsfSyncModeSta = 1,
TsfSyncModeIbss = 2,
TsfSyncModeHostap = 3,
TsfTimerEn = (1 << 16),
BcnIntvalShift = 0,
TbttSyncCfg = 0x1118,
BcnCwminShift = 20,
BcnAifsnShift = 16,
BcnExpWinShift = 8,
TbttAdjustShift = 0,
TsfTimerDw0 = 0x111c,
TsfTimerDw1 = 0x1120,
TbttTimer = 0x1124,
IntTimerCfg = 0x1128,
GpTimerShift = 16,
PreTbttTimerShift = 0,
IntTimerEn = 0x112c,
GpTimerEn = (1 << 1),
PreTbttIntEn = (1 << 0),
ChIdleTime = 0x1130,
};
enum {
/* MAC Power Save configuration registers */
MacStatusReg = 0x1200,
RxStatusBusy = (1 << 1),
TxStatusBusy = (1 << 0),
PwrPinCfg = 0x1204,
IoAddaPd = (1 << 3),
IoPllPd = (1 << 2),
IoRaPe = (1 << 1),
IoRfPe = (1 << 0),
AutoWakeupCfg = 0x1208,
AutoWakeupEn = (1 << 15),
SleepTbttNumShift = 8,
WakeupLeadTimeShift = 0,
};
enum {
/* MAC TX configuration registers */
#define EdcaAcCfg(aci) (0x1300 + (aci) * 4)
EdcaTidAcMap = 0x1310,
#define TxPwrCfg(ridx) (0x1314 + (ridx) * 4)
TxPinCfg = 0x1328,
Rt3593LnaPeG2Pol = (1 << 31),
Rt3593LnaPeA2Pol = (1 << 30),
Rt3593LnaPeG2En = (1 << 29),
Rt3593LnaPeA2En = (1 << 28),
Rt3593LnaPe2En = (Rt3593LnaPeA2En | Rt3593LnaPeG2En),
Rt3593PaPeG2Pol = (1 << 27),
Rt3593PaPeA2Pol = (1 << 26),
Rt3593PaPeG2En = (1 << 25),
Rt3593PaPeA2En = (1 << 24),
TrswPol = (1 << 19),
TrswEn = (1 << 18),
RftrPol = (1 << 17),
RftrEn = (1 << 16),
LnaPeG1Pol = (1 << 15),
LnaPeA1Pol = (1 << 14),
LnaPeG0Pol = (1 << 13),
LnaPeA0Pol = (1 << 12),
LnaPeG1En = (1 << 11),
LnaPeA1En = (1 << 10),
LnaPe1En = (LnaPeA1En | LnaPeG1En),
LnaPeG0En = (1 << 9),
LnaPeA0En = (1 << 8),
LnaPe0En = (LnaPeA0En | LnaPeG0En),
PaPeG1Pol = (1 << 7),
PaPeA1Pol = (1 << 6),
PaPeG0Pol = (1 << 5),
PaPeA0Pol = (1 << 4),
PaPeG1En = (1 << 3),
PaPeA1En = (1 << 2),
PaPeG0En = (1 << 1),
PaPeA0En = (1 << 0),
TxBandCfg = 0x132c,
Tx5gBandSelN = (1 << 2),
Tx5gBandSelP = (1 << 1),
TxBandSel = (1 << 0),
TxSwCfg0 = 0x1330,
DlyRftrEnShift = 24,
DlyTrswEnShift = 16,
DlyPapeEnShift = 8,
DlyTxpeEnShift = 0,
TxSwCfg1 = 0x1334,
DlyRftrDisShift = 16,
DlyTrswDisShift = 8,
DlyPapeDisShift = 0,
TxSwCfg2 = 0x1338,
DlyLnaEnShift = 24,
DlyLnaDisShift = 16,
DlyDacEnShift = 8,
DlyDacDisShift = 0,
TxopThresCfg = 0x133c,
TxopRemThresShift = 24,
CfEndThresShift = 16,
RdgInThres = 8,
RdgOutThres = 0,
TxopCtrlCfg = 0x1340,
ExtCwMinShift = 16,
ExtCcaDlyShift = 8,
ExtCcaEn = (1 << 7),
LsigTxopEn = (1 << 6),
TxopTrunEnMimops = (1 << 4),
TxopTrunEnTxop = (1 << 3),
TxopTrunEnRate = (1 << 2),
TxopTrunEnAc = (1 << 1),
TxopTrunEnTimeout = (1 << 0),
TxRtsCfg = 0x1344,
RtsFbkEn = (1 << 24),
RtsThresShift = 8,
RtsRtyLimitShift = 0,
TxTimeoutCfg = 0x1348,
TxopTimeoutShift = 16,
RxAckTimeoutShift = 8,
MpduLifeTimeShift = 4,
TxRtyCfg = 0x134c,
TxAutofbEn = (1 << 30),
AggRtyModeTimer = (1 << 29),
NagRtyModeTimer = (1 << 28),
LongRtyThresShift = 16,
LongRtyLimitShift = 8,
ShortRtyLimitShift = 0,
TxLinkCfg = 0x1350,
RemoteMfsShift = 24,
RemoteMfbShift = 16,
TxCfackEn = (1 << 12),
TxRdgEn = (1 << 11),
TxMrqEn = (1 << 10),
RemoteUmfsEn = (1 << 9),
TxMfbEn = (1 << 8),
RemoteMfbLtShift = 0,
HtFbkCfg0 = 0x1354,
HtFbkCfg1 = 0x1358,
LgFbkCfg0 = 0x135c,
LgFbkCfg1 = 0x1360,
CckProtCfg = 0x1364,
/* possible flags for registers *ProtCfg */
RtsthEn = (1 << 26),
TxopAllowGf40 = (1 << 25),
TxopAllowGf20 = (1 << 24),
TxopAllowMm40 = (1 << 23),
TxopAllowMm20 = (1 << 22),
TxopAllowOfdm = (1 << 21),
TxopAllowCck = (1 << 20),
TxopAllowAll = (0x3f << 20),
ProtNavShort = (1 << 18),
ProtNavLong = (2 << 18),
ProtCtrlRtsCts = (1 << 16),
ProtCtrlCts = (2 << 16),
OfdmProtCfg = 0x1368,
Mm20ProtCfg = 0x136c,
Mm40ProtCfg = 0x1370,
Gf20ProtCfg = 0x1374,
Gf40ProtCfg = 0x1378,
ExpCtsTime = 0x137c,
/* possible flags for registers EXP_{CTS,ACK}_TIME */
ExpOfdmTimeShift = 16,
ExpCckTimeShift = 0,
ExpAckTime = 0x1380,
};
enum {
/* MAC RX configuration registers */
RxFiltrCfg = 0x1400,
DropCtrlRsv = (1 << 16),
DropBar = (1 << 15),
DropBa = (1 << 14),
DropPspoll = (1 << 13),
DropRts = (1 << 12),
DropCts = (1 << 11),
DropAck = (1 << 10),
DropCfend = (1 << 9),
DropCfack = (1 << 8),
DropDupl = (1 << 7),
DropBc = (1 << 6),
DropMc = (1 << 5),
DropVerErr = (1 << 4),
DropNotMybss = (1 << 3),
DropUcNome = (1 << 2),
DropPhyErr = (1 << 1),
DropCrcErr = (1 << 0),
AutoRspCfg = 0x1404,
CtrlPwrBit = (1 << 7),
BacAckPolicy = (1 << 6),
CckShortEn = (1 << 4),
Cts40mRefEn = (1 << 3),
Cts40mModeEn = (1 << 2),
BacAckpolicyEn = (1 << 1),
AutoRspEn = (1 << 0),
LegacyBasicRate = 0x1408,
HtBasicRate = 0x140c,
HtCtrlCfg = 0x1410,
SifsCostCfg = 0x1414,
OfdmSifsCostShift = 8,
CckSifsCostShift = 0,
RxParserCfg = 0x1418,
};
enum {
/* MAC Security configuration registers */
TxSecCnt0 = 0x1500,
RxSecCnt0 = 0x1504,
CcmpFcMute = 0x1508,
};
enum {
/* MAC HCCA/PSMP configuration registers */
TxopHldrAddr0 = 0x1600,
TxopHldrAddr1 = 0x1604,
TxopHldrEt = 0x1608,
TxopEtm1En = (1 << 25),
TxopEtm0En = (1 << 24),
TxopEtmThresShift = 16,
TxopEtoEn = (1 << 8),
TxopEtoThresShift = 1,
PerRxRstEn = (1 << 0),
QosCfpollRaDw0 = 0x160c,
QosCfpollA1Dw1 = 0x1610,
QosCfpollQc = 0x1614,
};
enum {
/* MAC Statistics Counters */
RxStaCnt0 = 0x1700,
RxStaCnt1 = 0x1704,
RxStaCnt2 = 0x1708,
TxStaCnt0 = 0x170c,
TxStaCnt1 = 0x1710,
TxStaCnt2 = 0x1714,
TxStatFifo = 0x1718,
TxqMcsShift = 16,
TxqWcidShift = 8,
TxqAckreq = (1 << 7),
TxqAgg = (1 << 6),
TxqOk = (1 << 5),
TxqPidShift = 1,
TxqVld = (1 << 0),
};
/* RX WCID search table */
#define WcidEntry(wcid) (0x1800 + (wcid) * 8)
enum {
FwBase = 0x2000,
Rt2870FwBase = 0x3000,
};
/* Pair-wise key table */
#define Pkey(wcid) (0x4000 + (wcid) * 32)
/* IV/EIV table */
#define Iveiv(wcid) (0x6000 + (wcid) * 8)
/* WCID attribute table */
#define WcidAttr(wcid) (0x6800 + (wcid) * 4)
/* possible flags for register WCID_ATTR */
enum {
ModeNosec = 0,
ModeWep40 = 1,
ModeWep104 = 2,
ModeTkip = 3,
ModeAesCcmp = 4,
ModeCkip40 = 5,
ModeCkip104 = 6,
ModeCkip128 = 7,
RxPkeyEn = (1 << 0),
};
/* Shared Key Table */
#define Skey(vap, kidx) (0x6c00 + (vap) * 128 + (kidx) * 32)
/* Shared Key Mode */
enum {
SkeyMode07 = 0x7000,
SkeyMode815 = 0x7004,
SkeyMode1623 = 0x7008,
SkeyMode2431 = 0x700c,
};
enum {
/* Shared Memory between MCU and host */
H2mMailbox = 0x7010,
H2mBusy = (1 << 24),
TokenNoIntr = 0xff,
H2mMailboxCid = 0x7014,
H2mMailboxStatus = 0x701c,
H2mBbpagent = 0x7028,
#define BcnBase(vap) (0x7800 + (vap) * 512)
};
/*
* RT2860 TX descriptor
* --------------------
* u32int sdp0 Segment Data Pointer 0
* u16int sdl1 Segment Data Length 1
* u16int sdl0 Segment Data Length 0
* u32int sdp1 Segment Data Pointer 1
* u8int reserved[3]
* u8int flags
*/
enum {
/* sdl1 flags */
TxBurst = (1 << 15),
TxLs1 = (1 << 14) /* SDP1 is the last segment */,
/* sdl0 flags */
TxDdone = (1 << 15),
TxLs0 = (1 << 14) /* SDP0 is the last segment */,
/* flags */
TxQselShift = 1,
TxQselMgmt = (0 << 1),
TxQselHcca = (1 << 1),
TxQselEdca = (2 << 1),
TxWiv = (1 << 0),
};
/*
* TX Wireless Information
* -----------------------
* u8int flags
* u8int txop
* u16int phy
* u8int xflags
* u8int wcid Wireless Client ID
* u16int len
* u32int iv
* u32int eiv
*/
enum {
/* flags */
TxMpduDsityShift = 5,
TxAmpdu = (1 << 4),
TxTs = (1 << 3),
TxCfack = (1 << 2),
TxMmps = (1 << 1),
TxFrag = (1 << 0),
/* txop */
TxTxopHt = 0,
TxTxopPifs = 1,
TxTxopSifs = 2,
TxTxopBackoff = 3,
/* phy */
PhyMode = 0xc000,
PhyCck = (0 << 14),
PhyOfdm = (1 << 14),
PhyHt = (2 << 14),
PhyHtGf = (3 << 14),
PhySgi = (1 << 8),
PhyBw40 = (1 << 7),
PhyMcs = 0x7f,
PhyShpre = (1 << 3),
/* xflags */
TxBawinsizeShift = 2,
TxNseq = (1 << 1),
TxAck = (1 << 0),
/* len */
TxPidShift = 12,
};
/*
* RT2860 RX descriptor
* --------------------
* u32int sdp0
* u16int sdl1 unused
* u16int sdl0
* u32int sdp1 unused
* u32int flags
*/
enum {
/* sdl flags */
RxDdone = (1 << 15),
RxLs0 = (1 << 14),
/* flags */
RxDec = (1 << 16),
RxAmpdu = (1 << 15),
RxL2pad = (1 << 14),
RxRssi = (1 << 13),
RxHtc = (1 << 12),
RxAmsdu = (1 << 11),
RxMicerr = (1 << 10),
RxIcverr = (1 << 9),
RxCrcerr = (1 << 8),
RxMybss = (1 << 7),
RxBc = (1 << 6),
RxMc = (1 << 5),
RxUc2me = (1 << 4),
RxFrag = (1 << 3),
RxNull = (1 << 2),
RxData = (1 << 1),
RxBa = (1 << 0),
};
/*
* RX Wireless Information
* -----------------------
* u8int wcid
* u8int keyidx
* u16int len
* u16int seq
* u16int phy
* u8int rssi[3]
* u8int reserved1
* u8int snr[2]
* u16int reserved2
*/
enum {
/* keyidx flags */
RxUdfShift = 5,
RxBssIdxShift = 2,
/* len flags */
RxTidShift = 12,
};
enum {
WIFIHDRSIZE = 2+2+3*6+2,
Rdscsize = 16,
Tdscsize = 16,
Rbufsize = 4096,
Tbufsize = 4096,
Rxwisize = 16,
Txwisize = 16,
/* first DMA segment contains TXWI + 802.11 header + 32-bit padding */
TxwiDmaSz = Txwisize + WIFIHDRSIZE + 2
};
/* RF registers */
enum {
Rf1 = 0,
Rf2 = 2,
Rf3 = 1,
Rf4 = 3,
};
enum {
Rf2820 = 1 /* 2T3R */,
Rf2850 = 2 /* dual-band 2T3R */,
Rf2720 = 3 /* 1T2R */,
Rf2750 = 4 /* dual-band 1T2R */,
Rf3020 = 5 /* 1T1R */,
Rf2020 = 6 /* b/g */,
Rf3021 = 7 /* 1T2R */,
Rf3022 = 8 /* 2T2R */,
Rf3052 = 9 /* dual-band 2T2R */,
Rf3320 = 11 /* 1T1R */,
Rf3053 = 13 /* dual-band 3T3R */,
};
enum {
Rt3070RfBlock = (1 << 0),
Rt3070Rx0Pd = (1 << 2),
Rt3070Tx0Pd = (1 << 3),
Rt3070Rx1Pd = (1 << 4),
Rt3070Tx1Pd = (1 << 5),
Rt3070Rx2Pd = (1 << 6),
Rt3070Tx2Pd = (1 << 7),
Rt3070Tune = (1 << 0),
Rt3070TxLo2 = (1 << 3),
Rt3070TxLo1 = (1 << 3),
Rt3070RxLo1 = (1 << 3),
Rt3070RxLo2 = (1 << 3),
Rt3070RxCtb = (1 << 7),
Rt3070BbLoopback = (1 << 0),
Rt3593Vco = (1 << 0),
Rt3593Rescal = (1 << 7),
Rt3593Vcocal = (1 << 7),
Rt3593VcoIc = (1 << 6),
Rt3593LdoPllVcMask = 0x0e,
Rt3593LdoRfVcMask = 0xe0,
Rt3593CpIcMask = 0xe0,
Rt3593CpIcShift = 5,
Rt3593RxCtb = (1 << 5)
};
static const char* rfnames[] = {
[Rf2820] "RT2820",
[Rf2850] "RT2850",
[Rf2720] "RT2720",
[Rf2750] "RT2750",
[Rf3020] "RT3020",
[Rf2020] "RT2020",
[Rf3021] "RT3021",
[Rf3022] "RT3022",
[Rf3052] "RT3052",
[Rf3320] "RT3320",
[Rf3053] "RT3053",
};
enum {
/* USB commands, RT2870 only */
Rt2870Reset = 1,
Rt2870Write2 = 2,
Rt2870WriteRegion1 = 6,
Rt2870ReadRegion1 = 7,
Rt2870EepromRead = 9,
};
enum {
EepromDelay = 1 /* minimum hold time (microsecond) */,
EepromVersion = 0x01,
EepromMac01 = 0x02,
EepromMac23 = 0x03,
EepromMac45 = 0x04,
EepromPciePslevel = 0x11,
EepromRev = 0x12,
EepromAntenna = 0x1a,
EepromConfig = 0x1b,
EepromCountry = 0x1c,
EepromFreqLeds = 0x1d,
EepromLed1 = 0x1e,
EepromLed2 = 0x1f,
EepromLed3 = 0x20,
EepromLna = 0x22,
EepromRssi12ghz = 0x23,
EepromRssi22ghz = 0x24,
EepromRssi15ghz = 0x25,
EepromRssi25ghz = 0x26,
EepromDeltapwr = 0x28,
EepromPwr2ghzBase1 = 0x29,
EepromPwr2ghzBase2 = 0x30,
EepromTssi12ghz = 0x37,
EepromTssi22ghz = 0x38,
EepromTssi32ghz = 0x39,
EepromTssi42ghz = 0x3a,
EepromTssi52ghz = 0x3b,
EepromPwr5ghzBase1 = 0x3c,
EepromPwr5ghzBase2 = 0x53,
EepromTssi15ghz = 0x6a,
EepromTssi25ghz = 0x6b,
EepromTssi35ghz = 0x6c,
EepromTssi45ghz = 0x6d,
EepromTssi55ghz = 0x6e,
EepromRpwr = 0x6f,
EepromBbpBase = 0x78,
Rt3071EepromRfBase = 0x82,
};
enum {
RidxCck1 = 0,
RidxCck11 = 3,
RidxOfdm6 = 4,
RidxMax = 11,
};
/* ring and pool count */
enum {
Nrx = 128,
Ntx = 64,
Ntxpool = Ntx * 2
};
typedef struct FWImage FWImage;
typedef struct TXQ TXQ;
typedef struct RXQ RXQ;
typedef struct Pool Pool;
typedef struct Ctlr Ctlr;
struct FWImage {
uint size;
uchar *data;
};
struct TXQ
{
uint n; /* next */
uint i; /* current */
Block **b;
u32int *d; /* descriptors */
Rendez;
QLock;
};
struct RXQ
{
uint i;
Block **b;
u32int *p;
};
struct Pool
{
uint i; /* current */
uchar *p; /* txwi */
};
struct Ctlr {
Lock;
QLock;
Ctlr *link;
Pcidev *pdev;
Wifi *wifi;
u16int mac_ver;
u16int mac_rev;
u8int rf_rev;
u8int freq;
u8int ntxchains;
u8int nrxchains;
u8int pslevel;
s8int txpow1[54];
s8int txpow2[54];
s8int rssi_2ghz[3];
s8int rssi_5ghz[3];
u8int lna[4];
u8int rf24_20mhz;
u8int rf24_40mhz;
u8int patch_dac;
u8int rfswitch;
u8int ext_2ghz_lna;
u8int ext_5ghz_lna;
u8int calib_2ghz;
u8int calib_5ghz;
u8int txmixgain_2ghz;
u8int txmixgain_5ghz;
u8int tssi_2ghz[9];
u8int tssi_5ghz[9];
u8int step_2ghz;
u8int step_5ghz;
uint mgtqid;
struct {
u8int reg;
u8int val;
} bbp[8], rf[10];
u8int leds;
u16int led[3];
u32int txpow20mhz[5];
u32int txpow40mhz_2ghz[5];
u32int txpow40mhz_5ghz[5];
int flags;
int port;
int power;
int active;
int broken;
int attached;
u32int *nic;
/* assigned node ids in hardware node table or -1 if unassigned */
int bcastnodeid;
int bssnodeid;
u8int wcid;
/* current receiver settings */
uchar bssid[Eaddrlen];
int channel;
int prom;
int aid;
RXQ rx;
TXQ tx[6];
Pool pool;
FWImage *fw;
};
/* controller flags */
enum {
AdvancedPs = 1 << 0,
ConnPciE = 1 << 1,
};
static const struct rt2860_rate {
u8int rate;
u8int mcs;
/*enum ieee80211_phytype phy;*/
u8int ctl_ridx;
u16int sp_ack_dur;
u16int lp_ack_dur;
} rt2860_rates[] = {
{ 2, 0,/* IEEE80211_T_DS,*/ 0, 314, 314 },
{ 4, 1,/* IEEE80211_T_DS,*/ 1, 258, 162 },
{ 11, 2,/* IEEE80211_T_DS,*/ 2, 223, 127 },
{ 22, 3,/* IEEE80211_T_DS,*/ 3, 213, 117 },
{ 12, 0,/* IEEE80211_T_OFDM,*/ 4, 60, 60 },
{ 18, 1,/* IEEE80211_T_OFDM,*/ 4, 52, 52 },
{ 24, 2,/* IEEE80211_T_OFDM,*/ 6, 48, 48 },
{ 36, 3,/* IEEE80211_T_OFDM,*/ 6, 44, 44 },
{ 48, 4,/* IEEE80211_T_OFDM,*/ 8, 44, 44 },
{ 72, 5,/* IEEE80211_T_OFDM,*/ 8, 40, 40 },
{ 96, 6,/* IEEE80211_T_OFDM,*/ 8, 40, 40 },
{ 108, 7,/* IEEE80211_T_OFDM,*/ 8, 40, 40 }
};
/*
* Default values for MAC registers; values taken from the reference driver.
*/
static const struct {
u32int reg;
u32int val;
} rt2860_def_mac[] = {
{ BcnOffset0, 0xf8f0e8e0 },
{ LegacyBasicRate, 0x0000013f },
{ HtBasicRate, 0x00008003 },
{ MacSysCtrl, 0x00000000 },
{ BkoffSlotCfg, 0x00000209 },
{ TxSwCfg0, 0x00000000 },
{ TxSwCfg1, 0x00080606 },
{ TxLinkCfg, 0x00001020 },
{ TxTimeoutCfg, 0x000a2090 },
{ LedCfg, 0x7f031e46 },
{ WmmAifsnCfg, 0x00002273 },
{ WmmCwminCfg, 0x00002344 },
{ WmmCwmaxCfg, 0x000034aa },
{ MaxPcnt, 0x1f3fbf9f },
{ TxRtyCfg, 0x47d01f0f },
{ AutoRspCfg, 0x00000013 },
{ CckProtCfg, 0x05740003 },
{ OfdmProtCfg, 0x05740003 },
{ Gf20ProtCfg, 0x01744004 },
{ Gf40ProtCfg, 0x03f44084 },
{ Mm20ProtCfg, 0x01744004 },
{ Mm40ProtCfg, 0x03f54084 },
{ TxopCtrlCfg, 0x0000583f },
{ TxopHldrEt, 0x00000002 },
{ TxRtsCfg, 0x00092b20 },
{ ExpAckTime, 0x002400ca },
{ XifsTimeCfg, 0x33a41010 },
{ PwrPinCfg, 0x00000003 },
};
/*
* Default values for BBP registers; values taken from the reference driver.
*/
static const struct {
u8int reg;
u8int val;
} rt2860_def_bbp[] = {
{ 65, 0x2c },
{ 66, 0x38 },
{ 69, 0x12 },
{ 70, 0x0a },
{ 73, 0x10 },
{ 81, 0x37 },
{ 82, 0x62 },
{ 83, 0x6a },
{ 84, 0x99 },
{ 86, 0x00 },
{ 91, 0x04 },
{ 92, 0x00 },
{ 103, 0x00 },
{ 105, 0x05 },
{ 106, 0x35 },
};
/*
* Default settings for RF registers; values derived from the reference driver.
*/
static const struct rfprog {
u8int chan;
u32int r1, r2, r3, r4;
} rt2860_rf2850[] = {
{ 1, 0x100bb3, 0x1301e1, 0x05a014, 0x001402 },
{ 2, 0x100bb3, 0x1301e1, 0x05a014, 0x001407 },
{ 3, 0x100bb3, 0x1301e2, 0x05a014, 0x001402 },
{ 4, 0x100bb3, 0x1301e2, 0x05a014, 0x001407 },
{ 5, 0x100bb3, 0x1301e3, 0x05a014, 0x001402 },
{ 6, 0x100bb3, 0x1301e3, 0x05a014, 0x001407 },
{ 7, 0x100bb3, 0x1301e4, 0x05a014, 0x001402 },
{ 8, 0x100bb3, 0x1301e4, 0x05a014, 0x001407 },
{ 9, 0x100bb3, 0x1301e5, 0x05a014, 0x001402 },
{ 10, 0x100bb3, 0x1301e5, 0x05a014, 0x001407 },
{ 11, 0x100bb3, 0x1301e6, 0x05a014, 0x001402 },
{ 12, 0x100bb3, 0x1301e6, 0x05a014, 0x001407 },
{ 13, 0x100bb3, 0x1301e7, 0x05a014, 0x001402 },
{ 14, 0x100bb3, 0x1301e8, 0x05a014, 0x001404 },
{ 36, 0x100bb3, 0x130266, 0x056014, 0x001408 },
{ 38, 0x100bb3, 0x130267, 0x056014, 0x001404 },
{ 40, 0x100bb2, 0x1301a0, 0x056014, 0x001400 },
{ 44, 0x100bb2, 0x1301a0, 0x056014, 0x001408 },
{ 46, 0x100bb2, 0x1301a1, 0x056014, 0x001402 },
{ 48, 0x100bb2, 0x1301a1, 0x056014, 0x001406 },
{ 52, 0x100bb2, 0x1301a2, 0x056014, 0x001404 },
{ 54, 0x100bb2, 0x1301a2, 0x056014, 0x001408 },
{ 56, 0x100bb2, 0x1301a3, 0x056014, 0x001402 },
{ 60, 0x100bb2, 0x1301a4, 0x056014, 0x001400 },
{ 62, 0x100bb2, 0x1301a4, 0x056014, 0x001404 },
{ 64, 0x100bb2, 0x1301a4, 0x056014, 0x001408 },
{ 100, 0x100bb2, 0x1301ac, 0x05e014, 0x001400 },
{ 102, 0x100bb2, 0x1701ac, 0x15e014, 0x001404 },
{ 104, 0x100bb2, 0x1701ac, 0x15e014, 0x001408 },
{ 108, 0x100bb3, 0x17028c, 0x15e014, 0x001404 },
{ 110, 0x100bb3, 0x13028d, 0x05e014, 0x001400 },
{ 112, 0x100bb3, 0x13028d, 0x05e014, 0x001406 },
{ 116, 0x100bb3, 0x13028e, 0x05e014, 0x001408 },
{ 118, 0x100bb3, 0x13028f, 0x05e014, 0x001404 },
{ 120, 0x100bb1, 0x1300e0, 0x05e014, 0x001400 },
{ 124, 0x100bb1, 0x1300e0, 0x05e014, 0x001404 },
{ 126, 0x100bb1, 0x1300e0, 0x05e014, 0x001406 },
{ 128, 0x100bb1, 0x1300e0, 0x05e014, 0x001408 },
{ 132, 0x100bb1, 0x1300e1, 0x05e014, 0x001402 },
{ 134, 0x100bb1, 0x1300e1, 0x05e014, 0x001404 },
{ 136, 0x100bb1, 0x1300e1, 0x05e014, 0x001406 },
{ 140, 0x100bb1, 0x1300e2, 0x05e014, 0x001400 },
{ 149, 0x100bb1, 0x1300e2, 0x05e014, 0x001409 },
{ 151, 0x100bb1, 0x1300e3, 0x05e014, 0x001401 },
{ 153, 0x100bb1, 0x1300e3, 0x05e014, 0x001403 },
{ 157, 0x100bb1, 0x1300e3, 0x05e014, 0x001407 },
{ 159, 0x100bb1, 0x1300e3, 0x05e014, 0x001409 },
{ 161, 0x100bb1, 0x1300e4, 0x05e014, 0x001401 },
{ 165, 0x100bb1, 0x1300e4, 0x05e014, 0x001405 },
{ 167, 0x100bb1, 0x1300f4, 0x05e014, 0x001407 },
{ 169, 0x100bb1, 0x1300f4, 0x05e014, 0x001409 },
{ 171, 0x100bb1, 0x1300f5, 0x05e014, 0x001401 },
{ 173, 0x100bb1, 0x1300f5, 0x05e014, 0x001403 },
};
struct {
u8int n;
u8int r;
u8int k;
} rt3090_freqs[] = {
{ 0xf1, 2, 2 },
{ 0xf1, 2, 7 },
{ 0xf2, 2, 2 },
{ 0xf2, 2, 7 },
{ 0xf3, 2, 2 },
{ 0xf3, 2, 7 },
{ 0xf4, 2, 2 },
{ 0xf4, 2, 7 },
{ 0xf5, 2, 2 },
{ 0xf5, 2, 7 },
{ 0xf6, 2, 2 },
{ 0xf6, 2, 7 },
{ 0xf7, 2, 2 },
{ 0xf8, 2, 4 },
{ 0x56, 0, 4 },
{ 0x56, 0, 6 },
{ 0x56, 0, 8 },
{ 0x57, 0, 0 },
{ 0x57, 0, 2 },
{ 0x57, 0, 4 },
{ 0x57, 0, 8 },
{ 0x57, 0, 10 },
{ 0x58, 0, 0 },
{ 0x58, 0, 4 },
{ 0x58, 0, 6 },
{ 0x58, 0, 8 },
{ 0x5b, 0, 8 },
{ 0x5b, 0, 10 },
{ 0x5c, 0, 0 },
{ 0x5c, 0, 4 },
{ 0x5c, 0, 6 },
{ 0x5c, 0, 8 },
{ 0x5d, 0, 0 },
{ 0x5d, 0, 2 },
{ 0x5d, 0, 4 },
{ 0x5d, 0, 8 },
{ 0x5d, 0, 10 },
{ 0x5e, 0, 0 },
{ 0x5e, 0, 4 },
{ 0x5e, 0, 6 },
{ 0x5e, 0, 8 },
{ 0x5f, 0, 0 },
{ 0x5f, 0, 9 },
{ 0x5f, 0, 11 },
{ 0x60, 0, 1 },
{ 0x60, 0, 5 },
{ 0x60, 0, 7 },
{ 0x60, 0, 9 },
{ 0x61, 0, 1 },
{ 0x61, 0, 3 },
{ 0x61, 0, 5 },
{ 0x61, 0, 7 },
{ 0x61, 0, 9 }
};
static const struct {
u8int reg;
u8int val;
} rt3090_def_rf[] = {
{ 4, 0x40 },
{ 5, 0x03 },
{ 6, 0x02 },
{ 7, 0x70 },
{ 9, 0x0f },
{ 10, 0x41 },
{ 11, 0x21 },
{ 12, 0x7b },
{ 14, 0x90 },
{ 15, 0x58 },
{ 16, 0xb3 },
{ 17, 0x92 },
{ 18, 0x2c },
{ 19, 0x02 },
{ 20, 0xba },
{ 21, 0xdb },
{ 24, 0x16 },
{ 25, 0x01 },
{ 29, 0x1f }
};
/* vendors */
enum {
Ralink = 0x1814,
Awt = 0x1a3b,
};
/* products */
enum {
RalinkRT2890 = 0x0681,
RalinkRT2790 = 0x0781,
RalinkRT3090 = 0x3090,
AwtRT2890 = 0x1059,
};
#define csr32r(c, r) (*((c)->nic+((r)/4)))
#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v))
static int rbplant(Ctlr*, int);
static void setchan(Ctlr*, uint);
static void rt3090setchan(Ctlr*, uint);
static void selchangroup(Ctlr*, int);
static void setleds(Ctlr*, u16int);
static uint
get16(uchar *p){
return *((u16int*)p);
}
static uint
get32(uchar *p){
return *((u32int*)p);
}
static void
put32(uchar *p, uint v){
*((u32int*)p) = v;
}
static void
put16(uchar *p, uint v){
*((u16int*)p) = v;
};
static void
memwrite(Ctlr *ctlr, u32int off, uchar *data, uint size){
memmove((uchar*)ctlr->nic + off, data, size);
}
static void
setregion(Ctlr *ctlr, u32int off, uint val, uint size){
memset((uchar*)ctlr->nic + off, val, size);
}
static long
rt2860ctl(Ether *edev, void *buf, long n)
{
Ctlr *ctlr;
ctlr = edev->ctlr;
if(ctlr->wifi)
return wifictl(ctlr->wifi, buf, n);
return 0;
}
static long
rt2860ifstat(Ether *edev, void *buf, long n, ulong off)
{
Ctlr *ctlr;
ctlr = edev->ctlr;
if(ctlr->wifi)
return wifistat(ctlr->wifi, buf, n, off);
return 0;
}
static void
setoptions(Ether *edev)
{
Ctlr *ctlr;
int i;
ctlr = edev->ctlr;
for(i = 0; i < edev->nopt; i++)
wificfg(ctlr->wifi, edev->opt[i]);
}
static void
rxon(Ether *edev, Wnode *bss)
{
u32int tmp;
Ctlr *ctlr;
int cap;
ctlr = edev->ctlr;
if(bss != nil){
cap = bss->cap;
ctlr->channel = bss->channel;
memmove(ctlr->bssid, bss->bssid, Eaddrlen);
ctlr->aid = bss->aid;
if(ctlr->aid != 0){
if(ctlr->wifi->debug)
print("new assoc!");
ctlr->bssnodeid = -1;
}else
ctlr->bcastnodeid = -1;
}else{
cap = 0;
memmove(ctlr->bssid, edev->bcast, Eaddrlen);
ctlr->aid = 0;
ctlr->bcastnodeid = -1;
ctlr->bssnodeid = -1;
}
if(ctlr->aid != 0)
setleds(ctlr, LedRadio | LedLink2ghz);
else
setleds(ctlr, LedRadio);
if(ctlr->wifi->debug)
print("#l%d: rxon: bssid %E, aid %x, channel %d wcid %d\n",
edev->ctlrno, ctlr->bssid, ctlr->aid, ctlr->channel, ctlr->wcid);
/* Set channel */
if(ctlr->mac_ver >= 0x3071)
rt3090setchan(ctlr, ctlr->channel);
else
setchan(ctlr, ctlr->channel);
selchangroup(ctlr, 0);
microdelay(1000);
/* enable mrr(?) */
#define CCK(mcs) (mcs)
#define OFDM(mcs) (1 << 3 | (mcs))
csr32w(ctlr, LgFbkCfg0,
OFDM(6) << 28 | /* 54->48 */
OFDM(5) << 24 | /* 48->36 */
OFDM(4) << 20 | /* 36->24 */
OFDM(3) << 16 | /* 24->18 */
OFDM(2) << 12 | /* 18->12 */
OFDM(1) << 8 | /* 12-> 9 */
OFDM(0) << 4 | /* 9-> 6 */
OFDM(0)); /* 6-> 6 */
csr32w(ctlr, LgFbkCfg1,
CCK(2) << 12 | /* 11->5.5 */
CCK(1) << 8 | /* 5.5-> 2 */
CCK(0) << 4 | /* 2-> 1 */
CCK(0)); /* 1-> 1 */
#undef OFDM
#undef CCK
/* update slot */
tmp = csr32r(ctlr, BkoffSlotCfg);
tmp &= ~0xff;
tmp |= (cap & (1<<10)) ? 9 : 20;
csr32w(ctlr, BkoffSlotCfg, tmp);
/* set TX preamble */
tmp = csr32r(ctlr, AutoRspCfg);
tmp &= ~CckShortEn;
if(cap & (1<<5)) tmp |= CckShortEn;
csr32w(ctlr, AutoRspCfg, tmp);
/* set basic rates */
csr32w(ctlr, LegacyBasicRate, 0x003); /* 11B */
/* Set BSSID */
csr32w(ctlr, MacBssidDw0,
ctlr->bssid[0] | ctlr->bssid[1] << 8 | ctlr->bssid[2] << 16 | ctlr->bssid[3] << 24);
csr32w(ctlr, MacBssidDw1,
ctlr->bssid[4] | ctlr->bssid[5] << 8);
if(ctlr->bcastnodeid == -1){
ctlr->bcastnodeid = 0xff;
memwrite(ctlr, WcidEntry(ctlr->bcastnodeid), edev->bcast, Eaddrlen);
}
if(ctlr->bssnodeid == -1 && bss != nil && ctlr->aid != 0){
ctlr->bssnodeid = 0;
memwrite(ctlr, WcidEntry(ctlr->bssnodeid), ctlr->bssid, Eaddrlen);
}
}
static void
rt2860promiscuous(void *arg, int on)
{
Ether *edev;
Ctlr *ctlr;
edev = arg;
ctlr = edev->ctlr;
if(ctlr->attached == 0)
return;
qlock(ctlr);
ctlr->prom = on;
rxon(edev, ctlr->wifi->bss);
qunlock(ctlr);
}
static void
rt2860multicast(void *, uchar*, int)
{
}
static FWImage*
readfirmware(void){
static char name[] = "ral-rt2860";
uchar dirbuf[sizeof(Dir)+100], *data;
char buf[128];
FWImage *fw;
int n, r;
Chan *c;
Dir d;
if(!iseve())
error(Eperm);
if(!waserror()){
snprint(buf, sizeof buf, "/boot/%s", name);
c = namec(buf, Aopen, OREAD, 0);
poperror();
}else{
snprint(buf, sizeof buf, "/lib/firmware/%s", name);
c = namec(buf, Aopen, OREAD, 0);
}
if(waserror()){
cclose(c);
nexterror();
}
n = devtab[c->type]->stat(c, dirbuf, sizeof dirbuf);
if(n <= 0)
error("can't stat firmware");
convM2D(dirbuf, n, &d, nil);
fw = malloc(sizeof(*fw));
fw->size = d.length;
data = fw->data = smalloc(d.length);
if(waserror()){
free(fw);
nexterror();
}
r = 0;
while(r < d.length){
n = devtab[c->type]->read(c, data+r, d.length-r, (vlong)r);
if(n <= 0)
break;
r += n;
}
poperror();
poperror();
cclose(c);
return fw;
}
static char*
boot(Ctlr *ctlr)
{
int ntries;
/* set "host program ram write selection" bit */
csr32w(ctlr, SysCtrl, HstPmSel);
/* write microcode image */
memwrite(ctlr, FwBase, ctlr->fw->data, ctlr->fw->size);
/* kick microcontroller unit */
csr32w(ctlr, SysCtrl, 0);
coherence();
csr32w(ctlr, SysCtrl, McuReset);
csr32w(ctlr, H2mBbpagent, 0);
csr32w(ctlr, H2mMailbox, 0);
/* wait until microcontroller is ready */
coherence();
for(ntries = 0; ntries < 1000; ntries++){
if(csr32r(ctlr, SysCtrl) & McuReady)
break;
microdelay(1000);
}
if(ntries == 1000)
return "timeout waiting for MCU to initialize";
return 0;
}
/*
* Send a command to the 8051 microcontroller unit.
*/
static int
mcucmd(Ctlr *ctlr, u8int cmd, u16int arg, int wait)
{
int slot, ntries;
u32int tmp;
u8int cid;
SET(slot);
for(ntries = 0; ntries < 100; ntries++){
if(!(csr32r(ctlr, H2mMailbox) & H2mBusy))
break;
microdelay(2);
}
if(ntries == 100)
return -1;
cid = wait ? cmd : TokenNoIntr;
csr32w(ctlr, H2mMailbox, H2mBusy | cid << 16 | arg);
coherence();
csr32w(ctlr, HostCmd, cmd);
if(!wait)
return 0;
/* wait for the command to complete */
for(ntries = 0; ntries < 200; ntries++){
tmp = csr32r(ctlr, H2mMailboxCid);
/* find the command slot */
for(slot = 0; slot < 4; slot++, tmp >>= 8)
if((tmp & 0xff) == cid)
break;
if(slot < 4)
break;
microdelay(100);
}
if(ntries == 200){
/* clear command and status */
csr32w(ctlr, H2mMailboxStatus, 0xffffffff);
csr32w(ctlr, H2mMailboxCid, 0xffffffff);
return -1;
}
/* get command status (1 means success) */
tmp = csr32r(ctlr, H2mMailboxStatus);
tmp = (tmp >> (slot * 8)) & 0xff;
/* clear command and status */
csr32w(ctlr, H2mMailboxStatus, 0xffffffff);
csr32w(ctlr, H2mMailboxCid, 0xffffffff);
return (tmp == 1) ? 0 : -1;
}
/*
* Reading and writing from/to the BBP is different from RT2560 and RT2661.
* We access the BBP through the 8051 microcontroller unit which means that
* the microcode must be loaded first.
*/
static void
bbpwrite(Ctlr *ctlr, u8int reg, u8int val)
{
int ntries;
for(ntries = 0; ntries < 100; ntries++){
if(!(csr32r(ctlr, H2mBbpagent) & BbpCsrKick))
break;
microdelay(1);
}
if(ntries == 100){
print("could not write to BBP through MCU\n");
return;
}
csr32w(ctlr, H2mBbpagent, BbpRwParallel |
BbpCsrKick | reg << 8 | val);
coherence();
mcucmd(ctlr, McuCmdBbp, 0, 0);
microdelay(1000);
}
static u8int
bbpread(Ctlr *ctlr, u8int reg)
{
u32int val;
int ntries;
for(ntries = 0; ntries < 100; ntries++){
if(!(csr32r(ctlr, H2mBbpagent) & BbpCsrKick))
break;
microdelay(1);
}
if(ntries == 100){
print("could not read from BBP through MCU");
return 0;
}
csr32w(ctlr, H2mBbpagent, BbpRwParallel |
BbpCsrKick | BbpCsrRead | reg << 8);
coherence();
mcucmd(ctlr, McuCmdBbp, 0, 0);
microdelay(1000);
for(ntries = 0; ntries < 100; ntries++){
val = csr32r(ctlr, H2mBbpagent);
if(!(val & BbpCsrKick))
return val & 0xff;
microdelay(1);
}
print("could not read from BBP through MCU\n");
return 0;
}
static char*
bbpinit(Ctlr *ctlr)
{
int i, ntries;
char *err;
/* wait for BBP to wake up */
for(ntries = 0; ntries < 20; ntries++){
u8int bbp0 = bbpread(ctlr, 0);
if(bbp0 != 0 && bbp0 != 0xff)
break;
}
if(ntries == 20){
err = "timeout waiting for BBP to wake up";
return err;
}
/* initialize BBP registers to default values */
for(i = 0; i < nelem(rt2860_def_bbp); i++){
bbpwrite(ctlr, rt2860_def_bbp[i].reg,
rt2860_def_bbp[i].val);
}
/* fix BBP84 for RT2860E */
if(ctlr->mac_ver == 0x2860 && ctlr->mac_rev != 0x0101)
bbpwrite(ctlr, 84, 0x19);
if(ctlr->mac_ver >= 0x3071){
bbpwrite(ctlr, 79, 0x13);
bbpwrite(ctlr, 80, 0x05);
bbpwrite(ctlr, 81, 0x33);
}else if(ctlr->mac_ver == 0x2860 && ctlr->mac_rev == 0x0100){
bbpwrite(ctlr, 69, 0x16);
bbpwrite(ctlr, 73, 0x12);
}
return nil;
}
static void
setleds(Ctlr *ctlr, u16int which)
{
mcucmd(ctlr, McuCmdLeds,
which | (ctlr->leds & 0x7f), 0);
}
static char*
txrxon(Ctlr *ctlr)
{
u32int tmp;
int ntries;
char *err;
SET(tmp);
/* enable Tx/Rx DMA engine */
csr32w(ctlr, MacSysCtrl, MacTxEn);
coherence();
for(ntries = 0; ntries < 200; ntries++){
tmp = csr32r(ctlr, WpdmaGloCfg);
if((tmp & (TxDmaBusy | RxDmaBusy)) == 0)
break;
microdelay(1000);
}
if(ntries == 200){
err = "timeout waiting for DMA engine";
return err;
}
microdelay(50);
tmp |= RxDmaEn | TxDmaEn |
WpdmaBtSize64 << WpdmaBtSizeShift;
csr32w(ctlr, WpdmaGloCfg, tmp);
/* set Rx filter */
tmp = DropCrcErr | DropPhyErr;
if(!ctlr->prom){
tmp |= DropUcNome | DropDupl |
DropCts | DropBa | DropAck |
DropVerErr | DropCtrlRsv |
DropCfack | DropCfend;
tmp |= DropRts | DropPspoll;
}
csr32w(ctlr, RxFiltrCfg, tmp);
csr32w(ctlr, MacSysCtrl, MacRxEn | MacTxEn);
return 0;
}
/*
* Write to one of the 4 programmable 24-bit RF registers.
*/
static void
rfwrite(Ctlr *ctlr, u8int reg, u32int val)
{
u32int tmp;
int ntries;
for(ntries = 0; ntries < 100; ntries++){
if(!(csr32r(ctlr, RfCsrCfg0) & RfRegCtrl))
break;
microdelay(1);
}
if(ntries == 100){
print("could not write to RF\n");
return;
}
/* RF registers are 24-bit on the RT2860 */
tmp = RfRegCtrl | 24 << RfRegWidthShift |
(val & 0x3fffff) << 2 | (reg & 3);
csr32w(ctlr, RfCsrCfg0, tmp);
}
u8int
rt3090rfread(Ctlr *ctlr, u8int reg)
{
u32int tmp;
int ntries;
for(ntries = 0; ntries < 100; ntries++){
if(!(csr32r(ctlr, Rt3070RfCsrCfg) & Rt3070RfKick))
break;
microdelay(1);
}
if(ntries == 100){
print("could not read RF register\n");
return 0xff;
}
tmp = Rt3070RfKick | reg << 8;
csr32w(ctlr, Rt3070RfCsrCfg, tmp);
for(ntries = 0; ntries < 100; ntries++){
tmp = csr32r(ctlr, Rt3070RfCsrCfg);
if(!(tmp & Rt3070RfKick))
break;
microdelay(1);
}
if(ntries == 100){
print("could not read RF register\n");
return 0xff;
}
return tmp & 0xff;
}
static void
rt3090rfwrite(Ctlr *ctlr, u8int reg, u8int val)
{
u32int tmp;
int ntries;
for(ntries = 0; ntries < 10; ntries++){
if(!(csr32r(ctlr, Rt3070RfCsrCfg) & Rt3070RfKick))
break;
microdelay(10);
}
if(ntries == 10){
print("could not write to RF\n");
return;
}
tmp = Rt3070RfWrite | Rt3070RfKick | reg << 8 | val;
csr32w(ctlr, Rt3070RfCsrCfg, tmp);
}
static void
selchangroup(Ctlr *ctlr, int group)
{
u32int tmp;
u8int agc;
bbpwrite(ctlr, 62, 0x37 - ctlr->lna[group]);
bbpwrite(ctlr, 63, 0x37 - ctlr->lna[group]);
bbpwrite(ctlr, 64, 0x37 - ctlr->lna[group]);
bbpwrite(ctlr, 86, 0x00);
if(group == 0){
if(ctlr->ext_2ghz_lna){
bbpwrite(ctlr, 82, 0x62);
bbpwrite(ctlr, 75, 0x46);
}else{
bbpwrite(ctlr, 82, 0x84);
bbpwrite(ctlr, 75, 0x50);
}
}else{
if(ctlr->ext_5ghz_lna){
bbpwrite(ctlr, 82, 0xf2);
bbpwrite(ctlr, 75, 0x46);
}else{
bbpwrite(ctlr, 82, 0xf2);
bbpwrite(ctlr, 75, 0x50);
}
}
tmp = csr32r(ctlr, TxBandCfg);
tmp &= ~(Tx5gBandSelN | Tx5gBandSelP);
tmp |= (group == 0) ? Tx5gBandSelN : Tx5gBandSelP;
csr32w(ctlr, TxBandCfg, tmp);
/* enable appropriate Power Amplifiers and Low Noise Amplifiers */
tmp = RftrEn | TrswEn | LnaPe0En;
if(ctlr->nrxchains > 1)
tmp |= LnaPe1En;
if(ctlr->mac_ver == 0x3593 && ctlr->nrxchains > 2)
tmp |= Rt3593LnaPe2En;
if(group == 0){ /* 2GHz */
tmp |= PaPeG0En;
if(ctlr->ntxchains > 1)
tmp |= PaPeG1En;
if(ctlr->mac_ver == 0x3593 && ctlr->ntxchains > 2)
tmp |= Rt3593PaPeG2En;
}else{ /* 5GHz */
tmp |= PaPeA0En;
if(ctlr->ntxchains > 1)
tmp |= PaPeA1En;
if(ctlr->mac_ver == 0x3593 && ctlr->ntxchains > 2)
tmp |= Rt3593PaPeA2En;
}
csr32w(ctlr, TxPinCfg, tmp);
if(ctlr->mac_ver == 0x3593){
tmp = csr32r(ctlr, GpioCtrl);
if(ctlr->flags & ConnPciE){
tmp &= ~0x01010000;
if(group == 0)
tmp |= 0x00010000;
}else{
tmp &= ~0x00008080;
if(group == 0)
tmp |= 0x00000080;
}
tmp = (tmp & ~0x00001000) | 0x00000010;
csr32w(ctlr, GpioCtrl, tmp);
}
/* set initial AGC value */
if(group == 0){ /* 2GHz band */
if(ctlr->mac_ver >= 0x3071)
agc = 0x1c + ctlr->lna[0] * 2;
else
agc = 0x2e + ctlr->lna[0];
}else{ /* 5GHz band */
agc = 0x32 + (ctlr->lna[group] * 5) / 3;
}
bbpwrite(ctlr, 66, agc);
microdelay(1000);
}
static void
setchan(Ctlr *ctlr, uint chan)
{
const struct rfprog *rfprog = rt2860_rf2850;
u32int r2, r3, r4;
s8int txpow1, txpow2;
uint i;
/* find the settings for this channel (we know it exists) */
for(i = 0; rfprog[i].chan != chan; i++);
r2 = rfprog[i].r2;
if(ctlr->ntxchains == 1)
r2 |= 1 << 12; /* 1T: disable Tx chain 2 */
if(ctlr->nrxchains == 1)
r2 |= 1 << 15 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */
else if(ctlr->nrxchains == 2)
r2 |= 1 << 4; /* 2R: disable Rx chain 3 */
/* use Tx power values from EEPROM */
txpow1 = ctlr->txpow1[i];
txpow2 = ctlr->txpow2[i];
if(chan > 14){
if(txpow1 >= 0)
txpow1 = txpow1 << 1 | 1;
else
txpow1 = (7 + txpow1) << 1;
if(txpow2 >= 0)
txpow2 = txpow2 << 1 | 1;
else
txpow2 = (7 + txpow2) << 1;
}
r3 = rfprog[i].r3 | txpow1 << 7;
r4 = rfprog[i].r4 | ctlr->freq << 13 | txpow2 << 4;
rfwrite(ctlr, Rf1, rfprog[i].r1);
rfwrite(ctlr, Rf2, r2);
rfwrite(ctlr, Rf3, r3);
rfwrite(ctlr, Rf4, r4);
microdelay(200);
rfwrite(ctlr, Rf1, rfprog[i].r1);
rfwrite(ctlr, Rf2, r2);
rfwrite(ctlr, Rf3, r3 | 1);
rfwrite(ctlr, Rf4, r4);
microdelay(200);
rfwrite(ctlr, Rf1, rfprog[i].r1);
rfwrite(ctlr, Rf2, r2);
rfwrite(ctlr, Rf3, r3);
rfwrite(ctlr, Rf4, r4);
}
static void
rt3090setchan(Ctlr *ctlr, uint chan)
{
s8int txpow1, txpow2;
u8int rf;
int i;
assert(chan >= 1 && chan <= 14); /* RT3090 is 2GHz only */
/* find the settings for this channel (we know it exists) */
for(i = 0; rt2860_rf2850[i].chan != chan; i++);
/* use Tx power values from EEPROM */
txpow1 = ctlr->txpow1[i];
txpow2 = ctlr->txpow2[i];
rt3090rfwrite(ctlr, 2, rt3090_freqs[i].n);
rf = rt3090rfread(ctlr, 3);
rf = (rf & ~0x0f) | rt3090_freqs[i].k;
rt3090rfwrite(ctlr, 3, rf);
rf = rt3090rfread(ctlr, 6);
rf = (rf & ~0x03) | rt3090_freqs[i].r;
rt3090rfwrite(ctlr, 6, rf);
/* set Tx0 power */
rf = rt3090rfread(ctlr, 12);
rf = (rf & ~0x1f) | txpow1;
rt3090rfwrite(ctlr, 12, rf);
/* set Tx1 power */
rf = rt3090rfread(ctlr, 13);
rf = (rf & ~0x1f) | txpow2;
rt3090rfwrite(ctlr, 13, rf);
rf = rt3090rfread(ctlr, 1);
rf &= ~0xfc;
if(ctlr->ntxchains == 1)
rf |= Rt3070Tx1Pd | Rt3070Tx2Pd;
else if(ctlr->ntxchains == 2)
rf |= Rt3070Tx2Pd;
if(ctlr->nrxchains == 1)
rf |= Rt3070Rx1Pd | Rt3070Rx2Pd;
else if(ctlr->nrxchains == 2)
rf |= Rt3070Rx2Pd;
rt3090rfwrite(ctlr, 1, rf);
/* set RF offset */
rf = rt3090rfread(ctlr, 23);
rf = (rf & ~0x7f) | ctlr->freq;
rt3090rfwrite(ctlr, 23, rf);
/* program RF filter */
rf = rt3090rfread(ctlr, 24); /* Tx */
rf = (rf & ~0x3f) | ctlr->rf24_20mhz;
rt3090rfwrite(ctlr, 24, rf);
rf = rt3090rfread(ctlr, 31); /* Rx */
rf = (rf & ~0x3f) | ctlr->rf24_20mhz;
rt3090rfwrite(ctlr, 31, rf);
/* enable RF tuning */
rf = rt3090rfread(ctlr, 7);
rt3090rfwrite(ctlr, 7, rf | Rt3070Tune);
}
static int
rt3090filtercalib(Ctlr *ctlr, u8int init, u8int target, u8int *val)
{
u8int rf22, rf24;
u8int bbp55_pb, bbp55_sb, delta;
int ntries;
/* program filter */
rf24 = rt3090rfread(ctlr, 24);
rf24 = (rf24 & 0xc0) | init; /* initial filter value */
rt3090rfwrite(ctlr, 24, rf24);
/* enable baseband loopback mode */
rf22 = rt3090rfread(ctlr, 22);
rt3090rfwrite(ctlr, 22, rf22 | Rt3070BbLoopback);
/* set power and frequency of passband test tone */
bbpwrite(ctlr, 24, 0x00);
for(ntries = 0, bbp55_pb = 0; ntries < 100; ntries++){
/* transmit test tone */
bbpwrite(ctlr, 25, 0x90);
microdelay(1000);
/* read received power */
bbp55_pb = bbpread(ctlr, 55);
if(bbp55_pb != 0)
break;
}
if(ntries == 100)
return -1;
/* set power and frequency of stopband test tone */
bbpwrite(ctlr, 24, 0x06);
for(ntries = 0; ntries < 100; ntries++){
/* transmit test tone */
bbpwrite(ctlr, 25, 0x90);
microdelay(1000);
/* read received power */
bbp55_sb = bbpread(ctlr, 55);
delta = bbp55_pb - bbp55_sb;
if(delta > target)
break;
/* reprogram filter */
rf24++;
rt3090rfwrite(ctlr, 24, rf24);
}
if(ntries < 100){
if(rf24 != init)
rf24--; /* backtrack */
*val = rf24;
rt3090rfwrite(ctlr, 24, rf24);
}
/* restore initial state */
bbpwrite(ctlr, 24, 0x00);
/* disable baseband loopback mode */
rf22 = rt3090rfread(ctlr, 22);
rt3090rfwrite(ctlr, 22, rf22 & ~Rt3070BbLoopback);
return 0;
}
static void
rt3090setrxantenna(Ctlr *ctlr, int aux)
{
u32int tmp;
if(aux){
tmp = csr32r(ctlr, PciEectrl);
csr32w(ctlr, PciEectrl, tmp & ~EectrlC);
tmp = csr32r(ctlr, GpioCtrl);
csr32w(ctlr, GpioCtrl, (tmp & ~0x0808) | 0x08);
}else{
tmp = csr32r(ctlr, PciEectrl);
csr32w(ctlr, PciEectrl, tmp | EectrlC);
tmp = csr32r(ctlr, GpioCtrl);
csr32w(ctlr, GpioCtrl, tmp & ~0x0808);
}
}
static int
rt3090rfinit(Ctlr *ctlr)
{
u32int tmp;
u8int rf, bbp;
int i;
rf = rt3090rfread(ctlr, 30);
/* toggle RF R30 bit 7 */
rt3090rfwrite(ctlr, 30, rf | 0x80);
microdelay(1000);
rt3090rfwrite(ctlr, 30, rf & ~0x80);
tmp = csr32r(ctlr, Rt3070LdoCfg0);
tmp &= ~0x1f000000;
if(ctlr->patch_dac && ctlr->mac_rev < 0x0211)
tmp |= 0x0d000000; /* 1.35V */
else
tmp |= 0x01000000; /* 1.2V */
csr32w(ctlr, Rt3070LdoCfg0, tmp);
/* patch LNA_PE_G1 */
tmp = csr32r(ctlr, Rt3070GpioSwitch);
csr32w(ctlr, Rt3070GpioSwitch, tmp & ~0x20);
/* initialize RF registers to default value */
for(i = 0; i < nelem(rt3090_def_rf); i++){
rt3090rfwrite(ctlr, rt3090_def_rf[i].reg,
rt3090_def_rf[i].val);
}
/* select 20MHz bandwidth */
rt3090rfwrite(ctlr, 31, 0x14);
rf = rt3090rfread(ctlr, 6);
rt3090rfwrite(ctlr, 6, rf | 0x40);
if(ctlr->mac_ver != 0x3593){
/* calibrate filter for 20MHz bandwidth */
ctlr->rf24_20mhz = 0x1f; /* default value */
rt3090filtercalib(ctlr, 0x07, 0x16, &ctlr->rf24_20mhz);
/* select 40MHz bandwidth */
bbp = bbpread(ctlr, 4);
bbpwrite(ctlr, 4, (bbp & ~0x08) | 0x10);
rf = rt3090rfread(ctlr, 31);
rt3090rfwrite(ctlr, 31, rf | 0x20);
/* calibrate filter for 40MHz bandwidth */
ctlr->rf24_40mhz = 0x2f; /* default value */
rt3090filtercalib(ctlr, 0x27, 0x19, &ctlr->rf24_40mhz);
/* go back to 20MHz bandwidth */
bbp = bbpread(ctlr, 4);
bbpwrite(ctlr, 4, bbp & ~0x18);
}
if(ctlr->mac_rev < 0x0211)
rt3090rfwrite(ctlr, 27, 0x03);
tmp = csr32r(ctlr, Rt3070Opt14);
csr32w(ctlr, Rt3070Opt14, tmp | 1);
if(ctlr->rf_rev == Rf3020)
rt3090setrxantenna(ctlr, 0);
bbp = bbpread(ctlr, 138);
if(ctlr->mac_ver == 0x3593){
if(ctlr->ntxchains == 1)
bbp |= 0x60; /* turn off DAC1 and DAC2 */
else if(ctlr->ntxchains == 2)
bbp |= 0x40; /* turn off DAC2 */
if(ctlr->nrxchains == 1)
bbp &= ~0x06; /* turn off ADC1 and ADC2 */
else if(ctlr->nrxchains == 2)
bbp &= ~0x04; /* turn off ADC2 */
}else{
if(ctlr->ntxchains == 1)
bbp |= 0x20; /* turn off DAC1 */
if(ctlr->nrxchains == 1)
bbp &= ~0x02; /* turn off ADC1 */
}
bbpwrite(ctlr, 138, bbp);
rf = rt3090rfread(ctlr, 1);
rf &= ~(Rt3070Rx0Pd | Rt3070Tx0Pd);
rf |= Rt3070RfBlock | Rt3070Rx1Pd | Rt3070Tx1Pd;
rt3090rfwrite(ctlr, 1, rf);
rf = rt3090rfread(ctlr, 15);
rt3090rfwrite(ctlr, 15, rf & ~Rt3070TxLo2);
rf = rt3090rfread(ctlr, 17);
rf &= ~Rt3070TxLo1;
if(ctlr->mac_rev >= 0x0211 && !ctlr->ext_2ghz_lna)
rf |= 0x20; /* fix for long range Rx issue */
if(ctlr->txmixgain_2ghz >= 2)
rf = (rf & ~0x7) | ctlr->txmixgain_2ghz;
rt3090rfwrite(ctlr, 17, rf);
rf = rt3090rfread(ctlr, 20);
rt3090rfwrite(ctlr, 20, rf & ~Rt3070RxLo1);
rf = rt3090rfread(ctlr, 21);
rt3090rfwrite(ctlr, 21, rf & ~Rt3070RxLo2);
return 0;
}
static void
rt3090rfwakeup(Ctlr *ctlr)
{
u32int tmp;
u8int rf;
if(ctlr->mac_ver == 0x3593){
/* enable VCO */
rf = rt3090rfread(ctlr, 1);
rt3090rfwrite(ctlr, 1, rf | Rt3593Vco);
/* initiate VCO calibration */
rf = rt3090rfread(ctlr, 3);
rt3090rfwrite(ctlr, 3, rf | Rt3593Vcocal);
/* enable VCO bias current control */
rf = rt3090rfread(ctlr, 6);
rt3090rfwrite(ctlr, 6, rf | Rt3593VcoIc);
/* initiate res calibration */
rf = rt3090rfread(ctlr, 2);
rt3090rfwrite(ctlr, 2, rf | Rt3593Rescal);
/* set reference current control to 0.33 mA */
rf = rt3090rfread(ctlr, 22);
rf &= ~Rt3593CpIcMask;
rf |= 1 << Rt3593CpIcShift;
rt3090rfwrite(ctlr, 22, rf);
/* enable RX CTB */
rf = rt3090rfread(ctlr, 46);
rt3090rfwrite(ctlr, 46, rf | Rt3593RxCtb);
rf = rt3090rfread(ctlr, 20);
rf &= ~(Rt3593LdoRfVcMask | Rt3593LdoPllVcMask);
rt3090rfwrite(ctlr, 20, rf);
}else{
/* enable RF block */
rf = rt3090rfread(ctlr, 1);
rt3090rfwrite(ctlr, 1, rf | Rt3070RfBlock);
/* enable VCO bias current control */
rf = rt3090rfread(ctlr, 7);
rt3090rfwrite(ctlr, 7, rf | 0x30);
rf = rt3090rfread(ctlr, 9);
rt3090rfwrite(ctlr, 9, rf | 0x0e);
/* enable RX CTB */
rf = rt3090rfread(ctlr, 21);
rt3090rfwrite(ctlr, 21, rf | Rt3070RxCtb);
/* fix Tx to Rx IQ glitch by raising RF voltage */
rf = rt3090rfread(ctlr, 27);
rf &= ~0x77;
if(ctlr->mac_rev < 0x0211)
rf |= 0x03;
rt3090rfwrite(ctlr, 27, rf);
}
if(ctlr->patch_dac && ctlr->mac_rev < 0x0211){
tmp = csr32r(ctlr, Rt3070LdoCfg0);
tmp = (tmp & ~0x1f000000) | 0x0d000000;
csr32w(ctlr, Rt3070LdoCfg0, tmp);
}
}
static void
rt3090rfsetup(Ctlr *ctlr)
{
u8int bbp;
int i;
if(ctlr->mac_rev >= 0x0211){
/* enable DC filter */
bbpwrite(ctlr, 103, 0xc0);
/* improve power consumption */
bbp = bbpread(ctlr, 31);
bbpwrite(ctlr, 31, bbp & ~0x03);
}
csr32w(ctlr, TxSwCfg1, 0);
if(ctlr->mac_rev < 0x0211){
csr32w(ctlr, TxSwCfg2,
ctlr->patch_dac ? 0x2c : 0x0f);
}else
csr32w(ctlr, TxSwCfg2, 0);
/* initialize RF registers from ROM */
for(i = 0; i < 10; i++){
if(ctlr->rf[i].reg == 0 || ctlr->rf[i].reg == 0xff)
continue;
rt3090rfwrite(ctlr, ctlr->rf[i].reg, ctlr->rf[i].val);
}
}
static void
updateprot(Ctlr *ctlr)
{
u32int tmp;
tmp = RtsthEn | ProtNavShort | TxopAllowAll;
/* setup protection frame rate (MCS code) */
tmp |= /*(ic->ic_curmode == IEEE80211_MODE_11A) ?
rt2860_rates[RT2860_RIDX_OFDM6].mcs :*/
rt2860_rates[RidxCck11].mcs;
/* CCK frames don't require protection */
csr32w(ctlr, CckProtCfg, tmp);
/* XXX
if(ic->ic_flags & IEEE80211_F_USEPROT){
if(ic->ic_protmode == IEEE80211_PROT_RTSCTS)
tmp |= ProtCtrlRtsCts;
else if(ic->ic_protmode == IEEE80211_PROT_CTSONLY)
tmp |= ProtCtrlCts;
}
csr32w(ctlr, OfdmProtCfg, tmp); */
}
static char*
rt2860start(Ether *edev)
{
u32int tmp;
u8int bbp1, bbp3;
int i, qid, ridx, ntries;
char *err;
Ctlr *ctlr;
ctlr = edev->ctlr;
csr32w(ctlr, PwrPinCfg, IoRaPe);
/* disable DMA */
tmp = csr32r(ctlr, WpdmaGloCfg);
tmp &= 0xff0;
csr32w(ctlr, WpdmaGloCfg, tmp);
/* PBF hardware reset */
csr32w(ctlr, SysCtrl, 0xe1f);
coherence();
csr32w(ctlr, SysCtrl, 0xe00);
if((err = boot(ctlr)) != nil){
/*XXX: rt2860stop(ifp, 1);*/
return err;
}
/* set MAC address */
csr32w(ctlr, MacAddrDw0,
edev->ea[0] | edev->ea[1] << 8 | edev->ea[2] << 16 | edev->ea[3] << 24);
csr32w(ctlr, MacAddrDw1,
edev->ea[4] | edev->ea[5] << 8 | 0xff << 16);
/* init Tx power for all Tx rates (from EEPROM) */
for(ridx = 0; ridx < 5; ridx++){
if(ctlr->txpow20mhz[ridx] == 0xffffffff)
continue;
csr32w(ctlr, TxPwrCfg(ridx), ctlr->txpow20mhz[ridx]);
}
for(ntries = 0; ntries < 100; ntries++){
tmp = csr32r(ctlr, WpdmaGloCfg);
if((tmp & (TxDmaBusy | RxDmaBusy)) == 0)
break;
microdelay(1000);
}
if(ntries == 100){
err = "timeout waiting for DMA engine";
/*rt2860_stop(ifp, 1);*/
return err;
}
tmp &= 0xff0;
csr32w(ctlr, WpdmaGloCfg, tmp);
/* reset Rx ring and all 6 Tx rings */
csr32w(ctlr, WpdmaRstIdx, 0x1003f);
/* PBF hardware reset */
csr32w(ctlr, SysCtrl, 0xe1f);
coherence();
csr32w(ctlr, SysCtrl, 0xe00);
csr32w(ctlr, PwrPinCfg, IoRaPe | IoRfPe);
csr32w(ctlr, MacSysCtrl, BbpHrst | MacSrst);
coherence();
csr32w(ctlr, MacSysCtrl, 0);
for(i = 0; i < nelem(rt2860_def_mac); i++)
csr32w(ctlr, rt2860_def_mac[i].reg, rt2860_def_mac[i].val);
if(ctlr->mac_ver >= 0x3071){
/* set delay of PA_PE assertion to 1us (unit of 0.25us) */
csr32w(ctlr, TxSwCfg0,
4 << DlyPapeEnShift);
}
if(!(csr32r(ctlr, PciCfg) & PciCfgPci)){
ctlr->flags |= ConnPciE;
/* PCIe has different clock cycle count than PCI */
tmp = csr32r(ctlr, UsCycCnt);
tmp = (tmp & ~0xff) | 0x7d;
csr32w(ctlr, UsCycCnt, tmp);
}
/* wait while MAC is busy */
for(ntries = 0; ntries < 100; ntries++){
if(!(csr32r(ctlr, MacStatusReg) &
(RxStatusBusy | TxStatusBusy)))
break;
microdelay(1000);
}
if(ntries == 100){
err = "timeout waiting for MAC";
/*rt2860_stop(ifp, 1);*/
return err;
}
/* clear Host to MCU mailbox */
csr32w(ctlr, H2mBbpagent, 0);
csr32w(ctlr, H2mMailbox, 0);
mcucmd(ctlr, McuCmdRfreset, 0, 0);
microdelay(1000);
if((err = bbpinit(ctlr)) != nil){
/*rt2860_stop(ifp, 1);*/
return err;
}
/* clear RX WCID search table */
setregion(ctlr, WcidEntry(0), 0, 512);
/* clear pairwise key table */
setregion(ctlr, Pkey(0), 0, 2048);
/* clear IV/EIV table */
setregion(ctlr, Iveiv(0), 0, 512);
/* clear WCID attribute table */
setregion(ctlr, WcidAttr(0), 0, 256);
/* clear shared key table */
setregion(ctlr, Skey(0, 0), 0, 8 * 32);
/* clear shared key mode */
setregion(ctlr, SkeyMode07, 0, 4);
/* init Tx rings (4 EDCAs + HCCA + Mgt) */
for(qid = 0; qid < 6; qid++){
csr32w(ctlr, TxBasePtr(qid), PCIWADDR(ctlr->tx[qid].d));
csr32w(ctlr, TxMaxCnt(qid), Ntx);
csr32w(ctlr, TxCtxIdx(qid), 0);
}
/* init Rx ring */
csr32w(ctlr, RxBasePtr, PCIWADDR(ctlr->rx.p));
csr32w(ctlr, RxMaxCnt, Nrx);
csr32w(ctlr, RxCalcIdx, Nrx - 1);
/* setup maximum buffer sizes */
csr32w(ctlr, MaxLenCfg, 1 << 12 |
(Rbufsize - Rxwisize - 2));
for(ntries = 0; ntries < 100; ntries++){
tmp = csr32r(ctlr, WpdmaGloCfg);
if((tmp & (TxDmaBusy | RxDmaBusy)) == 0)
break;
microdelay(1000);
}
if(ntries == 100){
err = "timeout waiting for DMA engine";
/*rt2860_stop(ifp, 1);*/
return err;
}
tmp &= 0xff0;
csr32w(ctlr, WpdmaGloCfg, tmp);
/* disable interrupts mitigation */
csr32w(ctlr, DelayIntCfg, 0);
/* write vendor-specific BBP values (from EEPROM) */
for(i = 0; i < 8; i++){
if(ctlr->bbp[i].reg == 0 || ctlr->bbp[i].reg == 0xff)
continue;
bbpwrite(ctlr, ctlr->bbp[i].reg, ctlr->bbp[i].val);
}
/* select Main antenna for 1T1R devices */
if(ctlr->rf_rev == Rf2020 ||
ctlr->rf_rev == Rf3020 ||
ctlr->rf_rev == Rf3320)
rt3090setrxantenna(ctlr, 0);
/* send LEDs operating mode to microcontroller */
mcucmd(ctlr, McuCmdLed1, ctlr->led[0], 0);
mcucmd(ctlr, McuCmdLed2, ctlr->led[1], 0);
mcucmd(ctlr, McuCmdLed3, ctlr->led[2], 0);
if(ctlr->mac_ver >= 0x3071)
rt3090rfinit(ctlr);
mcucmd(ctlr, McuCmdSleep, 0x02ff, 1);
mcucmd(ctlr, McuCmdWakeup, 0, 1);
if(ctlr->mac_ver >= 0x3071)
rt3090rfwakeup(ctlr);
/* disable non-existing Rx chains */
bbp3 = bbpread(ctlr, 3);
bbp3 &= ~(1 << 3 | 1 << 4);
if(ctlr->nrxchains == 2)
bbp3 |= 1 << 3;
else if(ctlr->nrxchains == 3)
bbp3 |= 1 << 4;
bbpwrite(ctlr, 3, bbp3);
/* disable non-existing Tx chains */
bbp1 = bbpread(ctlr, 1);
if(ctlr->ntxchains == 1)
bbp1 = (bbp1 & ~(1 << 3 | 1 << 4));
else if(ctlr->mac_ver == 0x3593 && ctlr->ntxchains == 2)
bbp1 = (bbp1 & ~(1 << 4)) | 1 << 3;
else if(ctlr->mac_ver == 0x3593 && ctlr->ntxchains == 3)
bbp1 = (bbp1 & ~(1 << 3)) | 1 << 4;
bbpwrite(ctlr, 1, bbp1);
if(ctlr->mac_ver >= 0x3071)
rt3090rfsetup(ctlr);
/* select default channel */
if(ctlr->mac_ver >= 0x3071)
rt3090setchan(ctlr, 3);
else
setchan(ctlr, 3);
/* reset RF from MCU */
mcucmd(ctlr, McuCmdRfreset, 0, 0);
/* set RTS threshold */
tmp = csr32r(ctlr, TxRtsCfg);
tmp &= ~0xffff00;
tmp |= 1 /* ic->ic_rtsthreshold */ << 8;
csr32w(ctlr, TxRtsCfg, tmp);
/* setup initial protection mode */
updateprot(ctlr);
/* turn radio LED on */
setleds(ctlr, LedRadio);
/* enable Tx/Rx DMA engine */
if((err = txrxon(ctlr)) != 0){
/*rt2860_stop(ifp, 1);*/
return err;
}
/* clear pending interrupts */
csr32w(ctlr, IntStatus, 0xffffffff);
/* enable interrupts */
csr32w(ctlr, IntMask, 0x3fffc);
if(ctlr->flags & AdvancedPs)
mcucmd(ctlr, McuCmdPslevel, ctlr->pslevel, 0);
return nil;
}
/*
* Add `delta' (signed) to each 4-bit sub-word of a 32-bit word.
* Used to adjust per-rate Tx power registers.
*/
static u32int
b4inc(u32int b32, s8int delta)
{
s8int i, b4;
for(i = 0; i < 8; i++){
b4 = b32 & 0xf;
b4 += delta;
if(b4 < 0)
b4 = 0;
else if(b4 > 0xf)
b4 = 0xf;
b32 = b32 >> 4 | b4 << 28;
}
return b32;
}
static void
transmit(Wifi *wifi, Wnode *wn, Block *b)
{
Ether *edev;
Ctlr *ctlr;
Wifipkt *w;
u8int mcs, qid;
int ridx, /*ctl_ridx,*/ hdrlen;
uchar *p;
int nodeid;
Block *outb;
TXQ *tx;
Pool *pool;
edev = wifi->ether;
ctlr = edev->ctlr;
qlock(ctlr);
if(ctlr->attached == 0 || ctlr->broken){
qunlock(ctlr);
freeb(b);
return;
}
if((wn->channel != ctlr->channel)
|| (!ctlr->prom && (wn->aid != ctlr->aid || memcmp(wn->bssid, ctlr->bssid, Eaddrlen) != 0)))
rxon(edev, wn);
if(b == nil){
/* association note has no data to transmit */
qunlock(ctlr);
return;
}
pool = &ctlr->pool;
qid = 0; /* for now */
ridx = 0;
tx = &ctlr->tx[qid];
nodeid = ctlr->bcastnodeid;
w = (Wifipkt*)b->rp;
hdrlen = wifihdrlen(w);
p = pool->p + pool->i * TxwiDmaSz;
if((w->a1[0] & 1) == 0){
*(p+4) = TxAck; /* xflags */
if(BLEN(b) > 512-4)
*(p+1) = TxTxopBackoff; /* txop */
if((w->fc[0] & 0x0c) == 0x08 && ctlr->bssnodeid != -1){
nodeid = ctlr->bssnodeid;
ridx = 2; /* BUG: hardcode 11Mbit */
}
}
/*ctl_ridx = rt2860_rates[ridx].ctl_ridx;*/
mcs = rt2860_rates[ridx].mcs;
/* setup TX Wireless Information */
*p = 0; /* flags */
*(p+2) = PhyCck | mcs; /* phy */
/* let HW generate seq numbers */
*(p+4) |= TxNseq; /* xflags */
put16(p + 6, BLEN(b) | (((mcs+1) & 0xf) << TxPidShift) ); /* length */
/* put16((uchar*)&w->dur[0], rt2860_rates[ctl_ridx].lp_ack_dur); */
*(p+5) = nodeid; /* wcid */
/* copy packet header */
memmove(p + Txwisize, b->rp, hdrlen);
/* setup tx descriptor */
/* first segment is TXWI + 802.11 header */
p = (uchar*)tx->d + Tdscsize * tx->i;
put32(p, PCIWADDR(pool->p + pool->i * TxwiDmaSz)); /* sdp0 */
put16(p + 6, Txwisize + hdrlen); /* sdl0 */
*(p + 15) = TxQselEdca; /* flags */
/* allocate output buffer */
b->rp += hdrlen;
tx->b[tx->i] = outb = iallocb(BLEN(b) + 256);
if(outb == nil){
print("outb = nil\n");
return;
}
outb->rp = (uchar*)ROUND((uintptr)outb->base, 256);
memset(outb->rp, 0, BLEN(b));
memmove(outb->rp, b->rp, BLEN(b));
outb->wp = outb->rp + BLEN(b);
freeb(b);
/* setup payload segments */
put32(p + 8, PCIWADDR(outb->rp)); /* sdp1 */
put16(p + 4, BLEN(outb) | TxLs1); /* sdl1 */
p = pool->p + pool->i * TxwiDmaSz;
w = (Wifipkt*)(p + Txwisize);
if(ctlr->wifi->debug){
print("transmit: %E->%E,%E nodeid=%x txq[%d]=%d size=%zd\n", w->a2, w->a1, w->a3, nodeid, qid, ctlr->tx[qid].i, BLEN(outb));
}
tx->i = (tx->i + 1) % Ntx;
pool->i = (pool->i + 1) % Ntxpool;
coherence();
/* kick Tx */
csr32w(ctlr, TxCtxIdx(qid), ctlr->tx[qid].i);
qunlock(ctlr);
return;
}
static void
rt2860attach(Ether *edev)
{
FWImage *fw;
Ctlr *ctlr;
char *err;
ctlr = edev->ctlr;
eqlock(ctlr);
if(waserror()){
print("#l%d: %s\n", edev->ctlrno, up->errstr);
/*if(ctlr->power)
poweroff(ctlr);*/
qunlock(ctlr);
nexterror();
}
if(ctlr->attached == 0){
if(ctlr->wifi == nil)
ctlr->wifi = wifiattach(edev, transmit);
if(ctlr->fw == nil){
fw = readfirmware();
ctlr->fw = fw;
}
if((err = rt2860start(edev)) != nil){
error(err);
}
ctlr->bcastnodeid = -1;
ctlr->bssnodeid = -1;
ctlr->channel = 1;
ctlr->aid = 0;
setoptions(edev);
ctlr->attached = 1;
}
qunlock(ctlr);
poperror();
}
static void
receive(Ctlr *ctlr)
{
u32int hw;
RXQ *rx;
Block *b;
uchar *d;
rx = &ctlr->rx;
if(rx->b == nil){
print("rx->b == nil!");
return;
}
hw = csr32r(ctlr, FsDrxIdx) & 0xfff;
while(rx->i != hw){
u16int sdl0, len;
u32int flags;
uchar *p;
Wifipkt *w;
int hdrlen;
p = (uchar*)rx->p + Rdscsize * rx->i;
sdl0 = get16(p + 4 /* sdp0 */ + 2 /* sdl1 */);
if(!(sdl0 & RxDdone)){
print("rxd ddone bit not set\n");
break; /* should not happen */
}
flags = get32(p + 12);
if(flags & (RxCrcerr | RxIcverr)){
/* print("crc | icv err\n"); */
goto skip;
}
b = rx->b[rx->i];
if(b == nil){
print("no buf\n");
goto skip;
}
d = b->rp;
if(ctlr->wifi == nil)
goto skip;
if(rbplant(ctlr, rx->i) < 0){
print("can't plant");
goto skip;
}
ctlr->wcid = *b->rp;
len = get16(b->rp + 2 /* wcid, keyidx */) & 0xfff;
b->rp = d + Rxwisize;
b->wp = b->rp + len;
w = (Wifipkt*)b->rp;
hdrlen = wifihdrlen(w);
/* HW may insert 2 padding bytes after 802.11 header */
if(flags & RxL2pad){
memmove(b->rp + 2, b->rp, hdrlen);
b->rp += 2;
}
w = (Wifipkt*)b->rp;
if(ctlr->wifi->debug)
print("receive: %E->%E,%E wcid 0x%x \n", w->a2, w->a1, w->a3, ctlr->wcid);
wifiiq(ctlr->wifi, b);
skip:
put16(p + 4 /* sdp0 */ + 2 /* sdl1 */, sdl0 & ~RxDdone);
rx->i = (rx->i + 1) % Nrx;
}
coherence();
/* tell HW what we have processed */
csr32w(ctlr, RxCalcIdx, (rx->i - 1) % Nrx);
}
static void
stats(Ctlr *ctlr)
{
u32int stat;
u8int wcid;
while((stat = csr32r(ctlr, TxStatFifo)) & TxqVld){
wcid = (stat >> TxqWcidShift) & 0xff;
/* if no ACK was requested, no feedback is available */
if(!(stat & TxqAckreq) || wcid == 0xff){
continue;
}
}
}
static void
rt2860tx(Ctlr *ctlr, u8int q)
{
u32int hw;
TXQ *tx;
stats(ctlr);
tx = &ctlr->tx[q];
hw = csr32r(ctlr, TxDtxIdx(q));
while(tx->n != hw){
uchar *p = (uchar*)tx->d + Rdscsize * tx->n;
u16int sdl0;
if(tx->b[tx->n]){
freeb(tx->b[tx->n]);
tx->b[tx->n] = nil;
}
sdl0 = get16(p + 4 /* sdp0 */ + 2 /* sdl1 */);
if(!(sdl0 & TxDdone)){
print("txd ddone bit not set\n");
break; /* should not happen */
}
memset((uchar*)ctlr->pool.p + TxwiDmaSz * tx->n, 0, TxwiDmaSz);
memset((uchar*)tx->d + Tdscsize * tx->n, 0, Tdscsize);
// put16(p + 4 /* sdp0 */ + 2 /* sdl1 */, sdl0 & ~TxDdone);
tx->n = (tx->n + 1) % Ntx;
}
coherence();
}
static void
rt2860interrupt(Ureg*, void *arg)
{
u32int r;
Ether *edev;
Ctlr *ctlr;
int debug;
edev = arg;
ctlr = edev->ctlr;
ilock(ctlr);
debug = ctlr->wifi->debug;
r = csr32r(ctlr, IntStatus);
if(r == 0xffffffff){
iunlock(ctlr);
return;
}
if(r == 0){
iunlock(ctlr);
return;
}
/* acknowledge interrupts */
csr32w(ctlr, IntStatus, r);
if(r & TxRxCoherent){
u32int tmp;
/* DMA finds data coherent event when checking the DDONE bit */
if(debug)
print("txrx coherent intr\n");
/* restart DMA engine */
tmp = csr32r(ctlr, WpdmaGloCfg);
tmp &= ~(TxWbDdone | RxDmaEn | TxDmaEn);
csr32w(ctlr, WpdmaGloCfg, tmp);
txrxon(ctlr);
}
if(r & MacInt2)
stats(ctlr);
if(r & TxDoneInt5)
rt2860tx(ctlr, 5);
if(r & RxDoneInt)
receive(ctlr);
if(r & TxDoneInt4)
rt2860tx(ctlr, 4);
if(r & TxDoneInt3)
rt2860tx(ctlr, 3);
if(r & TxDoneInt2)
rt2860tx(ctlr, 2);
if(r & TxDoneInt1)
rt2860tx(ctlr, 1);
if(r & TxDoneInt0)
rt2860tx(ctlr, 0);
iunlock(ctlr);
}
static void
eepromctl(Ctlr *ctlr, u32int val)
{
csr32w(ctlr, PciEectrl, val);
coherence();
microdelay(EepromDelay);
}
/*
* Read 16 bits at address 'addr' from the serial EEPROM (either 93C46,
* 93C66 or 93C86).
*/
static u16int
eeread2(Ctlr *ctlr, u16int addr)
{
u32int tmp;
u16int val;
int n;
/* clock C once before the first command */
eepromctl(ctlr, 0);
eepromctl(ctlr, EectrlS);
eepromctl(ctlr, EectrlS | EectrlC);
eepromctl(ctlr, EectrlS);
/* write start bit (1) */
eepromctl(ctlr, EectrlS | EectrlD);
eepromctl(ctlr, EectrlS | EectrlD | EectrlC);
/* write READ opcode (10) */
eepromctl(ctlr, EectrlS | EectrlD);
eepromctl(ctlr, EectrlS | EectrlD | EectrlC);
eepromctl(ctlr, EectrlS);
eepromctl(ctlr, EectrlS | EectrlC);
/* write address (A5-A0 or A7-A0) */
n = ((csr32r(ctlr, PciEectrl) & 0x30) == 0) ? 5 : 7;
for(; n >= 0; n--){
eepromctl(ctlr, EectrlS |
(((addr >> n) & 1) << EectrlShiftD));
eepromctl(ctlr, EectrlS |
(((addr >> n) & 1) << EectrlShiftD) | EectrlC);
}
eepromctl(ctlr, EectrlS);
/* read data Q15-Q0 */
val = 0;
for(n = 15; n >= 0; n--){
eepromctl(ctlr, EectrlS | EectrlC);
tmp = csr32r(ctlr, PciEectrl);
val |= ((tmp & EectrlQ) >> EectrlShiftQ) << n;
eepromctl(ctlr, EectrlS);
}
eepromctl(ctlr, 0);
/* clear Chip Select and clock C */
eepromctl(ctlr, EectrlS);
eepromctl(ctlr, 0);
eepromctl(ctlr, EectrlC);
return val;
}
/* Read 16-bit from eFUSE ROM (>=RT3071 only.) */
static u16int
efuseread2(Ctlr *ctlr, u16int addr)
{
u32int tmp;
u16int reg;
int ntries;
addr *= 2;
/*-
* Read one 16-byte block into registers EFUSE_DATA[0-3]:
* DATA0: F E D C
* DATA1: B A 9 8
* DATA2: 7 6 5 4
* DATA3: 3 2 1 0
*/
tmp = csr32r(ctlr, Rt3070EfuseCtrl);
tmp &= ~(Rt3070EfsromModeMask | Rt3070EfsromAinMask);
tmp |= (addr & ~0xf) << Rt3070EfsromAinShift | Rt3070EfsromKick;
csr32w(ctlr, Rt3070EfuseCtrl, tmp);
for(ntries = 0; ntries < 500; ntries++){
tmp = csr32r(ctlr, Rt3070EfuseCtrl);
if(!(tmp & Rt3070EfsromKick))
break;
microdelay(2);
}
if(ntries == 500)
return 0xffff;
if((tmp & Rt3070EfuseAoutMask) == Rt3070EfuseAoutMask)
return 0xffff; /* address not found */
/* determine to which 32-bit register our 16-bit word belongs */
reg = Rt3070EfuseData3 - (addr & 0xc);
tmp = csr32r(ctlr, reg);
return (addr & 2) ? tmp >> 16 : tmp & 0xffff;
}
static char*
eepromread(Ether *edev)
{
s8int delta_2ghz, delta_5ghz;
u32int tmp;
u16int val;
int ridx, ant, i;
u16int (*rom_read)(Ctlr*, u16int);
Ctlr *ctlr;
enum { DefLna = 10 };
ctlr = edev->ctlr;
/* check whether the ROM is eFUSE ROM or EEPROM */
rom_read = eeread2;
if(ctlr->mac_ver >= 0x3071){
tmp = csr32r(ctlr, Rt3070EfuseCtrl);
if(tmp & Rt3070SelEfuse)
rom_read = efuseread2;
}
/* read MAC address */
val = rom_read(ctlr, EepromMac01);
edev->ea[0] = val & 0xff;
edev->ea[1] = val >> 8;
val = rom_read(ctlr, EepromMac23);
edev->ea[2] = val & 0xff;
edev->ea[3] = val >> 8;
val = rom_read(ctlr, EepromMac45);
edev->ea[4] = val & 0xff;
edev->ea[5] = val >> 8;
/* read vendor BBP settings */
for(i = 0; i < 8; i++){
val = rom_read(ctlr, EepromBbpBase + i);
ctlr->bbp[i].val = val & 0xff;
ctlr->bbp[i].reg = val >> 8;
}
if(ctlr->mac_ver >= 0x3071){
/* read vendor RF settings */
for(i = 0; i < 10; i++){
val = rom_read(ctlr, Rt3071EepromRfBase + i);
ctlr->rf[i].val = val & 0xff;
ctlr->rf[i].reg = val >> 8;
}
}
/* read RF frequency offset from EEPROM */
val = rom_read(ctlr, EepromFreqLeds);
ctlr->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0;
if((val >> 8) != 0xff){
/* read LEDs operating mode */
ctlr->leds = val >> 8;
ctlr->led[0] = rom_read(ctlr, EepromLed1);
ctlr->led[1] = rom_read(ctlr, EepromLed2);
ctlr->led[2] = rom_read(ctlr, EepromLed3);
}else{
/* broken EEPROM, use default settings */
ctlr->leds = 0x01;
ctlr->led[0] = 0x5555;
ctlr->led[1] = 0x2221;
ctlr->led[2] = 0xa9f8;
}
/* read RF information */
val = rom_read(ctlr, EepromAntenna);
if(val == 0xffff){
if(ctlr->mac_ver == 0x3593){
/* default to RF3053 3T3R */
ctlr->rf_rev = Rf3053;
ctlr->ntxchains = 3;
ctlr->nrxchains = 3;
}else if(ctlr->mac_ver >= 0x3071){
/* default to RF3020 1T1R */
ctlr->rf_rev = Rf3020;
ctlr->ntxchains = 1;
ctlr->nrxchains = 1;
}else{
/* default to RF2820 1T2R */
ctlr->rf_rev = Rf2820;
ctlr->ntxchains = 1;
ctlr->nrxchains = 2;
}
}else{
ctlr->rf_rev = (val >> 8) & 0xf;
ctlr->ntxchains = (val >> 4) & 0xf;
ctlr->nrxchains = val & 0xf;
}
/* check if RF supports automatic Tx access gain control */
val = rom_read(ctlr, EepromConfig);
/* check if driver should patch the DAC issue */
if((val >> 8) != 0xff)
ctlr->patch_dac = (val >> 15) & 1;
if((val & 0xff) != 0xff){
ctlr->ext_5ghz_lna = (val >> 3) & 1;
ctlr->ext_2ghz_lna = (val >> 2) & 1;
/* check if RF supports automatic Tx access gain control */
ctlr->calib_2ghz = ctlr->calib_5ghz = 0; /* XXX (val >> 1) & 1 */;
/* check if we have a hardware radio switch */
ctlr->rfswitch = val & 1;
}
if(ctlr->flags & AdvancedPs){
/* read PCIe power save level */
val = rom_read(ctlr, EepromPciePslevel);
if((val & 0xff) != 0xff){
ctlr->pslevel = val & 0x3;
val = rom_read(ctlr, EepromRev);
if((val & 0xff80) != 0x9280)
ctlr->pslevel = MIN(ctlr->pslevel, 1);
}
}
/* read power settings for 2GHz channels */
for(i = 0; i < 14; i += 2){
val = rom_read(ctlr,
EepromPwr2ghzBase1 + i / 2);
ctlr->txpow1[i + 0] = (s8int)(val & 0xff);
ctlr->txpow1[i + 1] = (s8int)(val >> 8);
val = rom_read(ctlr,
EepromPwr2ghzBase2 + i / 2);
ctlr->txpow2[i + 0] = (s8int)(val & 0xff);
ctlr->txpow2[i + 1] = (s8int)(val >> 8);
}
/* fix broken Tx power entries */
for(i = 0; i < 14; i++){
if(ctlr->txpow1[i] < 0 || ctlr->txpow1[i] > 31)
ctlr->txpow1[i] = 5;
if(ctlr->txpow2[i] < 0 || ctlr->txpow2[i] > 31)
ctlr->txpow2[i] = 5;
}
/* read power settings for 5GHz channels */
for(i = 0; i < 40; i += 2){
val = rom_read(ctlr,
EepromPwr5ghzBase1 + i / 2);
ctlr->txpow1[i + 14] = (s8int)(val & 0xff);
ctlr->txpow1[i + 15] = (s8int)(val >> 8);
val = rom_read(ctlr,
EepromPwr5ghzBase2 + i / 2);
ctlr->txpow2[i + 14] = (s8int)(val & 0xff);
ctlr->txpow2[i + 15] = (s8int)(val >> 8);
}
/* fix broken Tx power entries */
for(i = 0; i < 40; i++){
if(ctlr->txpow1[14 + i] < -7 || ctlr->txpow1[14 + i] > 15)
ctlr->txpow1[14 + i] = 5;
if(ctlr->txpow2[14 + i] < -7 || ctlr->txpow2[14 + i] > 15)
ctlr->txpow2[14 + i] = 5;
}
/* read Tx power compensation for each Tx rate */
val = rom_read(ctlr, EepromDeltapwr);
delta_2ghz = delta_5ghz = 0;
if((val & 0xff) != 0xff && (val & 0x80)){
delta_2ghz = val & 0xf;
if(!(val & 0x40)) /* negative number */
delta_2ghz = -delta_2ghz;
}
val >>= 8;
if((val & 0xff) != 0xff && (val & 0x80)){
delta_5ghz = val & 0xf;
if(!(val & 0x40)) /* negative number */
delta_5ghz = -delta_5ghz;
}
for(ridx = 0; ridx < 5; ridx++){
u32int reg;
val = rom_read(ctlr, EepromRpwr + ridx * 2);
reg = val;
val = rom_read(ctlr, EepromRpwr + ridx * 2 + 1);
reg |= (u32int)val << 16;
ctlr->txpow20mhz[ridx] = reg;
ctlr->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz);
ctlr->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz);
}
/* read factory-calibrated samples for temperature compensation */
val = rom_read(ctlr, EepromTssi12ghz);
ctlr->tssi_2ghz[0] = val & 0xff; /* [-4] */
ctlr->tssi_2ghz[1] = val >> 8; /* [-3] */
val = rom_read(ctlr, EepromTssi22ghz);
ctlr->tssi_2ghz[2] = val & 0xff; /* [-2] */
ctlr->tssi_2ghz[3] = val >> 8; /* [-1] */
val = rom_read(ctlr, EepromTssi32ghz);
ctlr->tssi_2ghz[4] = val & 0xff; /* [+0] */
ctlr->tssi_2ghz[5] = val >> 8; /* [+1] */
val = rom_read(ctlr, EepromTssi42ghz);
ctlr->tssi_2ghz[6] = val & 0xff; /* [+2] */
ctlr->tssi_2ghz[7] = val >> 8; /* [+3] */
val = rom_read(ctlr, EepromTssi52ghz);
ctlr->tssi_2ghz[8] = val & 0xff; /* [+4] */
ctlr->step_2ghz = val >> 8;
/* check that ref value is correct, otherwise disable calibration */
if(ctlr->tssi_2ghz[4] == 0xff)
ctlr->calib_2ghz = 0;
val = rom_read(ctlr, EepromTssi15ghz);
ctlr->tssi_5ghz[0] = val & 0xff; /* [-4] */
ctlr->tssi_5ghz[1] = val >> 8; /* [-3] */
val = rom_read(ctlr, EepromTssi25ghz);
ctlr->tssi_5ghz[2] = val & 0xff; /* [-2] */
ctlr->tssi_5ghz[3] = val >> 8; /* [-1] */
val = rom_read(ctlr, EepromTssi35ghz);
ctlr->tssi_5ghz[4] = val & 0xff; /* [+0] */
ctlr->tssi_5ghz[5] = val >> 8; /* [+1] */
val = rom_read(ctlr, EepromTssi45ghz);
ctlr->tssi_5ghz[6] = val & 0xff; /* [+2] */
ctlr->tssi_5ghz[7] = val >> 8; /* [+3] */
val = rom_read(ctlr, EepromTssi55ghz);
ctlr->tssi_5ghz[8] = val & 0xff; /* [+4] */
ctlr->step_5ghz = val >> 8;
/* check that ref value is correct, otherwise disable calibration */
if(ctlr->tssi_5ghz[4] == 0xff)
ctlr->calib_5ghz = 0;
/* read RSSI offsets and LNA gains from EEPROM */
val = rom_read(ctlr, EepromRssi12ghz);
ctlr->rssi_2ghz[0] = val & 0xff; /* Ant A */
ctlr->rssi_2ghz[1] = val >> 8; /* Ant B */
val = rom_read(ctlr, EepromRssi22ghz);
if(ctlr->mac_ver >= 0x3071){
/*
* On RT3090 chips (limited to 2 Rx chains), this ROM
* field contains the Tx mixer gain for the 2GHz band.
*/
if((val & 0xff) != 0xff)
ctlr->txmixgain_2ghz = val & 0x7;
}else
ctlr->rssi_2ghz[2] = val & 0xff; /* Ant C */
ctlr->lna[2] = val >> 8; /* channel group 2 */
val = rom_read(ctlr, EepromRssi15ghz);
ctlr->rssi_5ghz[0] = val & 0xff; /* Ant A */
ctlr->rssi_5ghz[1] = val >> 8; /* Ant B */
val = rom_read(ctlr, EepromRssi25ghz);
ctlr->rssi_5ghz[2] = val & 0xff; /* Ant C */
ctlr->lna[3] = val >> 8; /* channel group 3 */
val = rom_read(ctlr, EepromLna);
if(ctlr->mac_ver >= 0x3071)
ctlr->lna[0] = DefLna;
else /* channel group 0 */
ctlr->lna[0] = val & 0xff;
ctlr->lna[1] = val >> 8; /* channel group 1 */
/* fix broken 5GHz LNA entries */
if(ctlr->lna[2] == 0 || ctlr->lna[2] == 0xff){
ctlr->lna[2] = ctlr->lna[1];
}
if(ctlr->lna[3] == 0 || ctlr->lna[3] == 0xff){
ctlr->lna[3] = ctlr->lna[1];
}
/* fix broken RSSI offset entries */
for(ant = 0; ant < 3; ant++){
if(ctlr->rssi_2ghz[ant] < -10 || ctlr->rssi_2ghz[ant] > 10){
ctlr->rssi_2ghz[ant] = 0;
}
if(ctlr->rssi_5ghz[ant] < -10 || ctlr->rssi_5ghz[ant] > 10){
ctlr->rssi_5ghz[ant] = 0;
}
}
return 0;
}
static const char *
getrfname(u8int rev)
{
if((rev == 0) || (rev >= nelem(rfnames)))
return "unknown";
if(rfnames[rev][0] == '\0')
return "unknown";
return rfnames[rev];
}
static int
rbplant(Ctlr *ctlr, int i)
{
Block *b;
uchar *p;
b = iallocb(Rbufsize + 256);
if(b == nil)
return -1;
b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, 256);
memset(b->rp, 0, Rbufsize);
ctlr->rx.b[i] = b;
p = (uchar*)&ctlr->rx.p[i * 4]; /* sdp0 */
memset(p, 0, Rdscsize);
put32(p, PCIWADDR(b->rp));
p = (uchar*)&ctlr->rx.p[i * 4 + 1]; /* sdl0 */
p += 2; /* sdl1 */
put16(p, Rbufsize);;
return 0;
}
static char*
allocrx(Ctlr *ctlr, RXQ *rx)
{
int i;
if(rx->b == nil)
rx->b = malloc(sizeof(Block*) * Nrx);
if(rx->p == nil) /* Rx descriptors */
rx->p = mallocalign(Nrx * Rdscsize, 16, 0, 0);
if(rx->b == nil || rx->p == nil)
return "no memory for rx ring";
memset(rx->p, 0, Nrx * Rdscsize);
for(i=0; i<Nrx; i++){
if(rx->b[i] != nil){
freeb(rx->b[i]);
rx->b[i] = nil;
}
if(rbplant(ctlr, i) < 0)
return "no memory for rx descriptors";
}
rx->i = 0;
return nil;
}
static void
freerx(Ctlr *, RXQ *rx)
{
int i;
for(i = 0; i < Nrx; i++){
if(rx->b[i] != nil){
freeb(rx->b[i]);
rx->b[i] = nil;
}
}
free(rx->b);
free(rx->p);
rx->p = nil;
rx->b = nil;
rx->i = 0;
}
static char*
alloctx(Ctlr *, TXQ *tx)
{
if(tx->b == nil)
tx->b = malloc(sizeof(Block*) * Ntx);
if(tx->d == nil) /* Tx descriptors */
tx->d = mallocalign(Ntx * Tdscsize, 16, 0, 0);
if(tx->b == nil || tx->d == nil)
return "no memory for tx ring";
memset(tx->d, 0, Ntx * Tdscsize);
memset(tx->b, 0, Ntx * sizeof(Block*));
tx->i = 0;
return nil;
}
static void
freetx(Ctlr *, TXQ *tx)
{
free(tx->b);
free(tx->d);
tx->d = nil;
tx->i = 0;
}
static char*
alloctxpool(Ctlr *ctlr)
{
Pool *pool;
pool = &ctlr->pool;
if(pool->p == nil)
pool->p = mallocalign(Ntxpool * TxwiDmaSz, 4096, 0, 0);
if(pool->p == nil)
return "no memory for pool";
memset(pool->p, 0, Ntxpool * TxwiDmaSz);
pool->i = 0;
return 0;
}
static char*
initring(Ctlr *ctlr)
{
int qid;
char *err;
/*
* Allocate Tx (4 EDCAs + HCCA + Mgt) and Rx rings.
*/
for(qid = 0; qid < 6; qid++){
if((err = alloctx(ctlr, &ctlr->tx[qid])) != nil)
goto fail1;
}
if((err = allocrx(ctlr, &ctlr->rx)) != nil)
goto fail1;
if((err = alloctxpool(ctlr)) != nil)
goto fail2;
/* mgmt ring is broken on RT2860C, use EDCA AC VO ring instead */
ctlr->mgtqid = (ctlr->mac_ver == 0x2860 && ctlr->mac_rev == 0x0100) ?
3 : 5;
return nil;
fail2: freerx(ctlr, &ctlr->rx);
return err;
fail1: while(--qid >= 0)
freetx(ctlr, &ctlr->tx[qid]);
return err;
}
static int
rt2860init(Ether *edev)
{
Ctlr *ctlr;
int ntries;
char *err;
u32int tmp;
SET(tmp);
ctlr = edev->ctlr;
/* wait for NIC to initialize */
for(ntries = 0; ntries < 100; ntries++){
tmp = csr32r(ctlr, AsicVerId);
if(tmp != 0 && tmp != 0xffffffff)
break;
microdelay(10);
}
if(ntries == 100){
print("timeout waiting for NIC to initialize");
return -1;
}
ctlr->mac_ver = tmp >> 16;
ctlr->mac_rev = tmp & 0xffff;
if(ctlr->mac_ver != 0x2860){
switch(ctlr->pdev->did){
default:
break;
case RalinkRT2890:
case RalinkRT2790:
case RalinkRT3090:
case AwtRT2890:
ctlr->flags = AdvancedPs;
break;
}
}
/* retrieve RF rev. no and various other things from EEPROM */
eepromread(edev);
print("MAC/BBP RT%X (rev 0x%04X), RF %s (MIMO %dT%dR)\n",
ctlr->mac_ver, ctlr->mac_rev,
getrfname(ctlr->rf_rev), ctlr->ntxchains, ctlr->nrxchains);
if((err = initring(ctlr)) != nil){
print("error: %s", err);
return -1;
}
return 0;
}
static Ctlr *rt2860head, *rt2860tail;
static void
rt2860pci(void)
{
Pcidev *pdev;
pdev = nil;
while(pdev = pcimatch(pdev, 0, 0)){
Ctlr *ctlr;
void *mem;
if(pdev->ccrb != 2 || pdev->ccru != 0x80)
continue;
if(pdev->vid != 0x1814) /* Ralink */
continue;
switch(pdev->did){
default:
continue;
case RalinkRT2790:
case RalinkRT3090:
break;
}
pcisetbme(pdev);
pcisetpms(pdev, 0);
ctlr = malloc(sizeof(Ctlr));
if(ctlr == nil){
print("rt2860: unable to alloc Ctlr\n");
continue;
}
ctlr->port = pdev->mem[0].bar & ~0x0F;
mem = vmap(pdev->mem[0].bar & ~0x0F, pdev->mem[0].size);
if(mem == nil){
print("rt2860: can't map %8.8luX\n", pdev->mem[0].bar);
free(ctlr);
continue;
}
ctlr->nic = mem;
ctlr->pdev = pdev;
if(rt2860head != nil)
rt2860tail->link = ctlr;
else
rt2860head = ctlr;
rt2860tail = ctlr;
}
}
static int
rt2860pnp(Ether* edev)
{
Ctlr *ctlr;
if(rt2860head == nil)
rt2860pci();
again:
for(ctlr = rt2860head; ctlr != nil; ctlr = ctlr->link){
if(ctlr->active)
continue;
if(edev->port == 0 || edev->port == ctlr->port){
ctlr->active = 1;
break;
}
}
if(ctlr == nil)
return -1;
edev->ctlr = ctlr;
edev->port = ctlr->port;
edev->irq = ctlr->pdev->intl;
edev->tbdf = ctlr->pdev->tbdf;
edev->arg = edev;
edev->attach = rt2860attach;
edev->ifstat = rt2860ifstat;
edev->ctl = rt2860ctl;
edev->promiscuous = rt2860promiscuous;
edev->multicast = rt2860multicast;
edev->mbps = 10;
if(rt2860init(edev) < 0){
edev->ctlr = nil;
goto again;
}
intrenable(edev->irq, rt2860interrupt, edev, edev->tbdf, edev->name);
return 0;
}
void
etherrt2860link(void)
{
addethercard("rt2860", rt2860pnp);
}