/* * 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/pci.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; uvlong port; 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 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; ib[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; if(pdev->mem[0].bar & 1) continue; switch(pdev->did){ default: continue; case RalinkRT2790: case RalinkRT3090: break; } ctlr = malloc(sizeof(Ctlr)); if(ctlr == nil){ print("rt2860: unable to alloc Ctlr\n"); continue; } ctlr->port = pdev->mem[0].bar & ~0xF; mem = vmap(ctlr->port, pdev->mem[0].size); if(mem == nil){ print("rt2860: can't map %llux\n", ctlr->port); 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; pcienable(ctlr->pdev); pcisetbme(ctlr->pdev); 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); }