Index: head/sys/dev/ath/ath_hal/ah.c =================================================================== --- head/sys/dev/ath/ath_hal/ah.c (revision 280827) +++ head/sys/dev/ath/ath_hal/ah.c (revision 280828) @@ -1,1435 +1,1469 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_eeprom.h" /* for 5ghz fast clock flag */ #include "ar5416/ar5416reg.h" /* NB: includes ar5212reg.h */ #include "ar9003/ar9300_devid.h" /* linker set of registered chips */ OS_SET_DECLARE(ah_chips, struct ath_hal_chip); /* * Check the set of registered chips to see if any recognize * the device as one they can support. */ const char* ath_hal_probe(uint16_t vendorid, uint16_t devid) { struct ath_hal_chip * const *pchip; OS_SET_FOREACH(pchip, ah_chips) { const char *name = (*pchip)->probe(vendorid, devid); if (name != AH_NULL) return name; } return AH_NULL; } /* * Attach detects device chip revisions, initializes the hwLayer * function list, reads EEPROM information, * selects reset vectors, and performs a short self test. * Any failures will return an error that should cause a hardware * disable. */ struct ath_hal* ath_hal_attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *error) { struct ath_hal_chip * const *pchip; OS_SET_FOREACH(pchip, ah_chips) { struct ath_hal_chip *chip = *pchip; struct ath_hal *ah; /* XXX don't have vendorid, assume atheros one works */ if (chip->probe(ATHEROS_VENDOR_ID, devid) == AH_NULL) continue; ah = chip->attach(devid, sc, st, sh, eepromdata, ah_config, error); if (ah != AH_NULL) { /* copy back private state to public area */ ah->ah_devid = AH_PRIVATE(ah)->ah_devid; ah->ah_subvendorid = AH_PRIVATE(ah)->ah_subvendorid; ah->ah_macVersion = AH_PRIVATE(ah)->ah_macVersion; ah->ah_macRev = AH_PRIVATE(ah)->ah_macRev; ah->ah_phyRev = AH_PRIVATE(ah)->ah_phyRev; ah->ah_analog5GhzRev = AH_PRIVATE(ah)->ah_analog5GhzRev; ah->ah_analog2GhzRev = AH_PRIVATE(ah)->ah_analog2GhzRev; return ah; } } return AH_NULL; } const char * ath_hal_mac_name(struct ath_hal *ah) { switch (ah->ah_macVersion) { case AR_SREV_VERSION_CRETE: case AR_SREV_VERSION_MAUI_1: return "5210"; case AR_SREV_VERSION_MAUI_2: case AR_SREV_VERSION_OAHU: return "5211"; case AR_SREV_VERSION_VENICE: return "5212"; case AR_SREV_VERSION_GRIFFIN: return "2413"; case AR_SREV_VERSION_CONDOR: return "5424"; case AR_SREV_VERSION_EAGLE: return "5413"; case AR_SREV_VERSION_COBRA: return "2415"; case AR_SREV_2425: /* Swan */ return "2425"; case AR_SREV_2417: /* Nala */ return "2417"; case AR_XSREV_VERSION_OWL_PCI: return "5416"; case AR_XSREV_VERSION_OWL_PCIE: return "5418"; case AR_XSREV_VERSION_HOWL: return "9130"; case AR_XSREV_VERSION_SOWL: return "9160"; case AR_XSREV_VERSION_MERLIN: if (AH_PRIVATE(ah)->ah_ispcie) return "9280"; return "9220"; case AR_XSREV_VERSION_KITE: return "9285"; case AR_XSREV_VERSION_KIWI: if (AH_PRIVATE(ah)->ah_ispcie) return "9287"; return "9227"; case AR_SREV_VERSION_AR9380: if (ah->ah_macRev >= AR_SREV_REVISION_AR9580_10) return "9580"; return "9380"; case AR_SREV_VERSION_AR9460: return "9460"; case AR_SREV_VERSION_AR9330: return "9330"; case AR_SREV_VERSION_AR9340: return "9340"; case AR_SREV_VERSION_QCA9550: /* XXX should say QCA, not AR */ return "9550"; case AR_SREV_VERSION_AR9485: return "9485"; case AR_SREV_VERSION_QCA9565: /* XXX should say QCA, not AR */ return "9565"; } return "????"; } /* * Return the mask of available modes based on the hardware capabilities. */ u_int ath_hal_getwirelessmodes(struct ath_hal*ah) { return ath_hal_getWirelessModes(ah); } /* linker set of registered RF backends */ OS_SET_DECLARE(ah_rfs, struct ath_hal_rf); /* * Check the set of registered RF backends to see if * any recognize the device as one they can support. */ struct ath_hal_rf * ath_hal_rfprobe(struct ath_hal *ah, HAL_STATUS *ecode) { struct ath_hal_rf * const *prf; OS_SET_FOREACH(prf, ah_rfs) { struct ath_hal_rf *rf = *prf; if (rf->probe(ah)) return rf; } *ecode = HAL_ENOTSUPP; return AH_NULL; } const char * ath_hal_rf_name(struct ath_hal *ah) { switch (ah->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { case 0: /* 5210 */ return "5110"; /* NB: made up */ case AR_RAD5111_SREV_MAJOR: case AR_RAD5111_SREV_PROD: return "5111"; case AR_RAD2111_SREV_MAJOR: return "2111"; case AR_RAD5112_SREV_MAJOR: case AR_RAD5112_SREV_2_0: case AR_RAD5112_SREV_2_1: return "5112"; case AR_RAD2112_SREV_MAJOR: case AR_RAD2112_SREV_2_0: case AR_RAD2112_SREV_2_1: return "2112"; case AR_RAD2413_SREV_MAJOR: return "2413"; case AR_RAD5413_SREV_MAJOR: return "5413"; case AR_RAD2316_SREV_MAJOR: return "2316"; case AR_RAD2317_SREV_MAJOR: return "2317"; case AR_RAD5424_SREV_MAJOR: return "5424"; case AR_RAD5133_SREV_MAJOR: return "5133"; case AR_RAD2133_SREV_MAJOR: return "2133"; case AR_RAD5122_SREV_MAJOR: return "5122"; case AR_RAD2122_SREV_MAJOR: return "2122"; } return "????"; } /* * Poll the register looking for a specific value. */ HAL_BOOL ath_hal_wait(struct ath_hal *ah, u_int reg, uint32_t mask, uint32_t val) { #define AH_TIMEOUT 1000 return ath_hal_waitfor(ah, reg, mask, val, AH_TIMEOUT); #undef AH_TIMEOUT } HAL_BOOL ath_hal_waitfor(struct ath_hal *ah, u_int reg, uint32_t mask, uint32_t val, uint32_t timeout) { int i; for (i = 0; i < timeout; i++) { if ((OS_REG_READ(ah, reg) & mask) == val) return AH_TRUE; OS_DELAY(10); } HALDEBUG(ah, HAL_DEBUG_REGIO | HAL_DEBUG_PHYIO, "%s: timeout on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n", __func__, reg, OS_REG_READ(ah, reg), mask, val); return AH_FALSE; } /* * Reverse the bits starting at the low bit for a value of * bit_count in size */ uint32_t ath_hal_reverseBits(uint32_t val, uint32_t n) { uint32_t retval; int i; for (i = 0, retval = 0; i < n; i++) { retval = (retval << 1) | (val & 1); val >>= 1; } return retval; } /* 802.11n related timing definitions */ #define OFDM_PLCP_BITS 22 #define HT_L_STF 8 #define HT_L_LTF 8 #define HT_L_SIG 4 #define HT_SIG 8 #define HT_STF 4 #define HT_LTF(n) ((n) * 4) #define HT_RC_2_MCS(_rc) ((_rc) & 0xf) #define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) #define IS_HT_RATE(_rc) ( (_rc) & IEEE80211_RATE_MCS) /* * Calculate the duration of a packet whether it is 11n or legacy. */ uint32_t ath_hal_pkt_txtime(struct ath_hal *ah, const HAL_RATE_TABLE *rates, uint32_t frameLen, uint16_t rateix, HAL_BOOL isht40, HAL_BOOL shortPreamble) { uint8_t rc; int numStreams; rc = rates->info[rateix].rateCode; /* Legacy rate? Return the old way */ if (! IS_HT_RATE(rc)) return ath_hal_computetxtime(ah, rates, frameLen, rateix, shortPreamble); /* 11n frame - extract out the number of spatial streams */ numStreams = HT_RC_2_STREAMS(rc); KASSERT(numStreams > 0 && numStreams <= 4, ("number of spatial streams needs to be 1..3: MCS rate 0x%x!", rateix)); return ath_computedur_ht(frameLen, rc, numStreams, isht40, shortPreamble); } static const uint16_t ht20_bps[32] = { 26, 52, 78, 104, 156, 208, 234, 260, 52, 104, 156, 208, 312, 416, 468, 520, 78, 156, 234, 312, 468, 624, 702, 780, 104, 208, 312, 416, 624, 832, 936, 1040 }; static const uint16_t ht40_bps[32] = { 54, 108, 162, 216, 324, 432, 486, 540, 108, 216, 324, 432, 648, 864, 972, 1080, 162, 324, 486, 648, 972, 1296, 1458, 1620, 216, 432, 648, 864, 1296, 1728, 1944, 2160 }; /* * Calculate the transmit duration of an 11n frame. */ uint32_t ath_computedur_ht(uint32_t frameLen, uint16_t rate, int streams, HAL_BOOL isht40, HAL_BOOL isShortGI) { uint32_t bitsPerSymbol, numBits, numSymbols, txTime; KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate)); KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate)); if (isht40) bitsPerSymbol = ht40_bps[rate & 0x1f]; else bitsPerSymbol = ht20_bps[rate & 0x1f]; numBits = OFDM_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); if (isShortGI) txTime = ((numSymbols * 18) + 4) / 5; /* 3.6us */ else txTime = numSymbols * 4; /* 4us */ return txTime + HT_L_STF + HT_L_LTF + HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams); } /* * Compute the time to transmit a frame of length frameLen bytes * using the specified rate, phy, and short preamble setting. */ uint16_t ath_hal_computetxtime(struct ath_hal *ah, const HAL_RATE_TABLE *rates, uint32_t frameLen, uint16_t rateix, HAL_BOOL shortPreamble) { uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime; uint32_t kbps; /* Warn if this function is called for 11n rates; it should not be! */ if (IS_HT_RATE(rates->info[rateix].rateCode)) ath_hal_printf(ah, "%s: MCS rate? (index %d; hwrate 0x%x)\n", __func__, rateix, rates->info[rateix].rateCode); kbps = rates->info[rateix].rateKbps; /* * index can be invalid duting dynamic Turbo transitions. * XXX */ if (kbps == 0) return 0; switch (rates->info[rateix].phy) { case IEEE80211_T_CCK: phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS; if (shortPreamble && rates->info[rateix].shortPreamble) phyTime >>= 1; numBits = frameLen << 3; txTime = CCK_SIFS_TIME + phyTime + ((numBits * 1000)/kbps); break; case IEEE80211_T_OFDM: bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000; HALASSERT(bitsPerSymbol != 0); numBits = OFDM_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); txTime = OFDM_SIFS_TIME + OFDM_PREAMBLE_TIME + (numSymbols * OFDM_SYMBOL_TIME); break; case IEEE80211_T_OFDM_HALF: bitsPerSymbol = (kbps * OFDM_HALF_SYMBOL_TIME) / 1000; HALASSERT(bitsPerSymbol != 0); numBits = OFDM_HALF_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); txTime = OFDM_HALF_SIFS_TIME + OFDM_HALF_PREAMBLE_TIME + (numSymbols * OFDM_HALF_SYMBOL_TIME); break; case IEEE80211_T_OFDM_QUARTER: bitsPerSymbol = (kbps * OFDM_QUARTER_SYMBOL_TIME) / 1000; HALASSERT(bitsPerSymbol != 0); numBits = OFDM_QUARTER_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); txTime = OFDM_QUARTER_SIFS_TIME + OFDM_QUARTER_PREAMBLE_TIME + (numSymbols * OFDM_QUARTER_SYMBOL_TIME); break; case IEEE80211_T_TURBO: bitsPerSymbol = (kbps * TURBO_SYMBOL_TIME) / 1000; HALASSERT(bitsPerSymbol != 0); numBits = TURBO_PLCP_BITS + (frameLen << 3); numSymbols = howmany(numBits, bitsPerSymbol); txTime = TURBO_SIFS_TIME + TURBO_PREAMBLE_TIME + (numSymbols * TURBO_SYMBOL_TIME); break; default: HALDEBUG(ah, HAL_DEBUG_PHYIO, "%s: unknown phy %u (rate ix %u)\n", __func__, rates->info[rateix].phy, rateix); txTime = 0; break; } return txTime; } int ath_hal_get_curmode(struct ath_hal *ah, const struct ieee80211_channel *chan) { /* * Pick a default mode at bootup. A channel change is inevitable. */ if (!chan) return HAL_MODE_11NG_HT20; if (IEEE80211_IS_CHAN_TURBO(chan)) return HAL_MODE_TURBO; /* check for NA_HT before plain A, since IS_CHAN_A includes NA_HT */ if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT20(chan)) return HAL_MODE_11NA_HT20; if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT40U(chan)) return HAL_MODE_11NA_HT40PLUS; if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT40D(chan)) return HAL_MODE_11NA_HT40MINUS; if (IEEE80211_IS_CHAN_A(chan)) return HAL_MODE_11A; /* check for NG_HT before plain G, since IS_CHAN_G includes NG_HT */ if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT20(chan)) return HAL_MODE_11NG_HT20; if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT40U(chan)) return HAL_MODE_11NG_HT40PLUS; if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT40D(chan)) return HAL_MODE_11NG_HT40MINUS; /* * XXX For FreeBSD, will this work correctly given the DYN * chan mode (OFDM+CCK dynamic) ? We have pure-G versions DYN-BG.. */ if (IEEE80211_IS_CHAN_G(chan)) return HAL_MODE_11G; if (IEEE80211_IS_CHAN_B(chan)) return HAL_MODE_11B; HALASSERT(0); return HAL_MODE_11NG_HT20; } typedef enum { WIRELESS_MODE_11a = 0, WIRELESS_MODE_TURBO = 1, WIRELESS_MODE_11b = 2, WIRELESS_MODE_11g = 3, WIRELESS_MODE_108g = 4, WIRELESS_MODE_MAX } WIRELESS_MODE; static WIRELESS_MODE ath_hal_chan2wmode(struct ath_hal *ah, const struct ieee80211_channel *chan) { if (IEEE80211_IS_CHAN_B(chan)) return WIRELESS_MODE_11b; if (IEEE80211_IS_CHAN_G(chan)) return WIRELESS_MODE_11g; if (IEEE80211_IS_CHAN_108G(chan)) return WIRELESS_MODE_108g; if (IEEE80211_IS_CHAN_TURBO(chan)) return WIRELESS_MODE_TURBO; return WIRELESS_MODE_11a; } /* * Convert between microseconds and core system clocks. */ /* 11a Turbo 11b 11g 108g */ static const uint8_t CLOCK_RATE[] = { 40, 80, 22, 44, 88 }; #define CLOCK_FAST_RATE_5GHZ_OFDM 44 u_int ath_hal_mac_clks(struct ath_hal *ah, u_int usecs) { const struct ieee80211_channel *c = AH_PRIVATE(ah)->ah_curchan; u_int clks; /* NB: ah_curchan may be null when called attach time */ /* XXX merlin and later specific workaround - 5ghz fast clock is 44 */ if (c != AH_NULL && IS_5GHZ_FAST_CLOCK_EN(ah, c)) { clks = usecs * CLOCK_FAST_RATE_5GHZ_OFDM; if (IEEE80211_IS_CHAN_HT40(c)) clks <<= 1; } else if (c != AH_NULL) { clks = usecs * CLOCK_RATE[ath_hal_chan2wmode(ah, c)]; if (IEEE80211_IS_CHAN_HT40(c)) clks <<= 1; } else clks = usecs * CLOCK_RATE[WIRELESS_MODE_11b]; /* Compensate for half/quarter rate */ if (c != AH_NULL && IEEE80211_IS_CHAN_HALF(c)) clks = clks / 2; else if (c != AH_NULL && IEEE80211_IS_CHAN_QUARTER(c)) clks = clks / 4; return clks; } u_int ath_hal_mac_usec(struct ath_hal *ah, u_int clks) { const struct ieee80211_channel *c = AH_PRIVATE(ah)->ah_curchan; u_int usec; /* NB: ah_curchan may be null when called attach time */ /* XXX merlin and later specific workaround - 5ghz fast clock is 44 */ if (c != AH_NULL && IS_5GHZ_FAST_CLOCK_EN(ah, c)) { usec = clks / CLOCK_FAST_RATE_5GHZ_OFDM; if (IEEE80211_IS_CHAN_HT40(c)) usec >>= 1; } else if (c != AH_NULL) { usec = clks / CLOCK_RATE[ath_hal_chan2wmode(ah, c)]; if (IEEE80211_IS_CHAN_HT40(c)) usec >>= 1; } else usec = clks / CLOCK_RATE[WIRELESS_MODE_11b]; return usec; } /* * Setup a h/w rate table's reverse lookup table and * fill in ack durations. This routine is called for * each rate table returned through the ah_getRateTable * method. The reverse lookup tables are assumed to be * initialized to zero (or at least the first entry). * We use this as a key that indicates whether or not * we've previously setup the reverse lookup table. * * XXX not reentrant, but shouldn't matter */ void ath_hal_setupratetable(struct ath_hal *ah, HAL_RATE_TABLE *rt) { #define N(a) (sizeof(a)/sizeof(a[0])) int i; if (rt->rateCodeToIndex[0] != 0) /* already setup */ return; for (i = 0; i < N(rt->rateCodeToIndex); i++) rt->rateCodeToIndex[i] = (uint8_t) -1; for (i = 0; i < rt->rateCount; i++) { uint8_t code = rt->info[i].rateCode; uint8_t cix = rt->info[i].controlRate; HALASSERT(code < N(rt->rateCodeToIndex)); rt->rateCodeToIndex[code] = i; HALASSERT((code | rt->info[i].shortPreamble) < N(rt->rateCodeToIndex)); rt->rateCodeToIndex[code | rt->info[i].shortPreamble] = i; /* * XXX for 11g the control rate to use for 5.5 and 11 Mb/s * depends on whether they are marked as basic rates; * the static tables are setup with an 11b-compatible * 2Mb/s rate which will work but is suboptimal */ rt->info[i].lpAckDuration = ath_hal_computetxtime(ah, rt, WLAN_CTRL_FRAME_SIZE, cix, AH_FALSE); rt->info[i].spAckDuration = ath_hal_computetxtime(ah, rt, WLAN_CTRL_FRAME_SIZE, cix, AH_TRUE); } #undef N } HAL_STATUS ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t *result) { const HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; switch (type) { case HAL_CAP_REG_DMN: /* regulatory domain */ *result = AH_PRIVATE(ah)->ah_currentRD; return HAL_OK; case HAL_CAP_DFS_DMN: /* DFS Domain */ *result = AH_PRIVATE(ah)->ah_dfsDomain; return HAL_OK; case HAL_CAP_CIPHER: /* cipher handled in hardware */ case HAL_CAP_TKIP_MIC: /* handle TKIP MIC in hardware */ return HAL_ENOTSUPP; case HAL_CAP_TKIP_SPLIT: /* hardware TKIP uses split keys */ return HAL_ENOTSUPP; case HAL_CAP_PHYCOUNTERS: /* hardware PHY error counters */ return pCap->halHwPhyCounterSupport ? HAL_OK : HAL_ENXIO; case HAL_CAP_WME_TKIPMIC: /* hardware can do TKIP MIC when WMM is turned on */ return HAL_ENOTSUPP; case HAL_CAP_DIVERSITY: /* hardware supports fast diversity */ return HAL_ENOTSUPP; case HAL_CAP_KEYCACHE_SIZE: /* hardware key cache size */ *result = pCap->halKeyCacheSize; return HAL_OK; case HAL_CAP_NUM_TXQUEUES: /* number of hardware tx queues */ *result = pCap->halTotalQueues; return HAL_OK; case HAL_CAP_VEOL: /* hardware supports virtual EOL */ return pCap->halVEOLSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_PSPOLL: /* hardware PS-Poll support works */ return pCap->halPSPollBroken ? HAL_ENOTSUPP : HAL_OK; case HAL_CAP_COMPRESSION: return pCap->halCompressSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_BURST: return pCap->halBurstSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_FASTFRAME: return pCap->halFastFramesSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_DIAG: /* hardware diagnostic support */ *result = AH_PRIVATE(ah)->ah_diagreg; return HAL_OK; case HAL_CAP_TXPOW: /* global tx power limit */ switch (capability) { case 0: /* facility is supported */ return HAL_OK; case 1: /* current limit */ *result = AH_PRIVATE(ah)->ah_powerLimit; return HAL_OK; case 2: /* current max tx power */ *result = AH_PRIVATE(ah)->ah_maxPowerLevel; return HAL_OK; case 3: /* scale factor */ *result = AH_PRIVATE(ah)->ah_tpScale; return HAL_OK; } return HAL_ENOTSUPP; case HAL_CAP_BSSIDMASK: /* hardware supports bssid mask */ return pCap->halBssIdMaskSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_MCAST_KEYSRCH: /* multicast frame keycache search */ return pCap->halMcastKeySrchSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_TSF_ADJUST: /* hardware has beacon tsf adjust */ return HAL_ENOTSUPP; case HAL_CAP_RFSILENT: /* rfsilent support */ switch (capability) { case 0: /* facility is supported */ return pCap->halRfSilentSupport ? HAL_OK : HAL_ENOTSUPP; case 1: /* current setting */ return AH_PRIVATE(ah)->ah_rfkillEnabled ? HAL_OK : HAL_ENOTSUPP; case 2: /* rfsilent config */ *result = AH_PRIVATE(ah)->ah_rfsilent; return HAL_OK; } return HAL_ENOTSUPP; case HAL_CAP_11D: return HAL_OK; case HAL_CAP_HT: return pCap->halHTSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_GTXTO: return pCap->halGTTSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_FAST_CC: return pCap->halFastCCSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_TX_CHAINMASK: /* mask of TX chains supported */ *result = pCap->halTxChainMask; return HAL_OK; case HAL_CAP_RX_CHAINMASK: /* mask of RX chains supported */ *result = pCap->halRxChainMask; return HAL_OK; case HAL_CAP_NUM_GPIO_PINS: *result = pCap->halNumGpioPins; return HAL_OK; case HAL_CAP_CST: return pCap->halCSTSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_RTS_AGGR_LIMIT: *result = pCap->halRtsAggrLimit; return HAL_OK; case HAL_CAP_4ADDR_AGGR: return pCap->hal4AddrAggrSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_EXT_CHAN_DFS: return pCap->halExtChanDfsSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_RX_STBC: return pCap->halRxStbcSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_TX_STBC: return pCap->halTxStbcSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_COMBINED_RADAR_RSSI: return pCap->halUseCombinedRadarRssi ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_AUTO_SLEEP: return pCap->halAutoSleepSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_MBSSID_AGGR_SUPPORT: return pCap->halMbssidAggrSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_SPLIT_4KB_TRANS: /* hardware handles descriptors straddling 4k page boundary */ return pCap->hal4kbSplitTransSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_REG_FLAG: *result = AH_PRIVATE(ah)->ah_currentRDext; return HAL_OK; case HAL_CAP_ENHANCED_DMA_SUPPORT: return pCap->halEnhancedDmaSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_NUM_TXMAPS: *result = pCap->halNumTxMaps; return HAL_OK; case HAL_CAP_TXDESCLEN: *result = pCap->halTxDescLen; return HAL_OK; case HAL_CAP_TXSTATUSLEN: *result = pCap->halTxStatusLen; return HAL_OK; case HAL_CAP_RXSTATUSLEN: *result = pCap->halRxStatusLen; return HAL_OK; case HAL_CAP_RXFIFODEPTH: switch (capability) { case HAL_RX_QUEUE_HP: *result = pCap->halRxHpFifoDepth; return HAL_OK; case HAL_RX_QUEUE_LP: *result = pCap->halRxLpFifoDepth; return HAL_OK; default: return HAL_ENOTSUPP; } case HAL_CAP_RXBUFSIZE: case HAL_CAP_NUM_MR_RETRIES: *result = pCap->halNumMRRetries; return HAL_OK; case HAL_CAP_BT_COEX: return pCap->halBtCoexSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_SPECTRAL_SCAN: return pCap->halSpectralScanSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_HT20_SGI: return pCap->halHTSGI20Support ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_RXTSTAMP_PREC: /* rx desc tstamp precision (bits) */ *result = pCap->halTstampPrecision; return HAL_OK; case HAL_CAP_ANT_DIV_COMB: /* AR9285/AR9485 LNA diversity */ return pCap->halAntDivCombSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_ENHANCED_DFS_SUPPORT: return pCap->halEnhancedDfsSupport ? HAL_OK : HAL_ENOTSUPP; /* FreeBSD-specific entries for now */ case HAL_CAP_RXORN_FATAL: /* HAL_INT_RXORN treated as fatal */ return AH_PRIVATE(ah)->ah_rxornIsFatal ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_INTRMASK: /* mask of supported interrupts */ *result = pCap->halIntrMask; return HAL_OK; case HAL_CAP_BSSIDMATCH: /* hardware has disable bssid match */ return pCap->halBssidMatchSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_STREAMS: /* number of 11n spatial streams */ switch (capability) { case 0: /* TX */ *result = pCap->halTxStreams; return HAL_OK; case 1: /* RX */ *result = pCap->halRxStreams; return HAL_OK; default: return HAL_ENOTSUPP; } case HAL_CAP_RXDESC_SELFLINK: /* hardware supports self-linked final RX descriptors correctly */ return pCap->halHasRxSelfLinkedTail ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_LONG_RXDESC_TSF: /* 32 bit TSF in RX descriptor? */ return pCap->halHasLongRxDescTsf ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_BB_READ_WAR: /* Baseband read WAR */ return pCap->halHasBBReadWar? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_SERIALISE_WAR: /* PCI register serialisation */ return pCap->halSerialiseRegWar ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_MFP: /* Management frame protection setting */ *result = pCap->halMfpSupport; return HAL_OK; case HAL_CAP_RX_LNA_MIXING: /* Hardware uses an RX LNA mixer to map 2 antennas to a 1 stream receiver */ return pCap->halRxUsingLnaMixing ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_DO_MYBEACON: /* Hardware supports filtering my-beacons */ return pCap->halRxDoMyBeacon ? HAL_OK : HAL_ENOTSUPP; default: return HAL_EINVAL; } } HAL_BOOL ath_hal_setcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t setting, HAL_STATUS *status) { switch (type) { case HAL_CAP_TXPOW: switch (capability) { case 3: if (setting <= HAL_TP_SCALE_MIN) { AH_PRIVATE(ah)->ah_tpScale = setting; return AH_TRUE; } break; } break; case HAL_CAP_RFSILENT: /* rfsilent support */ /* * NB: allow even if halRfSilentSupport is false * in case the EEPROM is misprogrammed. */ switch (capability) { case 1: /* current setting */ AH_PRIVATE(ah)->ah_rfkillEnabled = (setting != 0); return AH_TRUE; case 2: /* rfsilent config */ /* XXX better done per-chip for validation? */ AH_PRIVATE(ah)->ah_rfsilent = setting; return AH_TRUE; } break; case HAL_CAP_REG_DMN: /* regulatory domain */ AH_PRIVATE(ah)->ah_currentRD = setting; return AH_TRUE; case HAL_CAP_RXORN_FATAL: /* HAL_INT_RXORN treated as fatal */ AH_PRIVATE(ah)->ah_rxornIsFatal = setting; return AH_TRUE; default: break; } if (status) *status = HAL_EINVAL; return AH_FALSE; } /* * Common support for getDiagState method. */ static u_int ath_hal_getregdump(struct ath_hal *ah, const HAL_REGRANGE *regs, void *dstbuf, int space) { uint32_t *dp = dstbuf; int i; for (i = 0; space >= 2*sizeof(uint32_t); i++) { uint32_t r = regs[i].start; uint32_t e = regs[i].end; *dp++ = r; *dp++ = e; space -= 2*sizeof(uint32_t); do { *dp++ = OS_REG_READ(ah, r); r += sizeof(uint32_t); space -= sizeof(uint32_t); } while (r <= e && space >= sizeof(uint32_t)); } return (char *) dp - (char *) dstbuf; } static void ath_hal_setregs(struct ath_hal *ah, const HAL_REGWRITE *regs, int space) { while (space >= sizeof(HAL_REGWRITE)) { OS_REG_WRITE(ah, regs->addr, regs->value); regs++, space -= sizeof(HAL_REGWRITE); } } HAL_BOOL ath_hal_getdiagstate(struct ath_hal *ah, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize) { + switch (request) { case HAL_DIAG_REVS: *result = &AH_PRIVATE(ah)->ah_devid; *resultsize = sizeof(HAL_REVS); return AH_TRUE; case HAL_DIAG_REGS: *resultsize = ath_hal_getregdump(ah, args, *result,*resultsize); return AH_TRUE; case HAL_DIAG_SETREGS: ath_hal_setregs(ah, args, argsize); *resultsize = 0; return AH_TRUE; case HAL_DIAG_FATALERR: *result = &AH_PRIVATE(ah)->ah_fatalState[0]; *resultsize = sizeof(AH_PRIVATE(ah)->ah_fatalState); return AH_TRUE; case HAL_DIAG_EEREAD: if (argsize != sizeof(uint16_t)) return AH_FALSE; if (!ath_hal_eepromRead(ah, *(const uint16_t *)args, *result)) return AH_FALSE; *resultsize = sizeof(uint16_t); return AH_TRUE; #ifdef AH_PRIVATE_DIAG case HAL_DIAG_SETKEY: { const HAL_DIAG_KEYVAL *dk; if (argsize != sizeof(HAL_DIAG_KEYVAL)) return AH_FALSE; dk = (const HAL_DIAG_KEYVAL *)args; return ah->ah_setKeyCacheEntry(ah, dk->dk_keyix, &dk->dk_keyval, dk->dk_mac, dk->dk_xor); } case HAL_DIAG_RESETKEY: if (argsize != sizeof(uint16_t)) return AH_FALSE; return ah->ah_resetKeyCacheEntry(ah, *(const uint16_t *)args); #ifdef AH_SUPPORT_WRITE_EEPROM case HAL_DIAG_EEWRITE: { const HAL_DIAG_EEVAL *ee; if (argsize != sizeof(HAL_DIAG_EEVAL)) return AH_FALSE; ee = (const HAL_DIAG_EEVAL *)args; return ath_hal_eepromWrite(ah, ee->ee_off, ee->ee_data); } #endif /* AH_SUPPORT_WRITE_EEPROM */ #endif /* AH_PRIVATE_DIAG */ case HAL_DIAG_11NCOMPAT: if (argsize == 0) { *resultsize = sizeof(uint32_t); *((uint32_t *)(*result)) = AH_PRIVATE(ah)->ah_11nCompat; } else if (argsize == sizeof(uint32_t)) { AH_PRIVATE(ah)->ah_11nCompat = *(const uint32_t *)args; } else return AH_FALSE; return AH_TRUE; + case HAL_DIAG_CHANSURVEY: + *result = &AH_PRIVATE(ah)->ah_chansurvey; + *resultsize = sizeof(HAL_CHANNEL_SURVEY); + return AH_TRUE; } return AH_FALSE; } /* * Set the properties of the tx queue with the parameters * from qInfo. */ HAL_BOOL ath_hal_setTxQProps(struct ath_hal *ah, HAL_TX_QUEUE_INFO *qi, const HAL_TXQ_INFO *qInfo) { uint32_t cw; if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: inactive queue\n", __func__); return AH_FALSE; } /* XXX validate parameters */ qi->tqi_ver = qInfo->tqi_ver; qi->tqi_subtype = qInfo->tqi_subtype; qi->tqi_qflags = qInfo->tqi_qflags; qi->tqi_priority = qInfo->tqi_priority; if (qInfo->tqi_aifs != HAL_TXQ_USEDEFAULT) qi->tqi_aifs = AH_MIN(qInfo->tqi_aifs, 255); else qi->tqi_aifs = INIT_AIFS; if (qInfo->tqi_cwmin != HAL_TXQ_USEDEFAULT) { cw = AH_MIN(qInfo->tqi_cwmin, 1024); /* make sure that the CWmin is of the form (2^n - 1) */ qi->tqi_cwmin = 1; while (qi->tqi_cwmin < cw) qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1; } else qi->tqi_cwmin = qInfo->tqi_cwmin; if (qInfo->tqi_cwmax != HAL_TXQ_USEDEFAULT) { cw = AH_MIN(qInfo->tqi_cwmax, 1024); /* make sure that the CWmax is of the form (2^n - 1) */ qi->tqi_cwmax = 1; while (qi->tqi_cwmax < cw) qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1; } else qi->tqi_cwmax = INIT_CWMAX; /* Set retry limit values */ if (qInfo->tqi_shretry != 0) qi->tqi_shretry = AH_MIN(qInfo->tqi_shretry, 15); else qi->tqi_shretry = INIT_SH_RETRY; if (qInfo->tqi_lgretry != 0) qi->tqi_lgretry = AH_MIN(qInfo->tqi_lgretry, 15); else qi->tqi_lgretry = INIT_LG_RETRY; qi->tqi_cbrPeriod = qInfo->tqi_cbrPeriod; qi->tqi_cbrOverflowLimit = qInfo->tqi_cbrOverflowLimit; qi->tqi_burstTime = qInfo->tqi_burstTime; qi->tqi_readyTime = qInfo->tqi_readyTime; switch (qInfo->tqi_subtype) { case HAL_WME_UPSD: if (qi->tqi_type == HAL_TX_QUEUE_DATA) qi->tqi_intFlags = HAL_TXQ_USE_LOCKOUT_BKOFF_DIS; break; default: break; /* NB: silence compiler */ } return AH_TRUE; } HAL_BOOL ath_hal_getTxQProps(struct ath_hal *ah, HAL_TXQ_INFO *qInfo, const HAL_TX_QUEUE_INFO *qi) { if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: inactive queue\n", __func__); return AH_FALSE; } qInfo->tqi_qflags = qi->tqi_qflags; qInfo->tqi_ver = qi->tqi_ver; qInfo->tqi_subtype = qi->tqi_subtype; qInfo->tqi_qflags = qi->tqi_qflags; qInfo->tqi_priority = qi->tqi_priority; qInfo->tqi_aifs = qi->tqi_aifs; qInfo->tqi_cwmin = qi->tqi_cwmin; qInfo->tqi_cwmax = qi->tqi_cwmax; qInfo->tqi_shretry = qi->tqi_shretry; qInfo->tqi_lgretry = qi->tqi_lgretry; qInfo->tqi_cbrPeriod = qi->tqi_cbrPeriod; qInfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit; qInfo->tqi_burstTime = qi->tqi_burstTime; qInfo->tqi_readyTime = qi->tqi_readyTime; return AH_TRUE; } /* 11a Turbo 11b 11g 108g */ static const int16_t NOISE_FLOOR[] = { -96, -93, -98, -96, -93 }; /* * Read the current channel noise floor and return. * If nf cal hasn't finished, channel noise floor should be 0 * and we return a nominal value based on band and frequency. * * NB: This is a private routine used by per-chip code to * implement the ah_getChanNoise method. */ int16_t ath_hal_getChanNoise(struct ath_hal *ah, const struct ieee80211_channel *chan) { HAL_CHANNEL_INTERNAL *ichan; ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return 0; } if (ichan->rawNoiseFloor == 0) { WIRELESS_MODE mode = ath_hal_chan2wmode(ah, chan); HALASSERT(mode < WIRELESS_MODE_MAX); return NOISE_FLOOR[mode] + ath_hal_getNfAdjust(ah, ichan); } else return ichan->rawNoiseFloor + ichan->noiseFloorAdjust; } /* * Fetch the current setup of ctl/ext noise floor values. * * If the CHANNEL_MIMO_NF_VALID flag isn't set, the array is simply * populated with values from NOISE_FLOOR[] + ath_hal_getNfAdjust(). * * The caller must supply ctl/ext NF arrays which are at least * AH_MAX_CHAINS entries long. */ int ath_hal_get_mimo_chan_noise(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *nf_ctl, int16_t *nf_ext) { #ifdef AH_SUPPORT_AR5416 HAL_CHANNEL_INTERNAL *ichan; int i; ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); for (i = 0; i < AH_MAX_CHAINS; i++) { nf_ctl[i] = nf_ext[i] = 0; } return 0; } /* Return 0 if there's no valid MIMO values (yet) */ if (! (ichan->privFlags & CHANNEL_MIMO_NF_VALID)) { for (i = 0; i < AH_MAX_CHAINS; i++) { nf_ctl[i] = nf_ext[i] = 0; } return 0; } if (ichan->rawNoiseFloor == 0) { WIRELESS_MODE mode = ath_hal_chan2wmode(ah, chan); HALASSERT(mode < WIRELESS_MODE_MAX); /* * See the comment below - this could cause issues for * stations which have a very low RSSI, below the * 'normalised' NF values in NOISE_FLOOR[]. */ for (i = 0; i < AH_MAX_CHAINS; i++) { nf_ctl[i] = nf_ext[i] = NOISE_FLOOR[mode] + ath_hal_getNfAdjust(ah, ichan); } return 1; } else { /* * The value returned here from a MIMO radio is presumed to be * "good enough" as a NF calculation. As RSSI values are calculated * against this, an adjusted NF may be higher than the RSSI value * returned from a vary weak station, resulting in an obscenely * high signal strength calculation being returned. * * This should be re-evaluated at a later date, along with any * signal strength calculations which are made. Quite likely the * RSSI values will need to be adjusted to ensure the calculations * don't "wrap" when RSSI is less than the "adjusted" NF value. * ("Adjust" here is via ichan->noiseFloorAdjust.) */ for (i = 0; i < AH_MAX_CHAINS; i++) { nf_ctl[i] = ichan->noiseFloorCtl[i] + ath_hal_getNfAdjust(ah, ichan); nf_ext[i] = ichan->noiseFloorExt[i] + ath_hal_getNfAdjust(ah, ichan); } return 1; } #else return 0; #endif /* AH_SUPPORT_AR5416 */ } /* * Process all valid raw noise floors into the dBm noise floor values. * Though our device has no reference for a dBm noise floor, we perform * a relative minimization of NF's based on the lowest NF found across a * channel scan. */ void ath_hal_process_noisefloor(struct ath_hal *ah) { HAL_CHANNEL_INTERNAL *c; int16_t correct2, correct5; int16_t lowest2, lowest5; int i; /* * Find the lowest 2GHz and 5GHz noise floor values after adjusting * for statistically recorded NF/channel deviation. */ correct2 = lowest2 = 0; correct5 = lowest5 = 0; for (i = 0; i < AH_PRIVATE(ah)->ah_nchan; i++) { WIRELESS_MODE mode; int16_t nf; c = &AH_PRIVATE(ah)->ah_channels[i]; if (c->rawNoiseFloor >= 0) continue; /* XXX can't identify proper mode */ mode = IS_CHAN_5GHZ(c) ? WIRELESS_MODE_11a : WIRELESS_MODE_11g; nf = c->rawNoiseFloor + NOISE_FLOOR[mode] + ath_hal_getNfAdjust(ah, c); if (IS_CHAN_5GHZ(c)) { if (nf < lowest5) { lowest5 = nf; correct5 = NOISE_FLOOR[mode] - (c->rawNoiseFloor + ath_hal_getNfAdjust(ah, c)); } } else { if (nf < lowest2) { lowest2 = nf; correct2 = NOISE_FLOOR[mode] - (c->rawNoiseFloor + ath_hal_getNfAdjust(ah, c)); } } } /* Correct the channels to reach the expected NF value */ for (i = 0; i < AH_PRIVATE(ah)->ah_nchan; i++) { c = &AH_PRIVATE(ah)->ah_channels[i]; if (c->rawNoiseFloor >= 0) continue; /* Apply correction factor */ c->noiseFloorAdjust = ath_hal_getNfAdjust(ah, c) + (IS_CHAN_5GHZ(c) ? correct5 : correct2); HALDEBUG(ah, HAL_DEBUG_NFCAL, "%u raw nf %d adjust %d\n", c->channel, c->rawNoiseFloor, c->noiseFloorAdjust); } } /* * INI support routines. */ int ath_hal_ini_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia, int col, int regWr) { int r; HALASSERT(col < ia->cols); for (r = 0; r < ia->rows; r++) { OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0), HAL_INI_VAL(ia, r, col)); /* Analog shift register delay seems needed for Merlin - PR kern/154220 */ if (HAL_INI_VAL(ia, r, 0) >= 0x7800 && HAL_INI_VAL(ia, r, 0) < 0x7900) OS_DELAY(100); DMA_YIELD(regWr); } return regWr; } void ath_hal_ini_bank_setup(uint32_t data[], const HAL_INI_ARRAY *ia, int col) { int r; HALASSERT(col < ia->cols); for (r = 0; r < ia->rows; r++) data[r] = HAL_INI_VAL(ia, r, col); } int ath_hal_ini_bank_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia, const uint32_t data[], int regWr) { int r; for (r = 0; r < ia->rows; r++) { OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0), data[r]); DMA_YIELD(regWr); } return regWr; } /* * These are EEPROM board related routines which should likely live in * a helper library of some sort. */ /************************************************************** * ath_ee_getLowerUppderIndex * * Return indices surrounding the value in sorted integer lists. * Requirement: the input list must be monotonically increasing * and populated up to the list size * Returns: match is set if an index in the array matches exactly * or a the target is before or after the range of the array. */ HAL_BOOL ath_ee_getLowerUpperIndex(uint8_t target, uint8_t *pList, uint16_t listSize, uint16_t *indexL, uint16_t *indexR) { uint16_t i; /* * Check first and last elements for beyond ordered array cases. */ if (target <= pList[0]) { *indexL = *indexR = 0; return AH_TRUE; } if (target >= pList[listSize-1]) { *indexL = *indexR = (uint16_t)(listSize - 1); return AH_TRUE; } /* look for value being near or between 2 values in list */ for (i = 0; i < listSize - 1; i++) { /* * If value is close to the current value of the list * then target is not between values, it is one of the values */ if (pList[i] == target) { *indexL = *indexR = i; return AH_TRUE; } /* * Look for value being between current value and next value * if so return these 2 values */ if (target < pList[i + 1]) { *indexL = i; *indexR = (uint16_t)(i + 1); return AH_FALSE; } } HALASSERT(0); *indexL = *indexR = 0; return AH_FALSE; } /************************************************************** * ath_ee_FillVpdTable * * Fill the Vpdlist for indices Pmax-Pmin * Note: pwrMin, pwrMax and Vpdlist are all in dBm * 4 */ HAL_BOOL ath_ee_FillVpdTable(uint8_t pwrMin, uint8_t pwrMax, uint8_t *pPwrList, uint8_t *pVpdList, uint16_t numIntercepts, uint8_t *pRetVpdList) { uint16_t i, k; uint8_t currPwr = pwrMin; uint16_t idxL, idxR; HALASSERT(pwrMax > pwrMin); for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) { ath_ee_getLowerUpperIndex(currPwr, pPwrList, numIntercepts, &(idxL), &(idxR)); if (idxR < 1) idxR = 1; /* extrapolate below */ if (idxL == numIntercepts - 1) idxL = (uint16_t)(numIntercepts - 2); /* extrapolate above */ if (pPwrList[idxL] == pPwrList[idxR]) k = pVpdList[idxL]; else k = (uint16_t)( ((currPwr - pPwrList[idxL]) * pVpdList[idxR] + (pPwrList[idxR] - currPwr) * pVpdList[idxL]) / (pPwrList[idxR] - pPwrList[idxL]) ); HALASSERT(k < 256); pRetVpdList[i] = (uint8_t)k; currPwr += 2; /* half dB steps */ } return AH_TRUE; } /************************************************************************** * ath_ee_interpolate * * Returns signed interpolated or the scaled up interpolated value */ int16_t ath_ee_interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight, int16_t targetLeft, int16_t targetRight) { int16_t rv; if (srcRight == srcLeft) { rv = targetLeft; } else { rv = (int16_t)( ((target - srcLeft) * targetRight + (srcRight - target) * targetLeft) / (srcRight - srcLeft) ); } return rv; } /* * Adjust the TSF. */ void ath_hal_adjusttsf(struct ath_hal *ah, int32_t tsfdelta) { /* XXX handle wrap/overflow */ OS_REG_WRITE(ah, AR_TSF_L32, OS_REG_READ(ah, AR_TSF_L32) + tsfdelta); } /* * Enable or disable CCA. */ void ath_hal_setcca(struct ath_hal *ah, int ena) { /* * NB: fill me in; this is not provided by default because disabling * CCA in most locales violates regulatory. */ } /* * Get CCA setting. */ int ath_hal_getcca(struct ath_hal *ah) { u_int32_t diag; if (ath_hal_getcapability(ah, HAL_CAP_DIAG, 0, &diag) != HAL_OK) return 1; return ((diag & 0x500000) == 0); } /* * This routine is only needed when supporting EEPROM-in-RAM setups * (eg embedded SoCs and on-board PCI/PCIe devices.) */ /* NB: This is in 16 bit words; not bytes */ /* XXX This doesn't belong here! */ #define ATH_DATA_EEPROM_SIZE 2048 HAL_BOOL ath_hal_EepromDataRead(struct ath_hal *ah, u_int off, uint16_t *data) { if (ah->ah_eepromdata == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no eeprom data!\n", __func__); return AH_FALSE; } if (off > ATH_DATA_EEPROM_SIZE) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: offset %x > %x\n", __func__, off, ATH_DATA_EEPROM_SIZE); return AH_FALSE; } (*data) = ah->ah_eepromdata[off]; return AH_TRUE; } /* * Do a 2GHz specific MHz->IEEE based on the hardware * frequency. * * This is the unmapped frequency which is programmed into the hardware. */ int ath_hal_mhz2ieee_2ghz(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan) { if (ichan->channel == 2484) return 14; if (ichan->channel < 2484) return ((int) ichan->channel - 2407) / 5; else return 15 + ((ichan->channel - 2512) / 20); +} + +/* + * Clear the current survey data. + * + * This should be done during a channel change. + */ +void +ath_hal_survey_clear(struct ath_hal *ah) +{ + + OS_MEMZERO(&AH_PRIVATE(ah)->ah_chansurvey, + sizeof(AH_PRIVATE(ah)->ah_chansurvey)); +} + +/* + * Add a sample to the channel survey. + */ +void +ath_hal_survey_add_sample(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hs) +{ + HAL_CHANNEL_SURVEY *cs; + + cs = &AH_PRIVATE(ah)->ah_chansurvey; + + OS_MEMCPY(&cs->samples[cs->cur_sample], hs, sizeof(*hs)); + cs->samples[cs->cur_sample].seq_num = cs->cur_seq; + cs->cur_sample = (cs->cur_sample + 1) % CHANNEL_SURVEY_SAMPLE_COUNT; + cs->cur_seq++; } Index: head/sys/dev/ath/ath_hal/ah_internal.h =================================================================== --- head/sys/dev/ath/ath_hal/ah_internal.h (revision 280827) +++ head/sys/dev/ath/ath_hal/ah_internal.h (revision 280828) @@ -1,1032 +1,1047 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #ifndef _ATH_AH_INTERAL_H_ #define _ATH_AH_INTERAL_H_ /* * Atheros Device Hardware Access Layer (HAL). * * Internal definitions. */ #define AH_NULL 0 #define AH_MIN(a,b) ((a)<(b)?(a):(b)) #define AH_MAX(a,b) ((a)>(b)?(a):(b)) #include #include "opt_ah.h" /* needed for AH_SUPPORT_AR5416 */ #ifndef AH_SUPPORT_AR5416 #define AH_SUPPORT_AR5416 1 #endif #ifndef NBBY #define NBBY 8 /* number of bits/byte */ #endif #ifndef roundup #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ #endif #ifndef howmany #define howmany(x, y) (((x)+((y)-1))/(y)) #endif #ifndef offsetof #define offsetof(type, field) ((size_t)(&((type *)0)->field)) #endif typedef struct { uint32_t start; /* first register */ uint32_t end; /* ending register or zero */ } HAL_REGRANGE; typedef struct { uint32_t addr; /* regiser address/offset */ uint32_t value; /* value to write */ } HAL_REGWRITE; /* * Transmit power scale factor. * * NB: This is not public because we want to discourage the use of * scaling; folks should use the tx power limit interface. */ typedef enum { HAL_TP_SCALE_MAX = 0, /* no scaling (default) */ HAL_TP_SCALE_50 = 1, /* 50% of max (-3 dBm) */ HAL_TP_SCALE_25 = 2, /* 25% of max (-6 dBm) */ HAL_TP_SCALE_12 = 3, /* 12% of max (-9 dBm) */ HAL_TP_SCALE_MIN = 4, /* min, but still on */ } HAL_TP_SCALE; typedef enum { HAL_CAP_RADAR = 0, /* Radar capability */ HAL_CAP_AR = 1, /* AR capability */ } HAL_PHYDIAG_CAPS; /* * Enable/disable strong signal fast diversity */ #define HAL_CAP_STRONG_DIV 2 /* * Each chip or class of chips registers to offer support. */ struct ath_hal_chip { const char *name; const char *(*probe)(uint16_t vendorid, uint16_t devid); struct ath_hal *(*attach)(uint16_t devid, HAL_SOFTC, HAL_BUS_TAG, HAL_BUS_HANDLE, uint16_t *eepromdata, HAL_OPS_CONFIG *ah, HAL_STATUS *error); }; #ifndef AH_CHIP #define AH_CHIP(_name, _probe, _attach) \ static struct ath_hal_chip _name##_chip = { \ .name = #_name, \ .probe = _probe, \ .attach = _attach \ }; \ OS_DATA_SET(ah_chips, _name##_chip) #endif /* * Each RF backend registers to offer support; this is mostly * used by multi-chip 5212 solutions. Single-chip solutions * have a fixed idea about which RF to use. */ struct ath_hal_rf { const char *name; HAL_BOOL (*probe)(struct ath_hal *ah); HAL_BOOL (*attach)(struct ath_hal *ah, HAL_STATUS *ecode); }; #ifndef AH_RF #define AH_RF(_name, _probe, _attach) \ static struct ath_hal_rf _name##_rf = { \ .name = __STRING(_name), \ .probe = _probe, \ .attach = _attach \ }; \ OS_DATA_SET(ah_rfs, _name##_rf) #endif struct ath_hal_rf *ath_hal_rfprobe(struct ath_hal *ah, HAL_STATUS *ecode); /* * Maximum number of internal channels. Entries are per unique * frequency so this might be need to be increased to handle all * usage cases; typically no more than 32 are really needed but * dynamically allocating the data structures is a bit painful * right now. */ #ifndef AH_MAXCHAN #define AH_MAXCHAN 96 #endif #define HAL_NF_CAL_HIST_LEN_FULL 5 #define HAL_NF_CAL_HIST_LEN_SMALL 1 #define HAL_NUM_NF_READINGS 6 /* 3 chains * (ctl + ext) */ #define HAL_NF_LOAD_DELAY 1000 /* * PER_CHAN doesn't work for now, as it looks like the device layer * has to pre-populate the per-channel list with nominal values. */ //#define ATH_NF_PER_CHAN 1 typedef struct { u_int8_t curr_index; int8_t invalidNFcount; /* TO DO: REMOVE THIS! */ int16_t priv_nf[HAL_NUM_NF_READINGS]; } HAL_NFCAL_BASE; typedef struct { HAL_NFCAL_BASE base; int16_t nf_cal_buffer[HAL_NF_CAL_HIST_LEN_FULL][HAL_NUM_NF_READINGS]; } HAL_NFCAL_HIST_FULL; typedef struct { HAL_NFCAL_BASE base; int16_t nf_cal_buffer[HAL_NF_CAL_HIST_LEN_SMALL][HAL_NUM_NF_READINGS]; } HAL_NFCAL_HIST_SMALL; #ifdef ATH_NF_PER_CHAN typedef HAL_NFCAL_HIST_FULL HAL_CHAN_NFCAL_HIST; #define AH_HOME_CHAN_NFCAL_HIST(ah, ichan) (ichan ? &ichan->nf_cal_hist: NULL) #else typedef HAL_NFCAL_HIST_SMALL HAL_CHAN_NFCAL_HIST; #define AH_HOME_CHAN_NFCAL_HIST(ah, ichan) (&AH_PRIVATE(ah)->nf_cal_hist) #endif /* ATH_NF_PER_CHAN */ /* * Internal per-channel state. These are found * using ic_devdata in the ieee80211_channel. */ typedef struct { uint16_t channel; /* h/w frequency, NB: may be mapped */ uint8_t privFlags; #define CHANNEL_IQVALID 0x01 /* IQ calibration valid */ #define CHANNEL_ANI_INIT 0x02 /* ANI state initialized */ #define CHANNEL_ANI_SETUP 0x04 /* ANI state setup */ #define CHANNEL_MIMO_NF_VALID 0x04 /* Mimo NF values are valid */ uint8_t calValid; /* bitmask of cal types */ int8_t iCoff; int8_t qCoff; int16_t rawNoiseFloor; int16_t noiseFloorAdjust; #ifdef AH_SUPPORT_AR5416 int16_t noiseFloorCtl[AH_MAX_CHAINS]; int16_t noiseFloorExt[AH_MAX_CHAINS]; #endif /* AH_SUPPORT_AR5416 */ uint16_t mainSpur; /* cached spur value for this channel */ /*XXX TODO: make these part of privFlags */ uint8_t paprd_done:1, /* 1: PAPRD DONE, 0: PAPRD Cal not done */ paprd_table_write_done:1; /* 1: DONE, 0: Cal data write not done */ int one_time_cals_done; HAL_CHAN_NFCAL_HIST nf_cal_hist; } HAL_CHANNEL_INTERNAL; /* channel requires noise floor check */ #define CHANNEL_NFCREQUIRED IEEE80211_CHAN_PRIV0 /* all full-width channels */ #define IEEE80211_CHAN_ALLFULL \ (IEEE80211_CHAN_ALL - (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) #define IEEE80211_CHAN_ALLTURBOFULL \ (IEEE80211_CHAN_ALLTURBO - \ (IEEE80211_CHAN_HALF | IEEE80211_CHAN_QUARTER)) typedef struct { uint32_t halChanSpreadSupport : 1, halSleepAfterBeaconBroken : 1, halCompressSupport : 1, halBurstSupport : 1, halFastFramesSupport : 1, halChapTuningSupport : 1, halTurboGSupport : 1, halTurboPrimeSupport : 1, halMicAesCcmSupport : 1, halMicCkipSupport : 1, halMicTkipSupport : 1, halTkipMicTxRxKeySupport : 1, halCipherAesCcmSupport : 1, halCipherCkipSupport : 1, halCipherTkipSupport : 1, halPSPollBroken : 1, halVEOLSupport : 1, halBssIdMaskSupport : 1, halMcastKeySrchSupport : 1, halTsfAddSupport : 1, halChanHalfRate : 1, halChanQuarterRate : 1, halHTSupport : 1, halHTSGI20Support : 1, halRfSilentSupport : 1, halHwPhyCounterSupport : 1, halWowSupport : 1, halWowMatchPatternExact : 1, halAutoSleepSupport : 1, halFastCCSupport : 1, halBtCoexSupport : 1; uint32_t halRxStbcSupport : 1, halTxStbcSupport : 1, halGTTSupport : 1, halCSTSupport : 1, halRifsRxSupport : 1, halRifsTxSupport : 1, hal4AddrAggrSupport : 1, halExtChanDfsSupport : 1, halUseCombinedRadarRssi : 1, halForcePpmSupport : 1, halEnhancedPmSupport : 1, halEnhancedDfsSupport : 1, halMbssidAggrSupport : 1, halBssidMatchSupport : 1, hal4kbSplitTransSupport : 1, halHasRxSelfLinkedTail : 1, halSupportsFastClock5GHz : 1, halHasLongRxDescTsf : 1, halHasBBReadWar : 1, halSerialiseRegWar : 1, halMciSupport : 1, halRxTxAbortSupport : 1, halPaprdEnabled : 1, halHasUapsdSupport : 1, halWpsPushButtonSupport : 1, halBtCoexApsmWar : 1, halGenTimerSupport : 1, halLDPCSupport : 1, halHwBeaconProcSupport : 1, halEnhancedDmaSupport : 1; uint32_t halIsrRacSupport : 1, halApmEnable : 1, halIntrMitigation : 1, hal49GhzSupport : 1, halAntDivCombSupport : 1, halAntDivCombSupportOrg : 1, halRadioRetentionSupport : 1, halSpectralScanSupport : 1, halRxUsingLnaMixing : 1, halRxDoMyBeacon : 1, halHwUapsdTrig : 1; uint32_t halWirelessModes; uint16_t halTotalQueues; uint16_t halKeyCacheSize; uint16_t halLow5GhzChan, halHigh5GhzChan; uint16_t halLow2GhzChan, halHigh2GhzChan; int halTstampPrecision; int halRtsAggrLimit; uint8_t halTxChainMask; uint8_t halRxChainMask; uint8_t halNumGpioPins; uint8_t halNumAntCfg2GHz; uint8_t halNumAntCfg5GHz; uint32_t halIntrMask; uint8_t halTxStreams; uint8_t halRxStreams; HAL_MFP_OPT_T halMfpSupport; /* AR9300 HAL porting capabilities */ int hal_paprd_enabled; int hal_pcie_lcr_offset; int hal_pcie_lcr_extsync_en; int halNumTxMaps; int halTxDescLen; int halTxStatusLen; int halRxStatusLen; int halRxHpFifoDepth; int halRxLpFifoDepth; uint32_t halRegCap; /* XXX needed? */ int halNumMRRetries; int hal_ani_poll_interval; int hal_channel_switch_time_usec; } HAL_CAPABILITIES; struct regDomain; /* * Definitions for ah_flags in ath_hal_private */ #define AH_USE_EEPROM 0x1 #define AH_IS_HB63 0x2 /* * The ``private area'' follows immediately after the ``public area'' * in the data structure returned by ath_hal_attach. Private data are * used by device-independent code such as the regulatory domain support. * In general, code within the HAL should never depend on data in the * public area. Instead any public data needed internally should be * shadowed here. * * When declaring a device-specific ath_hal data structure this structure * is assumed to at the front; e.g. * * struct ath_hal_5212 { * struct ath_hal_private ah_priv; * ... * }; * * It might be better to manage the method pointers in this structure * using an indirect pointer to a read-only data structure but this would * disallow class-style method overriding. */ struct ath_hal_private { struct ath_hal h; /* public area */ /* NB: all methods go first to simplify initialization */ HAL_BOOL (*ah_getChannelEdges)(struct ath_hal*, uint16_t channelFlags, uint16_t *lowChannel, uint16_t *highChannel); u_int (*ah_getWirelessModes)(struct ath_hal*); HAL_BOOL (*ah_eepromRead)(struct ath_hal *, u_int off, uint16_t *data); HAL_BOOL (*ah_eepromWrite)(struct ath_hal *, u_int off, uint16_t data); HAL_BOOL (*ah_getChipPowerLimits)(struct ath_hal *, struct ieee80211_channel *); int16_t (*ah_getNfAdjust)(struct ath_hal *, const HAL_CHANNEL_INTERNAL*); void (*ah_getNoiseFloor)(struct ath_hal *, int16_t nfarray[]); void *ah_eeprom; /* opaque EEPROM state */ uint16_t ah_eeversion; /* EEPROM version */ void (*ah_eepromDetach)(struct ath_hal *); HAL_STATUS (*ah_eepromGet)(struct ath_hal *, int, void *); HAL_STATUS (*ah_eepromSet)(struct ath_hal *, int, int); uint16_t (*ah_getSpurChan)(struct ath_hal *, int, HAL_BOOL); HAL_BOOL (*ah_eepromDiag)(struct ath_hal *, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize); /* * Device revision information. */ uint16_t ah_devid; /* PCI device ID */ uint16_t ah_subvendorid; /* PCI subvendor ID */ uint32_t ah_macVersion; /* MAC version id */ uint16_t ah_macRev; /* MAC revision */ uint16_t ah_phyRev; /* PHY revision */ uint16_t ah_analog5GhzRev; /* 2GHz radio revision */ uint16_t ah_analog2GhzRev; /* 5GHz radio revision */ uint32_t ah_flags; /* misc flags */ uint8_t ah_ispcie; /* PCIE, special treatment */ uint8_t ah_devType; /* card type - CB, PCI, PCIe */ HAL_OPMODE ah_opmode; /* operating mode from reset */ const struct ieee80211_channel *ah_curchan;/* operating channel */ HAL_CAPABILITIES ah_caps; /* device capabilities */ uint32_t ah_diagreg; /* user-specified AR_DIAG_SW */ int16_t ah_powerLimit; /* tx power cap */ uint16_t ah_maxPowerLevel; /* calculated max tx power */ u_int ah_tpScale; /* tx power scale factor */ u_int16_t ah_extraTxPow; /* low rates extra-txpower */ uint32_t ah_11nCompat; /* 11n compat controls */ /* * State for regulatory domain handling. */ HAL_REG_DOMAIN ah_currentRD; /* EEPROM regulatory domain */ HAL_REG_DOMAIN ah_currentRDext; /* EEPROM extended regdomain flags */ HAL_DFS_DOMAIN ah_dfsDomain; /* current DFS domain */ HAL_CHANNEL_INTERNAL ah_channels[AH_MAXCHAN]; /* private chan state */ u_int ah_nchan; /* valid items in ah_channels */ const struct regDomain *ah_rd2GHz; /* reg state for 2G band */ const struct regDomain *ah_rd5GHz; /* reg state for 5G band */ uint8_t ah_coverageClass; /* coverage class */ /* * RF Silent handling; setup according to the EEPROM. */ uint16_t ah_rfsilent; /* GPIO pin + polarity */ HAL_BOOL ah_rfkillEnabled; /* enable/disable RfKill */ /* * Diagnostic support for discriminating HIUERR reports. */ uint32_t ah_fatalState[6]; /* AR_ISR+shadow regs */ int ah_rxornIsFatal; /* how to treat HAL_INT_RXORN */ -#ifndef ATH_NF_PER_CHAN + /* Only used if ATH_NF_PER_CHAN is defined */ HAL_NFCAL_HIST_FULL nf_cal_hist; -#endif /* ! ATH_NF_PER_CHAN */ + + /* + * Channel survey history - current channel only. + */ + HAL_CHANNEL_SURVEY ah_chansurvey; /* channel survey */ }; #define AH_PRIVATE(_ah) ((struct ath_hal_private *)(_ah)) #define ath_hal_getChannelEdges(_ah, _cf, _lc, _hc) \ AH_PRIVATE(_ah)->ah_getChannelEdges(_ah, _cf, _lc, _hc) #define ath_hal_getWirelessModes(_ah) \ AH_PRIVATE(_ah)->ah_getWirelessModes(_ah) #define ath_hal_eepromRead(_ah, _off, _data) \ AH_PRIVATE(_ah)->ah_eepromRead(_ah, _off, _data) #define ath_hal_eepromWrite(_ah, _off, _data) \ AH_PRIVATE(_ah)->ah_eepromWrite(_ah, _off, _data) #define ath_hal_gpioCfgOutput(_ah, _gpio, _type) \ (_ah)->ah_gpioCfgOutput(_ah, _gpio, _type) #define ath_hal_gpioCfgInput(_ah, _gpio) \ (_ah)->ah_gpioCfgInput(_ah, _gpio) #define ath_hal_gpioGet(_ah, _gpio) \ (_ah)->ah_gpioGet(_ah, _gpio) #define ath_hal_gpioSet(_ah, _gpio, _val) \ (_ah)->ah_gpioSet(_ah, _gpio, _val) #define ath_hal_gpioSetIntr(_ah, _gpio, _ilevel) \ (_ah)->ah_gpioSetIntr(_ah, _gpio, _ilevel) #define ath_hal_getpowerlimits(_ah, _chan) \ AH_PRIVATE(_ah)->ah_getChipPowerLimits(_ah, _chan) #define ath_hal_getNfAdjust(_ah, _c) \ AH_PRIVATE(_ah)->ah_getNfAdjust(_ah, _c) #define ath_hal_getNoiseFloor(_ah, _nfArray) \ AH_PRIVATE(_ah)->ah_getNoiseFloor(_ah, _nfArray) #define ath_hal_configPCIE(_ah, _reset, _poweroff) \ (_ah)->ah_configPCIE(_ah, _reset, _poweroff) #define ath_hal_disablePCIE(_ah) \ (_ah)->ah_disablePCIE(_ah) #define ath_hal_setInterrupts(_ah, _mask) \ (_ah)->ah_setInterrupts(_ah, _mask) #define ath_hal_isrfkillenabled(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 1, AH_NULL) == HAL_OK) #define ath_hal_enable_rfkill(_ah, _v) \ ath_hal_setcapability(_ah, HAL_CAP_RFSILENT, 1, _v, AH_NULL) #define ath_hal_hasrfkill_int(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 3, AH_NULL) == HAL_OK) #define ath_hal_eepromDetach(_ah) do { \ if (AH_PRIVATE(_ah)->ah_eepromDetach != AH_NULL) \ AH_PRIVATE(_ah)->ah_eepromDetach(_ah); \ } while (0) #define ath_hal_eepromGet(_ah, _param, _val) \ AH_PRIVATE(_ah)->ah_eepromGet(_ah, _param, _val) #define ath_hal_eepromSet(_ah, _param, _val) \ AH_PRIVATE(_ah)->ah_eepromSet(_ah, _param, _val) #define ath_hal_eepromGetFlag(_ah, _param) \ (AH_PRIVATE(_ah)->ah_eepromGet(_ah, _param, AH_NULL) == HAL_OK) #define ath_hal_getSpurChan(_ah, _ix, _is2G) \ AH_PRIVATE(_ah)->ah_getSpurChan(_ah, _ix, _is2G) #define ath_hal_eepromDiag(_ah, _request, _a, _asize, _r, _rsize) \ AH_PRIVATE(_ah)->ah_eepromDiag(_ah, _request, _a, _asize, _r, _rsize) #ifndef _NET_IF_IEEE80211_H_ /* * Stuff that would naturally come from _ieee80211.h */ #define IEEE80211_ADDR_LEN 6 #define IEEE80211_WEP_IVLEN 3 /* 24bit */ #define IEEE80211_WEP_KIDLEN 1 /* 1 octet */ #define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */ #define IEEE80211_CRC_LEN 4 #define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \ (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN)) #endif /* _NET_IF_IEEE80211_H_ */ #define HAL_TXQ_USE_LOCKOUT_BKOFF_DIS 0x00000001 #define INIT_AIFS 2 #define INIT_CWMIN 15 #define INIT_CWMIN_11B 31 #define INIT_CWMAX 1023 #define INIT_SH_RETRY 10 #define INIT_LG_RETRY 10 #define INIT_SSH_RETRY 32 #define INIT_SLG_RETRY 32 typedef struct { uint32_t tqi_ver; /* HAL TXQ verson */ HAL_TX_QUEUE tqi_type; /* hw queue type*/ HAL_TX_QUEUE_SUBTYPE tqi_subtype; /* queue subtype, if applicable */ HAL_TX_QUEUE_FLAGS tqi_qflags; /* queue flags */ uint32_t tqi_priority; uint32_t tqi_aifs; /* aifs */ uint32_t tqi_cwmin; /* cwMin */ uint32_t tqi_cwmax; /* cwMax */ uint16_t tqi_shretry; /* frame short retry limit */ uint16_t tqi_lgretry; /* frame long retry limit */ uint32_t tqi_cbrPeriod; uint32_t tqi_cbrOverflowLimit; uint32_t tqi_burstTime; uint32_t tqi_readyTime; uint32_t tqi_physCompBuf; uint32_t tqi_intFlags; /* flags for internal use */ } HAL_TX_QUEUE_INFO; extern HAL_BOOL ath_hal_setTxQProps(struct ath_hal *ah, HAL_TX_QUEUE_INFO *qi, const HAL_TXQ_INFO *qInfo); extern HAL_BOOL ath_hal_getTxQProps(struct ath_hal *ah, HAL_TXQ_INFO *qInfo, const HAL_TX_QUEUE_INFO *qi); #define HAL_SPUR_VAL_MASK 0x3FFF #define HAL_SPUR_CHAN_WIDTH 87 #define HAL_BIN_WIDTH_BASE_100HZ 3125 #define HAL_BIN_WIDTH_TURBO_100HZ 6250 #define HAL_MAX_BINS_ALLOWED 28 #define IS_CHAN_5GHZ(_c) ((_c)->channel > 4900) #define IS_CHAN_2GHZ(_c) (!IS_CHAN_5GHZ(_c)) #define IS_CHAN_IN_PUBLIC_SAFETY_BAND(_c) ((_c) > 4940 && (_c) < 4990) /* * Deduce if the host cpu has big- or litt-endian byte order. */ static __inline__ int isBigEndian(void) { union { int32_t i; char c[4]; } u; u.i = 1; return (u.c[0] == 0); } /* unalligned little endian access */ #define LE_READ_2(p) \ ((uint16_t) \ ((((const uint8_t *)(p))[0] ) | (((const uint8_t *)(p))[1]<< 8))) #define LE_READ_4(p) \ ((uint32_t) \ ((((const uint8_t *)(p))[0] ) | (((const uint8_t *)(p))[1]<< 8) |\ (((const uint8_t *)(p))[2]<<16) | (((const uint8_t *)(p))[3]<<24))) /* * Register manipulation macros that expect bit field defines * to follow the convention that an _S suffix is appended for * a shift count, while the field mask has no suffix. */ #define SM(_v, _f) (((_v) << _f##_S) & (_f)) #define MS(_v, _f) (((_v) & (_f)) >> _f##_S) #define OS_REG_RMW(_a, _r, _set, _clr) \ OS_REG_WRITE(_a, _r, (OS_REG_READ(_a, _r) & ~(_clr)) | (_set)) #define OS_REG_RMW_FIELD(_a, _r, _f, _v) \ OS_REG_WRITE(_a, _r, \ (OS_REG_READ(_a, _r) &~ (_f)) | (((_v) << _f##_S) & (_f))) #define OS_REG_SET_BIT(_a, _r, _f) \ OS_REG_WRITE(_a, _r, OS_REG_READ(_a, _r) | (_f)) #define OS_REG_CLR_BIT(_a, _r, _f) \ OS_REG_WRITE(_a, _r, OS_REG_READ(_a, _r) &~ (_f)) #define OS_REG_IS_BIT_SET(_a, _r, _f) \ ((OS_REG_READ(_a, _r) & (_f)) != 0) #define OS_REG_RMW_FIELD_ALT(_a, _r, _f, _v) \ OS_REG_WRITE(_a, _r, \ (OS_REG_READ(_a, _r) &~(_f<<_f##_S)) | \ (((_v) << _f##_S) & (_f<<_f##_S))) #define OS_REG_READ_FIELD(_a, _r, _f) \ (((OS_REG_READ(_a, _r) & _f) >> _f##_S)) #define OS_REG_READ_FIELD_ALT(_a, _r, _f) \ ((OS_REG_READ(_a, _r) >> (_f##_S))&(_f)) /* Analog register writes may require a delay between each one (eg Merlin?) */ #define OS_A_REG_RMW_FIELD(_a, _r, _f, _v) \ do { OS_REG_WRITE(_a, _r, (OS_REG_READ(_a, _r) &~ (_f)) | \ (((_v) << _f##_S) & (_f))) ; OS_DELAY(100); } while (0) #define OS_A_REG_WRITE(_a, _r, _v) \ do { OS_REG_WRITE(_a, _r, _v); OS_DELAY(100); } while (0) /* wait for the register contents to have the specified value */ extern HAL_BOOL ath_hal_wait(struct ath_hal *, u_int reg, uint32_t mask, uint32_t val); extern HAL_BOOL ath_hal_waitfor(struct ath_hal *, u_int reg, uint32_t mask, uint32_t val, uint32_t timeout); /* return the first n bits in val reversed */ extern uint32_t ath_hal_reverseBits(uint32_t val, uint32_t n); /* printf interfaces */ extern void ath_hal_printf(struct ath_hal *, const char*, ...) __printflike(2,3); extern void ath_hal_vprintf(struct ath_hal *, const char*, __va_list) __printflike(2, 0); extern const char* ath_hal_ether_sprintf(const uint8_t *mac); /* allocate and free memory */ extern void *ath_hal_malloc(size_t); extern void ath_hal_free(void *); /* common debugging interfaces */ #ifdef AH_DEBUG #include "ah_debug.h" extern int ath_hal_debug; /* Global debug flags */ /* * The typecast is purely because some callers will pass in * AH_NULL directly rather than using a NULL ath_hal pointer. */ #define HALDEBUG(_ah, __m, ...) \ do { \ if ((__m) == HAL_DEBUG_UNMASKABLE || \ ath_hal_debug & (__m) || \ ((_ah) != NULL && \ ((struct ath_hal *) (_ah))->ah_config.ah_debug & (__m))) { \ DO_HALDEBUG((_ah), (__m), __VA_ARGS__); \ } \ } while(0); extern void DO_HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...) __printflike(3,4); #else #define HALDEBUG(_ah, __m, ...) #endif /* AH_DEBUG */ /* * Register logging definitions shared with ardecode. */ #include "ah_decode.h" /* * Common assertion interface. Note: it is a bad idea to generate * an assertion failure for any recoverable event. Instead catch * the violation and, if possible, fix it up or recover from it; either * with an error return value or a diagnostic messages. System software * does not panic unless the situation is hopeless. */ #ifdef AH_ASSERT extern void ath_hal_assert_failed(const char* filename, int lineno, const char* msg); #define HALASSERT(_x) do { \ if (!(_x)) { \ ath_hal_assert_failed(__FILE__, __LINE__, #_x); \ } \ } while (0) #else #define HALASSERT(_x) #endif /* AH_ASSERT */ /* * Regulatory domain support. */ /* * Return the max allowed antenna gain and apply any regulatory * domain specific changes. */ u_int ath_hal_getantennareduction(struct ath_hal *ah, const struct ieee80211_channel *chan, u_int twiceGain); /* * Return the test group for the specific channel based on * the current regulatory setup. */ u_int ath_hal_getctl(struct ath_hal *, const struct ieee80211_channel *); /* * Map a public channel definition to the corresponding * internal data structure. This implicitly specifies * whether or not the specified channel is ok to use * based on the current regulatory domain constraints. */ #ifndef AH_DEBUG static OS_INLINE HAL_CHANNEL_INTERNAL * ath_hal_checkchannel(struct ath_hal *ah, const struct ieee80211_channel *c) { HAL_CHANNEL_INTERNAL *cc; HALASSERT(c->ic_devdata < AH_PRIVATE(ah)->ah_nchan); cc = &AH_PRIVATE(ah)->ah_channels[c->ic_devdata]; HALASSERT(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c)); return cc; } #else /* NB: non-inline version that checks state */ HAL_CHANNEL_INTERNAL *ath_hal_checkchannel(struct ath_hal *, const struct ieee80211_channel *); #endif /* AH_DEBUG */ /* * Return the h/w frequency for a channel. This may be * different from ic_freq if this is a GSM device that * takes 2.4GHz frequencies and down-converts them. */ static OS_INLINE uint16_t ath_hal_gethwchannel(struct ath_hal *ah, const struct ieee80211_channel *c) { return ath_hal_checkchannel(ah, c)->channel; } /* * Convert between microseconds and core system clocks. */ extern u_int ath_hal_mac_clks(struct ath_hal *ah, u_int usecs); extern u_int ath_hal_mac_usec(struct ath_hal *ah, u_int clks); /* * Generic get/set capability support. Each chip overrides * this routine to support chip-specific capabilities. */ extern HAL_STATUS ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t *result); extern HAL_BOOL ath_hal_setcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t setting, HAL_STATUS *status); /* The diagnostic codes used to be internally defined here -adrian */ #include "ah_diagcodes.h" /* * The AR5416 and later HALs have MAC and baseband hang checking. */ typedef struct { uint32_t hang_reg_offset; uint32_t hang_val; uint32_t hang_mask; uint32_t hang_offset; } hal_hw_hang_check_t; typedef struct { uint32_t dma_dbg_3; uint32_t dma_dbg_4; uint32_t dma_dbg_5; uint32_t dma_dbg_6; } mac_dbg_regs_t; typedef enum { dcu_chain_state = 0x1, dcu_complete_state = 0x2, qcu_state = 0x4, qcu_fsp_ok = 0x8, qcu_fsp_state = 0x10, qcu_stitch_state = 0x20, qcu_fetch_state = 0x40, qcu_complete_state = 0x80 } hal_mac_hangs_t; typedef struct { int states; uint8_t dcu_chain_state; uint8_t dcu_complete_state; uint8_t qcu_state; uint8_t qcu_fsp_ok; uint8_t qcu_fsp_state; uint8_t qcu_stitch_state; uint8_t qcu_fetch_state; uint8_t qcu_complete_state; } hal_mac_hang_check_t; enum { HAL_BB_HANG_DFS = 0x0001, HAL_BB_HANG_RIFS = 0x0002, HAL_BB_HANG_RX_CLEAR = 0x0004, HAL_BB_HANG_UNKNOWN = 0x0080, HAL_MAC_HANG_SIG1 = 0x0100, HAL_MAC_HANG_SIG2 = 0x0200, HAL_MAC_HANG_UNKNOWN = 0x8000, HAL_BB_HANGS = HAL_BB_HANG_DFS | HAL_BB_HANG_RIFS | HAL_BB_HANG_RX_CLEAR | HAL_BB_HANG_UNKNOWN, HAL_MAC_HANGS = HAL_MAC_HANG_SIG1 | HAL_MAC_HANG_SIG2 | HAL_MAC_HANG_UNKNOWN, }; /* Merge these with above */ typedef enum hal_hw_hangs { HAL_DFS_BB_HANG_WAR = 0x1, HAL_RIFS_BB_HANG_WAR = 0x2, HAL_RX_STUCK_LOW_BB_HANG_WAR = 0x4, HAL_MAC_HANG_WAR = 0x8, HAL_PHYRESTART_CLR_WAR = 0x10, HAL_MAC_HANG_DETECTED = 0x40000000, HAL_BB_HANG_DETECTED = 0x80000000 } hal_hw_hangs_t; /* * Device revision information. */ typedef struct { uint16_t ah_devid; /* PCI device ID */ uint16_t ah_subvendorid; /* PCI subvendor ID */ uint32_t ah_macVersion; /* MAC version id */ uint16_t ah_macRev; /* MAC revision */ uint16_t ah_phyRev; /* PHY revision */ uint16_t ah_analog5GhzRev; /* 2GHz radio revision */ uint16_t ah_analog2GhzRev; /* 5GHz radio revision */ } HAL_REVS; /* * Argument payload for HAL_DIAG_SETKEY. */ typedef struct { HAL_KEYVAL dk_keyval; uint16_t dk_keyix; /* key index */ uint8_t dk_mac[IEEE80211_ADDR_LEN]; int dk_xor; /* XOR key data */ } HAL_DIAG_KEYVAL; /* * Argument payload for HAL_DIAG_EEWRITE. */ typedef struct { uint16_t ee_off; /* eeprom offset */ uint16_t ee_data; /* write data */ } HAL_DIAG_EEVAL; typedef struct { u_int offset; /* reg offset */ uint32_t val; /* reg value */ } HAL_DIAG_REGVAL; /* * 11n compatibility tweaks. */ #define HAL_DIAG_11N_SERVICES 0x00000003 #define HAL_DIAG_11N_SERVICES_S 0 #define HAL_DIAG_11N_TXSTOMP 0x0000000c #define HAL_DIAG_11N_TXSTOMP_S 2 typedef struct { int maxNoiseImmunityLevel; /* [0..4] */ int totalSizeDesired[5]; int coarseHigh[5]; int coarseLow[5]; int firpwr[5]; int maxSpurImmunityLevel; /* [0..7] */ int cycPwrThr1[8]; int maxFirstepLevel; /* [0..2] */ int firstep[3]; uint32_t ofdmTrigHigh; uint32_t ofdmTrigLow; int32_t cckTrigHigh; int32_t cckTrigLow; int32_t rssiThrLow; int32_t rssiThrHigh; int period; /* update listen period */ } HAL_ANI_PARAMS; extern HAL_BOOL ath_hal_getdiagstate(struct ath_hal *ah, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize); /* * Setup a h/w rate table for use. */ extern void ath_hal_setupratetable(struct ath_hal *ah, HAL_RATE_TABLE *rt); /* * Common routine for implementing getChanNoise api. */ int16_t ath_hal_getChanNoise(struct ath_hal *, const struct ieee80211_channel *); /* * Initialization support. */ typedef struct { const uint32_t *data; int rows, cols; } HAL_INI_ARRAY; #define HAL_INI_INIT(_ia, _data, _cols) do { \ (_ia)->data = (const uint32_t *)(_data); \ (_ia)->rows = sizeof(_data) / sizeof((_data)[0]); \ (_ia)->cols = (_cols); \ } while (0) #define HAL_INI_VAL(_ia, _r, _c) \ ((_ia)->data[((_r)*(_ia)->cols) + (_c)]) /* * OS_DELAY() does a PIO READ on the PCI bus which allows * other cards' DMA reads to complete in the middle of our reset. */ #define DMA_YIELD(x) do { \ if ((++(x) % 64) == 0) \ OS_DELAY(1); \ } while (0) #define HAL_INI_WRITE_ARRAY(ah, regArray, col, regWr) do { \ int r; \ for (r = 0; r < N(regArray); r++) { \ OS_REG_WRITE(ah, (regArray)[r][0], (regArray)[r][col]); \ DMA_YIELD(regWr); \ } \ } while (0) #define HAL_INI_WRITE_BANK(ah, regArray, bankData, regWr) do { \ int r; \ for (r = 0; r < N(regArray); r++) { \ OS_REG_WRITE(ah, (regArray)[r][0], (bankData)[r]); \ DMA_YIELD(regWr); \ } \ } while (0) extern int ath_hal_ini_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia, int col, int regWr); extern void ath_hal_ini_bank_setup(uint32_t data[], const HAL_INI_ARRAY *ia, int col); extern int ath_hal_ini_bank_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia, const uint32_t data[], int regWr); #define CCK_SIFS_TIME 10 #define CCK_PREAMBLE_BITS 144 #define CCK_PLCP_BITS 48 #define OFDM_SIFS_TIME 16 #define OFDM_PREAMBLE_TIME 20 #define OFDM_PLCP_BITS 22 #define OFDM_SYMBOL_TIME 4 #define OFDM_HALF_SIFS_TIME 32 #define OFDM_HALF_PREAMBLE_TIME 40 #define OFDM_HALF_PLCP_BITS 22 #define OFDM_HALF_SYMBOL_TIME 8 #define OFDM_QUARTER_SIFS_TIME 64 #define OFDM_QUARTER_PREAMBLE_TIME 80 #define OFDM_QUARTER_PLCP_BITS 22 #define OFDM_QUARTER_SYMBOL_TIME 16 #define TURBO_SIFS_TIME 8 #define TURBO_PREAMBLE_TIME 14 #define TURBO_PLCP_BITS 22 #define TURBO_SYMBOL_TIME 4 #define WLAN_CTRL_FRAME_SIZE (2+2+6+4) /* ACK+FCS */ /* Generic EEPROM board value functions */ extern HAL_BOOL ath_ee_getLowerUpperIndex(uint8_t target, uint8_t *pList, uint16_t listSize, uint16_t *indexL, uint16_t *indexR); extern HAL_BOOL ath_ee_FillVpdTable(uint8_t pwrMin, uint8_t pwrMax, uint8_t *pPwrList, uint8_t *pVpdList, uint16_t numIntercepts, uint8_t *pRetVpdList); extern int16_t ath_ee_interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight, int16_t targetLeft, int16_t targetRight); /* Whether 5ghz fast clock is needed */ /* * The chipset (Merlin, AR9300/later) should set the capability flag below; * this flag simply says that the hardware can do it, not that the EEPROM * says it can. * * Merlin 2.0/2.1 chips with an EEPROM version > 16 do 5ghz fast clock * if the relevant eeprom flag is set. * Merlin 2.0/2.1 chips with an EEPROM version <= 16 do 5ghz fast clock * by default. */ #define IS_5GHZ_FAST_CLOCK_EN(_ah, _c) \ (IEEE80211_IS_CHAN_5GHZ(_c) && \ AH_PRIVATE((_ah))->ah_caps.halSupportsFastClock5GHz && \ ath_hal_eepromGetFlag((_ah), AR_EEP_FSTCLK_5G)) /* * Fetch the maximum regulatory domain power for the given channel * in 1/2dBm steps. */ static inline int ath_hal_get_twice_max_regpower(struct ath_hal_private *ahp, const HAL_CHANNEL_INTERNAL *ichan, const struct ieee80211_channel *chan) { struct ath_hal *ah = &ahp->h; if (! chan) { ath_hal_printf(ah, "%s: called with chan=NULL!\n", __func__); return (0); } return (chan->ic_maxpower); } /* * Get the maximum antenna gain allowed, in 1/2dBm steps. */ static inline int ath_hal_getantennaallowed(struct ath_hal *ah, const struct ieee80211_channel *chan) { if (! chan) return (0); return (chan->ic_maxantgain); } /* * Map the given 2GHz channel to an IEEE number. */ extern int ath_hal_mhz2ieee_2ghz(struct ath_hal *, HAL_CHANNEL_INTERNAL *); + +/* + * Clear the channel survey data. + */ +extern void ath_hal_survey_clear(struct ath_hal *ah); + +/* + * Add a sample to the channel survey data. + */ +extern void ath_hal_survey_add_sample(struct ath_hal *ah, + HAL_SURVEY_SAMPLE *hs); #endif /* _ATH_AH_INTERAL_H_ */ Index: head/sys/dev/ath/ath_hal/ar5212/ar5212.h =================================================================== --- head/sys/dev/ath/ath_hal/ar5212/ar5212.h (revision 280827) +++ head/sys/dev/ath/ath_hal/ar5212/ar5212.h (revision 280828) @@ -1,655 +1,654 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #ifndef _ATH_AR5212_H_ #define _ATH_AR5212_H_ #include "ah_eeprom.h" #define AR5212_MAGIC 0x19541014 /* DCU Transmit Filter macros */ #define CALC_MMR(dcu, idx) \ ( (4 * dcu) + (idx < 32 ? 0 : (idx < 64 ? 1 : (idx < 96 ? 2 : 3))) ) #define TXBLK_FROM_MMR(mmr) \ (AR_D_TXBLK_BASE + ((mmr & 0x1f) << 6) + ((mmr & 0x20) >> 3)) #define CALC_TXBLK_ADDR(dcu, idx) (TXBLK_FROM_MMR(CALC_MMR(dcu, idx))) #define CALC_TXBLK_VALUE(idx) (1 << (idx & 0x1f)) /* MAC register values */ #define INIT_INTERRUPT_MASK \ ( AR_IMR_TXERR | AR_IMR_TXOK | AR_IMR_RXORN | \ AR_IMR_RXERR | AR_IMR_RXOK | AR_IMR_TXURN | \ AR_IMR_HIUERR ) #define INIT_BEACON_CONTROL \ ((INIT_RESET_TSF << 24) | (INIT_BEACON_EN << 23) | \ (INIT_TIM_OFFSET << 16) | INIT_BEACON_PERIOD) #define INIT_CONFIG_STATUS 0x00000000 #define INIT_RSSI_THR 0x00000781 /* Missed beacon counter initialized to 0x7 (max is 0xff) */ #define INIT_IQCAL_LOG_COUNT_MAX 0xF #define INIT_BCON_CNTRL_REG 0x00000000 #define INIT_USEC 40 #define HALF_RATE_USEC 19 /* ((40 / 2) - 1 ) */ #define QUARTER_RATE_USEC 9 /* ((40 / 4) - 1 ) */ #define RX_NON_FULL_RATE_LATENCY 63 #define TX_HALF_RATE_LATENCY 108 #define TX_QUARTER_RATE_LATENCY 216 #define IFS_SLOT_FULL_RATE 0x168 /* 9 us half, 40 MHz core clock (9*40) */ #define IFS_SLOT_HALF_RATE 0x104 /* 13 us half, 20 MHz core clock (13*20) */ #define IFS_SLOT_QUARTER_RATE 0xD2 /* 21 us quarter, 10 MHz core clock (21*10) */ #define IFS_EIFS_FULL_RATE 0xE60 /* (74 + (2 * 9)) * 40MHz core clock */ #define IFS_EIFS_HALF_RATE 0xDAC /* (149 + (2 * 13)) * 20MHz core clock */ #define IFS_EIFS_QUARTER_RATE 0xD48 /* (298 + (2 * 21)) * 10MHz core clock */ #define ACK_CTS_TIMEOUT_11A 0x3E8 /* ACK timeout in 11a core clocks */ /* Tx frame start to tx data start delay */ #define TX_FRAME_D_START_HALF_RATE 0xc #define TX_FRAME_D_START_QUARTER_RATE 0xd /* * Various fifo fill before Tx start, in 64-byte units * i.e. put the frame in the air while still DMAing */ #define MIN_TX_FIFO_THRESHOLD 0x1 #define MAX_TX_FIFO_THRESHOLD ((IEEE80211_MAX_LEN / 64) + 1) #define INIT_TX_FIFO_THRESHOLD MIN_TX_FIFO_THRESHOLD #define HAL_DECOMP_MASK_SIZE 128 /* 1 byte per key */ /* * Gain support. */ #define NUM_CORNER_FIX_BITS 4 #define NUM_CORNER_FIX_BITS_5112 7 #define DYN_ADJ_UP_MARGIN 15 #define DYN_ADJ_LO_MARGIN 20 #define PHY_PROBE_CCK_CORRECTION 5 #define CCK_OFDM_GAIN_DELTA 15 enum GAIN_PARAMS { GP_TXCLIP, GP_PD90, GP_PD84, GP_GSEL, }; enum GAIN_PARAMS_5112 { GP_MIXGAIN_OVR, GP_PWD_138, GP_PWD_137, GP_PWD_136, GP_PWD_132, GP_PWD_131, GP_PWD_130, }; typedef struct _gainOptStep { int16_t paramVal[NUM_CORNER_FIX_BITS_5112]; int32_t stepGain; int8_t stepName[16]; } GAIN_OPTIMIZATION_STEP; typedef struct { uint32_t numStepsInLadder; uint32_t defaultStepNum; GAIN_OPTIMIZATION_STEP optStep[10]; } GAIN_OPTIMIZATION_LADDER; typedef struct { uint32_t currStepNum; uint32_t currGain; uint32_t targetGain; uint32_t loTrig; uint32_t hiTrig; uint32_t active; const GAIN_OPTIMIZATION_STEP *currStep; } GAIN_VALUES; /* RF HAL structures */ typedef struct RfHalFuncs { void *priv; /* private state */ void (*rfDetach)(struct ath_hal *ah); void (*writeRegs)(struct ath_hal *, u_int modeIndex, u_int freqIndex, int regWrites); uint32_t *(*getRfBank)(struct ath_hal *ah, int bank); HAL_BOOL (*setChannel)(struct ath_hal *, const struct ieee80211_channel *); HAL_BOOL (*setRfRegs)(struct ath_hal *, const struct ieee80211_channel *, uint16_t modesIndex, uint16_t *rfXpdGain); HAL_BOOL (*setPowerTable)(struct ath_hal *ah, int16_t *minPower, int16_t *maxPower, const struct ieee80211_channel *, uint16_t *rfXpdGain); HAL_BOOL (*getChannelMaxMinPower)(struct ath_hal *ah, const struct ieee80211_channel *, int16_t *maxPow, int16_t *minPow); int16_t (*getNfAdjust)(struct ath_hal *, const HAL_CHANNEL_INTERNAL*); } RF_HAL_FUNCS; struct ar5212AniParams { int maxNoiseImmunityLevel; /* [0..4] */ int totalSizeDesired[5]; int coarseHigh[5]; int coarseLow[5]; int firpwr[5]; int maxSpurImmunityLevel; /* [0..7] */ int cycPwrThr1[8]; int maxFirstepLevel; /* [0..2] */ int firstep[3]; uint32_t ofdmTrigHigh; uint32_t ofdmTrigLow; uint32_t cckTrigHigh; uint32_t cckTrigLow; int32_t rssiThrLow; uint32_t rssiThrHigh; int period; /* update listen period */ /* NB: intentionally ordered so data exported to user space is first */ uint32_t ofdmPhyErrBase; /* Base value for ofdm err counter */ uint32_t cckPhyErrBase; /* Base value for cck err counters */ }; /* * Per-channel ANI state private to the driver. */ struct ar5212AniState { uint8_t noiseImmunityLevel; uint8_t spurImmunityLevel; uint8_t firstepLevel; uint8_t ofdmWeakSigDetectOff; uint8_t cckWeakSigThreshold; uint32_t listenTime; /* NB: intentionally ordered so data exported to user space is first */ uint32_t txFrameCount; /* Last txFrameCount */ uint32_t rxFrameCount; /* Last rx Frame count */ uint32_t cycleCount; /* Last cycleCount (to detect wrap-around) */ uint32_t ofdmPhyErrCount;/* OFDM err count since last reset */ uint32_t cckPhyErrCount; /* CCK err count since last reset */ const struct ar5212AniParams *params; }; #define HAL_ANI_ENA 0x00000001 /* ANI operation enabled */ #define HAL_RSSI_ANI_ENA 0x00000002 /* rssi-based processing ena'd*/ struct ar5212Stats { uint32_t ast_ani_niup; /* ANI increased noise immunity */ uint32_t ast_ani_nidown; /* ANI decreased noise immunity */ uint32_t ast_ani_spurup; /* ANI increased spur immunity */ uint32_t ast_ani_spurdown;/* ANI descreased spur immunity */ uint32_t ast_ani_ofdmon; /* ANI OFDM weak signal detect on */ uint32_t ast_ani_ofdmoff;/* ANI OFDM weak signal detect off */ uint32_t ast_ani_cckhigh;/* ANI CCK weak signal threshold high */ uint32_t ast_ani_ccklow; /* ANI CCK weak signal threshold low */ uint32_t ast_ani_stepup; /* ANI increased first step level */ uint32_t ast_ani_stepdown;/* ANI decreased first step level */ uint32_t ast_ani_ofdmerrs;/* ANI cumulative ofdm phy err count */ uint32_t ast_ani_cckerrs;/* ANI cumulative cck phy err count */ uint32_t ast_ani_reset; /* ANI parameters zero'd for non-STA */ uint32_t ast_ani_lzero; /* ANI listen time forced to zero */ uint32_t ast_ani_lneg; /* ANI listen time calculated < 0 */ HAL_MIB_STATS ast_mibstats; /* MIB counter stats */ HAL_NODE_STATS ast_nodestats; /* Latest rssi stats from driver */ }; /* * NF Cal history buffer */ #define AR5212_CCA_MAX_GOOD_VALUE -95 #define AR5212_CCA_MAX_HIGH_VALUE -62 #define AR5212_CCA_MIN_BAD_VALUE -125 #define AR512_NF_CAL_HIST_MAX 5 struct ar5212NfCalHist { int16_t nfCalBuffer[AR512_NF_CAL_HIST_MAX]; int16_t privNF; uint8_t currIndex; uint8_t first_run; uint8_t invalidNFcount; }; struct ath_hal_5212 { struct ath_hal_private ah_priv; /* base class */ /* * Per-chip common Initialization data. * NB: RF backends have their own ini data. */ HAL_INI_ARRAY ah_ini_modes; HAL_INI_ARRAY ah_ini_common; GAIN_VALUES ah_gainValues; uint8_t ah_macaddr[IEEE80211_ADDR_LEN]; uint8_t ah_bssid[IEEE80211_ADDR_LEN]; uint8_t ah_bssidmask[IEEE80211_ADDR_LEN]; uint16_t ah_assocId; /* * Runtime state. */ uint32_t ah_maskReg; /* copy of AR_IMR */ struct ar5212Stats ah_stats; /* various statistics */ RF_HAL_FUNCS *ah_rfHal; uint32_t ah_txDescMask; /* mask for TXDESC */ uint32_t ah_txOkInterruptMask; uint32_t ah_txErrInterruptMask; uint32_t ah_txDescInterruptMask; uint32_t ah_txEolInterruptMask; uint32_t ah_txUrnInterruptMask; HAL_TX_QUEUE_INFO ah_txq[HAL_NUM_TX_QUEUES]; uint32_t ah_intrTxqs; /* tx q interrupt state */ /* decomp mask array */ uint8_t ah_decompMask[HAL_DECOMP_MASK_SIZE]; HAL_ANT_SETTING ah_antControl; /* antenna setting */ HAL_BOOL ah_diversity; /* fast diversity setting */ enum { IQ_CAL_INACTIVE, IQ_CAL_RUNNING, IQ_CAL_DONE } ah_bIQCalibration; /* IQ calibrate state */ HAL_RFGAIN ah_rfgainState; /* RF gain calibrartion state */ uint32_t ah_tx6PowerInHalfDbm; /* power output for 6Mb tx */ uint32_t ah_staId1Defaults; /* STA_ID1 default settings */ uint32_t ah_miscMode; /* MISC_MODE settings */ uint32_t ah_rssiThr; /* RSSI_THR settings */ HAL_BOOL ah_cwCalRequire; /* for ap51 */ HAL_BOOL ah_tpcEnabled; /* per-packet tpc enabled */ HAL_BOOL ah_phyPowerOn; /* PHY power state */ HAL_BOOL ah_isHb63; /* cached HB63 check */ uint32_t ah_macTPC; /* tpc register */ uint32_t ah_beaconInterval; /* XXX */ enum { AUTO_32KHZ, /* use it if 32kHz crystal present */ USE_32KHZ, /* do it regardless */ DONT_USE_32KHZ, /* don't use it regardless */ } ah_enable32kHzClock; /* whether to sleep at 32kHz */ uint32_t ah_ofdmTxPower; int16_t ah_txPowerIndexOffset; /* * Noise floor cal histogram support. */ struct ar5212NfCalHist ah_nfCalHist; u_int ah_slottime; /* user-specified slot time */ u_int ah_acktimeout; /* user-specified ack timeout */ u_int ah_ctstimeout; /* user-specified cts timeout */ u_int ah_sifstime; /* user-specified sifs time */ /* * RF Silent handling; setup according to the EEPROM. */ uint32_t ah_gpioSelect; /* GPIO pin to use */ uint32_t ah_polarity; /* polarity to disable RF */ uint32_t ah_gpioBit; /* after init, prev value */ /* * ANI support. */ uint32_t ah_procPhyErr; /* Process Phy errs */ HAL_BOOL ah_hasHwPhyCounters; /* Hardware has phy counters */ struct ar5212AniParams ah_aniParams24; /* 2.4GHz parameters */ struct ar5212AniParams ah_aniParams5; /* 5GHz parameters */ struct ar5212AniState *ah_curani; /* cached last reference */ struct ar5212AniState ah_ani[AH_MAXCHAN]; /* per-channel state */ - HAL_CHANNEL_SURVEY ah_chansurvey; /* channel survey */ /* AR5416 uses some of the AR5212 ANI code; these are the ANI methods */ HAL_BOOL (*ah_aniControl) (struct ath_hal *, HAL_ANI_CMD cmd, int param); /* * Transmit power state. Note these are maintained * here so they can be retrieved by diagnostic tools. */ uint16_t *ah_pcdacTable; u_int ah_pcdacTableSize; uint16_t ah_ratesArray[37]; uint8_t ah_txTrigLev; /* current Tx trigger level */ uint8_t ah_maxTxTrigLev; /* max tx trigger level */ /* * Channel Tx, Rx, Rx Clear State */ uint32_t ah_cycleCount; uint32_t ah_ctlBusy; uint32_t ah_rxBusy; uint32_t ah_txBusy; uint32_t ah_rx_chainmask; uint32_t ah_tx_chainmask; }; #define AH5212(_ah) ((struct ath_hal_5212 *)(_ah)) /* * IS_XXXX macros test the MAC version * IS_RADXXX macros test the radio/RF version (matching both 2G-only and 2/5G) * * Some single chip radios have equivalent radio/RF (e.g. 5112) * for those use IS_RADXXX_ANY macros. */ #define IS_2317(ah) \ ((AH_PRIVATE(ah)->ah_devid == AR5212_AR2317_REV1) || \ (AH_PRIVATE(ah)->ah_devid == AR5212_AR2317_REV2)) #define IS_2316(ah) \ (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_2415) #define IS_2413(ah) \ (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_2413 || IS_2316(ah)) #define IS_5424(ah) \ (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_5424 || \ (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_5413 && \ AH_PRIVATE(ah)->ah_macRev <= AR_SREV_D2PLUS_MS)) #define IS_5413(ah) \ (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_5413 || IS_5424(ah)) #define IS_2425(ah) \ (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_2425) #define IS_2417(ah) \ ((AH_PRIVATE(ah)->ah_macVersion) == AR_SREV_2417) #define IS_HB63(ah) (AH5212(ah)->ah_isHb63 == AH_TRUE) #define AH_RADIO_MAJOR(ah) \ (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) #define AH_RADIO_MINOR(ah) \ (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MINOR) #define IS_RAD5111(ah) \ (AH_RADIO_MAJOR(ah) == AR_RAD5111_SREV_MAJOR || \ AH_RADIO_MAJOR(ah) == AR_RAD2111_SREV_MAJOR) #define IS_RAD5112(ah) \ (AH_RADIO_MAJOR(ah) == AR_RAD5112_SREV_MAJOR || \ AH_RADIO_MAJOR(ah) == AR_RAD2112_SREV_MAJOR) /* NB: does not include 5413 as Atheros' IS_5112 macro does */ #define IS_RAD5112_ANY(ah) \ (AR_RAD5112_SREV_MAJOR <= AH_RADIO_MAJOR(ah) && \ AH_RADIO_MAJOR(ah) <= AR_RAD2413_SREV_MAJOR) #define IS_RAD5112_REV1(ah) \ (IS_RAD5112(ah) && \ AH_RADIO_MINOR(ah) < (AR_RAD5112_SREV_2_0 & AR_RADIO_SREV_MINOR)) #define IS_RADX112_REV2(ah) \ (AH_PRIVATE(ah)->ah_analog5GhzRev == AR_RAD5112_SREV_2_0 || \ AH_PRIVATE(ah)->ah_analog5GhzRev == AR_RAD2112_SREV_2_0 || \ AH_PRIVATE(ah)->ah_analog5GhzRev == AR_RAD2112_SREV_2_1 || \ AH_PRIVATE(ah)->ah_analog5GhzRev == AR_RAD5112_SREV_2_1) #define ar5212RfDetach(ah) do { \ if (AH5212(ah)->ah_rfHal != AH_NULL) \ AH5212(ah)->ah_rfHal->rfDetach(ah); \ } while (0) #define ar5212GetRfBank(ah, b) \ AH5212(ah)->ah_rfHal->getRfBank(ah, b) /* * Hack macros for Nala/San: 11b is handled * using 11g; flip the channel flags to accomplish this. */ #define SAVE_CCK(_ah, _chan, _flag) do { \ if ((IS_2425(_ah) || IS_2417(_ah)) && \ (((_chan)->ic_flags) & IEEE80211_CHAN_CCK)) { \ (_chan)->ic_flags &= ~IEEE80211_CHAN_CCK; \ (_chan)->ic_flags |= IEEE80211_CHAN_DYN; \ (_flag) = AH_TRUE; \ } else \ (_flag) = AH_FALSE; \ } while (0) #define RESTORE_CCK(_ah, _chan, _flag) do { \ if ((_flag) && (IS_2425(_ah) || IS_2417(_ah))) { \ (_chan)->ic_flags &= ~IEEE80211_CHAN_DYN; \ (_chan)->ic_flags |= IEEE80211_CHAN_CCK; \ } \ } while (0) struct ath_hal; extern uint32_t ar5212GetRadioRev(struct ath_hal *ah); extern void ar5212InitState(struct ath_hal_5212 *, uint16_t devid, HAL_SOFTC, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, HAL_STATUS *status); extern void ar5212Detach(struct ath_hal *ah); extern HAL_BOOL ar5212ChipTest(struct ath_hal *ah); extern HAL_BOOL ar5212GetChannelEdges(struct ath_hal *ah, uint16_t flags, uint16_t *low, uint16_t *high); extern HAL_BOOL ar5212FillCapabilityInfo(struct ath_hal *ah); extern void ar5212SetBeaconTimers(struct ath_hal *ah, const HAL_BEACON_TIMERS *); extern void ar5212BeaconInit(struct ath_hal *ah, uint32_t next_beacon, uint32_t beacon_period); extern void ar5212ResetStaBeaconTimers(struct ath_hal *ah); extern void ar5212SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *); extern uint64_t ar5212GetNextTBTT(struct ath_hal *); extern HAL_BOOL ar5212IsInterruptPending(struct ath_hal *ah); extern HAL_BOOL ar5212GetPendingInterrupts(struct ath_hal *ah, HAL_INT *); extern HAL_INT ar5212GetInterrupts(struct ath_hal *ah); extern HAL_INT ar5212SetInterrupts(struct ath_hal *ah, HAL_INT ints); extern uint32_t ar5212GetKeyCacheSize(struct ath_hal *); extern HAL_BOOL ar5212IsKeyCacheEntryValid(struct ath_hal *, uint16_t entry); extern HAL_BOOL ar5212ResetKeyCacheEntry(struct ath_hal *ah, uint16_t entry); extern HAL_BOOL ar5212SetKeyCacheEntryMac(struct ath_hal *, uint16_t entry, const uint8_t *mac); extern HAL_BOOL ar5212SetKeyCacheEntry(struct ath_hal *ah, uint16_t entry, const HAL_KEYVAL *k, const uint8_t *mac, int xorKey); extern void ar5212GetMacAddress(struct ath_hal *ah, uint8_t *mac); extern HAL_BOOL ar5212SetMacAddress(struct ath_hal *ah, const uint8_t *); extern void ar5212GetBssIdMask(struct ath_hal *ah, uint8_t *mac); extern HAL_BOOL ar5212SetBssIdMask(struct ath_hal *, const uint8_t *); extern HAL_BOOL ar5212EepromRead(struct ath_hal *, u_int off, uint16_t *data); extern HAL_BOOL ar5212EepromWrite(struct ath_hal *, u_int off, uint16_t data); extern HAL_BOOL ar5212SetRegulatoryDomain(struct ath_hal *ah, uint16_t regDomain, HAL_STATUS *stats); extern u_int ar5212GetWirelessModes(struct ath_hal *ah); extern void ar5212EnableRfKill(struct ath_hal *); extern HAL_BOOL ar5212GpioCfgOutput(struct ath_hal *, uint32_t gpio, HAL_GPIO_MUX_TYPE); extern HAL_BOOL ar5212GpioCfgInput(struct ath_hal *, uint32_t gpio); extern HAL_BOOL ar5212GpioSet(struct ath_hal *, uint32_t gpio, uint32_t val); extern uint32_t ar5212GpioGet(struct ath_hal *ah, uint32_t gpio); extern void ar5212GpioSetIntr(struct ath_hal *ah, u_int, uint32_t ilevel); extern void ar5212SetLedState(struct ath_hal *ah, HAL_LED_STATE state); extern void ar5212WriteAssocid(struct ath_hal *ah, const uint8_t *bssid, uint16_t assocId); extern uint32_t ar5212GetTsf32(struct ath_hal *ah); extern uint64_t ar5212GetTsf64(struct ath_hal *ah); extern void ar5212SetTsf64(struct ath_hal *ah, uint64_t tsf64); extern void ar5212ResetTsf(struct ath_hal *ah); extern void ar5212SetBasicRate(struct ath_hal *ah, HAL_RATE_SET *pSet); extern uint32_t ar5212GetRandomSeed(struct ath_hal *ah); extern HAL_BOOL ar5212DetectCardPresent(struct ath_hal *ah); extern void ar5212EnableMibCounters(struct ath_hal *); extern void ar5212DisableMibCounters(struct ath_hal *); extern void ar5212UpdateMibCounters(struct ath_hal *ah, HAL_MIB_STATS* stats); extern HAL_BOOL ar5212IsJapanChannelSpreadSupported(struct ath_hal *ah); extern uint32_t ar5212GetCurRssi(struct ath_hal *ah); extern u_int ar5212GetDefAntenna(struct ath_hal *ah); extern void ar5212SetDefAntenna(struct ath_hal *ah, u_int antenna); extern HAL_ANT_SETTING ar5212GetAntennaSwitch(struct ath_hal *); extern HAL_BOOL ar5212SetAntennaSwitch(struct ath_hal *, HAL_ANT_SETTING); extern HAL_BOOL ar5212IsSleepAfterBeaconBroken(struct ath_hal *ah); extern HAL_BOOL ar5212SetSifsTime(struct ath_hal *, u_int); extern u_int ar5212GetSifsTime(struct ath_hal *); extern HAL_BOOL ar5212SetSlotTime(struct ath_hal *, u_int); extern u_int ar5212GetSlotTime(struct ath_hal *); extern HAL_BOOL ar5212SetAckTimeout(struct ath_hal *, u_int); extern u_int ar5212GetAckTimeout(struct ath_hal *); extern HAL_BOOL ar5212SetAckCTSRate(struct ath_hal *, u_int); extern u_int ar5212GetAckCTSRate(struct ath_hal *); extern HAL_BOOL ar5212SetCTSTimeout(struct ath_hal *, u_int); extern u_int ar5212GetCTSTimeout(struct ath_hal *); extern HAL_BOOL ar5212SetDecompMask(struct ath_hal *, uint16_t, int); void ar5212SetCoverageClass(struct ath_hal *, uint8_t, int); extern void ar5212SetPCUConfig(struct ath_hal *); extern HAL_BOOL ar5212Use32KHzclock(struct ath_hal *ah, HAL_OPMODE opmode); extern void ar5212SetupClock(struct ath_hal *ah, HAL_OPMODE opmode); extern void ar5212RestoreClock(struct ath_hal *ah, HAL_OPMODE opmode); extern int16_t ar5212GetNfAdjust(struct ath_hal *, const HAL_CHANNEL_INTERNAL *); extern void ar5212SetCompRegs(struct ath_hal *ah); extern HAL_STATUS ar5212GetCapability(struct ath_hal *, HAL_CAPABILITY_TYPE, uint32_t, uint32_t *); extern HAL_BOOL ar5212SetCapability(struct ath_hal *, HAL_CAPABILITY_TYPE, uint32_t, uint32_t, HAL_STATUS *); extern HAL_BOOL ar5212GetDiagState(struct ath_hal *ah, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize); extern HAL_STATUS ar5212SetQuiet(struct ath_hal *ah, uint32_t period, uint32_t duration, uint32_t nextStart, HAL_QUIET_FLAG flag); extern HAL_BOOL ar5212GetMibCycleCounts(struct ath_hal *, HAL_SURVEY_SAMPLE *); extern void ar5212SetChainMasks(struct ath_hal *, uint32_t, uint32_t); extern HAL_BOOL ar5212SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip); extern HAL_POWER_MODE ar5212GetPowerMode(struct ath_hal *ah); extern HAL_BOOL ar5212GetPowerStatus(struct ath_hal *ah); extern uint32_t ar5212GetRxDP(struct ath_hal *ath, HAL_RX_QUEUE); extern void ar5212SetRxDP(struct ath_hal *ah, uint32_t rxdp, HAL_RX_QUEUE); extern void ar5212EnableReceive(struct ath_hal *ah); extern HAL_BOOL ar5212StopDmaReceive(struct ath_hal *ah); extern void ar5212StartPcuReceive(struct ath_hal *ah); extern void ar5212StopPcuReceive(struct ath_hal *ah); extern void ar5212SetMulticastFilter(struct ath_hal *ah, uint32_t filter0, uint32_t filter1); extern HAL_BOOL ar5212ClrMulticastFilterIndex(struct ath_hal *, uint32_t ix); extern HAL_BOOL ar5212SetMulticastFilterIndex(struct ath_hal *, uint32_t ix); extern uint32_t ar5212GetRxFilter(struct ath_hal *ah); extern void ar5212SetRxFilter(struct ath_hal *ah, uint32_t bits); extern HAL_BOOL ar5212SetupRxDesc(struct ath_hal *, struct ath_desc *, uint32_t size, u_int flags); extern HAL_STATUS ar5212ProcRxDesc(struct ath_hal *ah, struct ath_desc *, uint32_t, struct ath_desc *, uint64_t, struct ath_rx_status *); extern HAL_BOOL ar5212Reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_BOOL bChannelChange, HAL_STATUS *status); extern HAL_BOOL ar5212SetChannel(struct ath_hal *, const struct ieee80211_channel *); extern void ar5212SetOperatingMode(struct ath_hal *ah, int opmode); extern HAL_BOOL ar5212PhyDisable(struct ath_hal *ah); extern HAL_BOOL ar5212Disable(struct ath_hal *ah); extern HAL_BOOL ar5212ChipReset(struct ath_hal *ah, const struct ieee80211_channel *); extern HAL_BOOL ar5212PerCalibration(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_BOOL *isIQdone); extern HAL_BOOL ar5212PerCalibrationN(struct ath_hal *ah, struct ieee80211_channel *chan, u_int chainMask, HAL_BOOL longCal, HAL_BOOL *isCalDone); extern HAL_BOOL ar5212ResetCalValid(struct ath_hal *ah, const struct ieee80211_channel *); extern int16_t ar5212GetNoiseFloor(struct ath_hal *ah); extern void ar5212InitNfCalHistBuffer(struct ath_hal *); extern int16_t ar5212GetNfHistMid(const int16_t calData[]); extern void ar5212SetSpurMitigation(struct ath_hal *, const struct ieee80211_channel *); extern HAL_BOOL ar5212SetAntennaSwitchInternal(struct ath_hal *ah, HAL_ANT_SETTING settings, const struct ieee80211_channel *); extern HAL_BOOL ar5212SetTxPowerLimit(struct ath_hal *ah, uint32_t limit); extern HAL_BOOL ar5212GetChipPowerLimits(struct ath_hal *ah, struct ieee80211_channel *chan); extern void ar5212InitializeGainValues(struct ath_hal *); extern HAL_RFGAIN ar5212GetRfgain(struct ath_hal *ah); extern void ar5212RequestRfgain(struct ath_hal *); extern HAL_BOOL ar5212UpdateTxTrigLevel(struct ath_hal *, HAL_BOOL IncTrigLevel); extern HAL_BOOL ar5212SetTxQueueProps(struct ath_hal *ah, int q, const HAL_TXQ_INFO *qInfo); extern HAL_BOOL ar5212GetTxQueueProps(struct ath_hal *ah, int q, HAL_TXQ_INFO *qInfo); extern int ar5212SetupTxQueue(struct ath_hal *ah, HAL_TX_QUEUE type, const HAL_TXQ_INFO *qInfo); extern HAL_BOOL ar5212ReleaseTxQueue(struct ath_hal *ah, u_int q); extern HAL_BOOL ar5212ResetTxQueue(struct ath_hal *ah, u_int q); extern uint32_t ar5212GetTxDP(struct ath_hal *ah, u_int q); extern HAL_BOOL ar5212SetTxDP(struct ath_hal *ah, u_int q, uint32_t txdp); extern HAL_BOOL ar5212StartTxDma(struct ath_hal *ah, u_int q); extern uint32_t ar5212NumTxPending(struct ath_hal *ah, u_int q); extern HAL_BOOL ar5212StopTxDma(struct ath_hal *ah, u_int q); extern HAL_BOOL ar5212SetupTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int txPower, u_int txRate0, u_int txTries0, u_int keyIx, u_int antMode, u_int flags, u_int rtsctsRate, u_int rtsctsDuration, u_int compicvLen, u_int compivLen, u_int comp); extern HAL_BOOL ar5212SetupXTxDesc(struct ath_hal *, struct ath_desc *, u_int txRate1, u_int txRetries1, u_int txRate2, u_int txRetries2, u_int txRate3, u_int txRetries3); extern HAL_BOOL ar5212FillTxDesc(struct ath_hal *ah, struct ath_desc *ds, HAL_DMA_ADDR *bufAddrList, uint32_t *segLenList, u_int descId, u_int qcuId, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0); extern HAL_STATUS ar5212ProcTxDesc(struct ath_hal *ah, struct ath_desc *, struct ath_tx_status *); extern void ar5212GetTxIntrQueue(struct ath_hal *ah, uint32_t *); extern void ar5212IntrReqTxDesc(struct ath_hal *ah, struct ath_desc *); extern HAL_BOOL ar5212GetTxCompletionRates(struct ath_hal *ah, const struct ath_desc *ds0, int *rates, int *tries); extern void ar5212SetTxDescLink(struct ath_hal *ah, void *ds, uint32_t link); extern void ar5212GetTxDescLink(struct ath_hal *ah, void *ds, uint32_t *link); extern void ar5212GetTxDescLinkPtr(struct ath_hal *ah, void *ds, uint32_t **linkptr); extern const HAL_RATE_TABLE *ar5212GetRateTable(struct ath_hal *, u_int mode); extern void ar5212AniAttach(struct ath_hal *, const struct ar5212AniParams *, const struct ar5212AniParams *, HAL_BOOL ena); extern void ar5212AniDetach(struct ath_hal *); extern struct ar5212AniState *ar5212AniGetCurrentState(struct ath_hal *); extern struct ar5212Stats *ar5212AniGetCurrentStats(struct ath_hal *); extern HAL_BOOL ar5212AniControl(struct ath_hal *, HAL_ANI_CMD cmd, int param); extern HAL_BOOL ar5212AniSetParams(struct ath_hal *, const struct ar5212AniParams *, const struct ar5212AniParams *); struct ath_rx_status; extern void ar5212AniPhyErrReport(struct ath_hal *ah, const struct ath_rx_status *rs); extern void ar5212ProcessMibIntr(struct ath_hal *, const HAL_NODE_STATS *); extern void ar5212RxMonitor(struct ath_hal *, const HAL_NODE_STATS *, const struct ieee80211_channel *); extern void ar5212AniPoll(struct ath_hal *, const struct ieee80211_channel *); extern void ar5212AniReset(struct ath_hal *, const struct ieee80211_channel *, HAL_OPMODE, int); extern HAL_BOOL ar5212IsNFCalInProgress(struct ath_hal *ah); extern HAL_BOOL ar5212WaitNFCalComplete(struct ath_hal *ah, int i); extern void ar5212EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); extern HAL_BOOL ar5212GetDfsDefaultThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); extern void ar5212GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); extern HAL_BOOL ar5212ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs, uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event); extern HAL_BOOL ar5212IsFastClockEnabled(struct ath_hal *ah); extern uint32_t ar5212Get11nExtBusy(struct ath_hal *ah); #endif /* _ATH_AR5212_H_ */ Index: head/sys/dev/ath/ath_hal/ar5212/ar5212_ani.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5212/ar5212_ani.c (revision 280827) +++ head/sys/dev/ath/ath_hal/ar5212/ar5212_ani.c (revision 280828) @@ -1,1050 +1,1041 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_desc.h" #include "ar5212/ar5212.h" #include "ar5212/ar5212reg.h" #include "ar5212/ar5212phy.h" /* * Anti noise immunity support. We track phy errors and react * to excessive errors by adjusting the noise immunity parameters. */ #define HAL_EP_RND(x, mul) \ ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) #define BEACON_RSSI(ahp) \ HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \ HAL_RSSI_EP_MULTIPLIER) /* * ANI processing tunes radio parameters according to PHY errors * and related information. This is done for for noise and spur * immunity in all operating modes if the device indicates it's * capable at attach time. In addition, when there is a reference * rssi value (e.g. beacon frames from an ap in station mode) * further tuning is done. * * ANI_ENA indicates whether any ANI processing should be done; * this is specified at attach time. * * ANI_ENA_RSSI indicates whether rssi-based processing should * done, this is enabled based on operating mode and is meaningful * only if ANI_ENA is true. * * ANI parameters are typically controlled only by the hal. The * AniControl interface however permits manual tuning through the * diagnostic api. */ #define ANI_ENA(ah) \ (AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA) #define ANI_ENA_RSSI(ah) \ (AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA) #define ah_mibStats ah_stats.ast_mibstats static void enableAniMIBCounters(struct ath_hal *ah, const struct ar5212AniParams *params) { struct ath_hal_5212 *ahp = AH5212(ah); HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Enable mib counters: " "OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n", __func__, params->ofdmPhyErrBase, params->cckPhyErrBase); OS_REG_WRITE(ah, AR_FILTOFDM, 0); OS_REG_WRITE(ah, AR_FILTCCK, 0); OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase); OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase); OS_REG_WRITE(ah, AR_PHYCNTMASK1, AR_PHY_ERR_OFDM_TIMING); OS_REG_WRITE(ah, AR_PHYCNTMASK2, AR_PHY_ERR_CCK_TIMING); ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save+clear counters*/ ar5212EnableMibCounters(ah); /* enable everything */ } static void disableAniMIBCounters(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); HALDEBUG(ah, HAL_DEBUG_ANI, "Disable MIB counters\n"); ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save stats */ ar5212DisableMibCounters(ah); /* disable everything */ OS_REG_WRITE(ah, AR_PHYCNTMASK1, 0); OS_REG_WRITE(ah, AR_PHYCNTMASK2, 0); } /* * Return the current ANI state of the channel we're on */ struct ar5212AniState * ar5212AniGetCurrentState(struct ath_hal *ah) { return AH5212(ah)->ah_curani; } /* * Return the current statistics. */ struct ar5212Stats * ar5212AniGetCurrentStats(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); /* update mib stats so we return current data */ /* XXX? side-effects to doing this here? */ ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); return &ahp->ah_stats; } static void setPhyErrBase(struct ath_hal *ah, struct ar5212AniParams *params) { if (params->ofdmTrigHigh >= AR_PHY_COUNTMAX) { HALDEBUG(ah, HAL_DEBUG_ANY, "OFDM Trigger %d is too high for hw counters, using max\n", params->ofdmTrigHigh); params->ofdmPhyErrBase = 0; } else params->ofdmPhyErrBase = AR_PHY_COUNTMAX - params->ofdmTrigHigh; if (params->cckTrigHigh >= AR_PHY_COUNTMAX) { HALDEBUG(ah, HAL_DEBUG_ANY, "CCK Trigger %d is too high for hw counters, using max\n", params->cckTrigHigh); params->cckPhyErrBase = 0; } else params->cckPhyErrBase = AR_PHY_COUNTMAX - params->cckTrigHigh; } /* * Setup ANI handling. Sets all thresholds and reset the * channel statistics. Note that ar5212AniReset should be * called by ar5212Reset before anything else happens and * that's where we force initial settings. */ void ar5212AniAttach(struct ath_hal *ah, const struct ar5212AniParams *params24, const struct ar5212AniParams *params5, HAL_BOOL enable) { struct ath_hal_5212 *ahp = AH5212(ah); ahp->ah_hasHwPhyCounters = AH_PRIVATE(ah)->ah_caps.halHwPhyCounterSupport; if (params24 != AH_NULL) { OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24)); setPhyErrBase(ah, &ahp->ah_aniParams24); } if (params5 != AH_NULL) { OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5)); setPhyErrBase(ah, &ahp->ah_aniParams5); } OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani)); if (ahp->ah_hasHwPhyCounters) { /* Enable MIB Counters */ enableAniMIBCounters(ah, &ahp->ah_aniParams24 /*XXX*/); } if (enable) { /* Enable ani now */ HALASSERT(params24 != AH_NULL && params5 != AH_NULL); ahp->ah_procPhyErr |= HAL_ANI_ENA; } else { ahp->ah_procPhyErr &= ~HAL_ANI_ENA; } } HAL_BOOL ar5212AniSetParams(struct ath_hal *ah, const struct ar5212AniParams *params24, const struct ar5212AniParams *params5) { struct ath_hal_5212 *ahp = AH5212(ah); HAL_BOOL ena = (ahp->ah_procPhyErr & HAL_ANI_ENA) != 0; ar5212AniControl(ah, HAL_ANI_MODE, AH_FALSE); OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24)); setPhyErrBase(ah, &ahp->ah_aniParams24); OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5)); setPhyErrBase(ah, &ahp->ah_aniParams5); OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani)); ar5212AniReset(ah, AH_PRIVATE(ah)->ah_curchan, AH_PRIVATE(ah)->ah_opmode, AH_FALSE); ar5212AniControl(ah, HAL_ANI_MODE, ena); return AH_TRUE; } /* * Cleanup any ANI state setup. */ void ar5212AniDetach(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); HALDEBUG(ah, HAL_DEBUG_ANI, "Detaching Ani\n"); if (ahp->ah_hasHwPhyCounters) disableAniMIBCounters(ah); } /* * Control Adaptive Noise Immunity Parameters */ HAL_BOOL ar5212AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param) { typedef int TABLE[]; struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212AniState *aniState = ahp->ah_curani; const struct ar5212AniParams *params = AH_NULL; /* * This function may be called before there's a current * channel (eg to disable ANI.) */ if (aniState != AH_NULL) params = aniState->params; OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd); switch (cmd) { case HAL_ANI_NOISE_IMMUNITY_LEVEL: { u_int level = param; if (level > params->maxNoiseImmunityLevel) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: level out of range (%u > %u)\n", __func__, level, params->maxNoiseImmunityLevel); return AH_FALSE; } OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_TOT_DES, params->totalSizeDesired[level]); OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, AR_PHY_AGC_CTL1_COARSE_LOW, params->coarseLow[level]); OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, AR_PHY_AGC_CTL1_COARSE_HIGH, params->coarseHigh[level]); OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRPWR, params->firpwr[level]); if (level > aniState->noiseImmunityLevel) ahp->ah_stats.ast_ani_niup++; else if (level < aniState->noiseImmunityLevel) ahp->ah_stats.ast_ani_nidown++; aniState->noiseImmunityLevel = level; break; } case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: { static const TABLE m1ThreshLow = { 127, 50 }; static const TABLE m2ThreshLow = { 127, 40 }; static const TABLE m1Thresh = { 127, 0x4d }; static const TABLE m2Thresh = { 127, 0x40 }; static const TABLE m2CountThr = { 31, 16 }; static const TABLE m2CountThrLow = { 63, 48 }; u_int on = param ? 1 : 0; OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow[on]); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow[on]); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M1_THRESH, m1Thresh[on]); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2_THRESH, m2Thresh[on]); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2COUNT_THR, m2CountThr[on]); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow[on]); if (on) { OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); ahp->ah_stats.ast_ani_ofdmon++; } else { OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); ahp->ah_stats.ast_ani_ofdmoff++; } aniState->ofdmWeakSigDetectOff = !on; break; } case HAL_ANI_CCK_WEAK_SIGNAL_THR: { static const TABLE weakSigThrCck = { 8, 6 }; u_int high = param ? 1 : 0; OS_REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT, AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, weakSigThrCck[high]); if (high) ahp->ah_stats.ast_ani_cckhigh++; else ahp->ah_stats.ast_ani_ccklow++; aniState->cckWeakSigThreshold = high; break; } case HAL_ANI_FIRSTEP_LEVEL: { u_int level = param; if (level > params->maxFirstepLevel) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: level out of range (%u > %u)\n", __func__, level, params->maxFirstepLevel); return AH_FALSE; } OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP, params->firstep[level]); if (level > aniState->firstepLevel) ahp->ah_stats.ast_ani_stepup++; else if (level < aniState->firstepLevel) ahp->ah_stats.ast_ani_stepdown++; aniState->firstepLevel = level; break; } case HAL_ANI_SPUR_IMMUNITY_LEVEL: { u_int level = param; if (level > params->maxSpurImmunityLevel) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: level out of range (%u > %u)\n", __func__, level, params->maxSpurImmunityLevel); return AH_FALSE; } OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]); if (level > aniState->spurImmunityLevel) ahp->ah_stats.ast_ani_spurup++; else if (level < aniState->spurImmunityLevel) ahp->ah_stats.ast_ani_spurdown++; aniState->spurImmunityLevel = level; break; } case HAL_ANI_PRESENT: break; case HAL_ANI_MODE: if (param == 0) { ahp->ah_procPhyErr &= ~HAL_ANI_ENA; /* Turn off HW counters if we have them */ ar5212AniDetach(ah); ah->ah_setRxFilter(ah, ah->ah_getRxFilter(ah) &~ HAL_RX_FILTER_PHYERR); } else { /* normal/auto mode */ /* don't mess with state if already enabled */ if (ahp->ah_procPhyErr & HAL_ANI_ENA) break; if (ahp->ah_hasHwPhyCounters) { ar5212SetRxFilter(ah, ar5212GetRxFilter(ah) &~ HAL_RX_FILTER_PHYERR); /* Enable MIB Counters */ enableAniMIBCounters(ah, ahp->ah_curani != AH_NULL ? ahp->ah_curani->params: &ahp->ah_aniParams24 /*XXX*/); } else { ah->ah_setRxFilter(ah, ah->ah_getRxFilter(ah) | HAL_RX_FILTER_PHYERR); } ahp->ah_procPhyErr |= HAL_ANI_ENA; } break; #ifdef AH_PRIVATE_DIAG case HAL_ANI_PHYERR_RESET: ahp->ah_stats.ast_ani_ofdmerrs = 0; ahp->ah_stats.ast_ani_cckerrs = 0; break; #endif /* AH_PRIVATE_DIAG */ default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid cmd %u\n", __func__, cmd); return AH_FALSE; } return AH_TRUE; } static void ar5212AniOfdmErrTrigger(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; struct ar5212AniState *aniState; const struct ar5212AniParams *params; HALASSERT(chan != AH_NULL); if (!ANI_ENA(ah)) return; aniState = ahp->ah_curani; params = aniState->params; /* First, raise noise immunity level, up to max */ if (aniState->noiseImmunityLevel+1 <= params->maxNoiseImmunityLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise NI to %u\n", __func__, aniState->noiseImmunityLevel + 1); ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel + 1); return; } /* then, raise spur immunity level, up to max */ if (aniState->spurImmunityLevel+1 <= params->maxSpurImmunityLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise SI to %u\n", __func__, aniState->spurImmunityLevel + 1); ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, aniState->spurImmunityLevel + 1); return; } if (ANI_ENA_RSSI(ah)) { int32_t rssi = BEACON_RSSI(ahp); if (rssi > params->rssiThrHigh) { /* * Beacon rssi is high, can turn off ofdm * weak sig detect. */ if (!aniState->ofdmWeakSigDetectOff) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d OWSD off\n", __func__, rssi); ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_FALSE); ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); return; } /* * If weak sig detect is already off, as last resort, * raise firstep level */ if (aniState->firstepLevel+1 <= params->maxFirstepLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d raise ST %u\n", __func__, rssi, aniState->firstepLevel+1); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1); return; } } else if (rssi > params->rssiThrLow) { /* * Beacon rssi in mid range, need ofdm weak signal * detect, but we can raise firststepLevel. */ if (aniState->ofdmWeakSigDetectOff) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d OWSD on\n", __func__, rssi); ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_TRUE); } if (aniState->firstepLevel+1 <= params->maxFirstepLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d raise ST %u\n", __func__, rssi, aniState->firstepLevel+1); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1); } return; } else { /* * Beacon rssi is low, if in 11b/g mode, turn off ofdm * weak signal detection and zero firstepLevel to * maximize CCK sensitivity */ if (IEEE80211_IS_CHAN_CCK(chan)) { if (!aniState->ofdmWeakSigDetectOff) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d OWSD off\n", __func__, rssi); ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_FALSE); } if (aniState->firstepLevel > 0) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d zero ST (was %u)\n", __func__, rssi, aniState->firstepLevel); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); } return; } } } } static void ar5212AniCckErrTrigger(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; struct ar5212AniState *aniState; const struct ar5212AniParams *params; HALASSERT(chan != AH_NULL); if (!ANI_ENA(ah)) return; /* first, raise noise immunity level, up to max */ aniState = ahp->ah_curani; params = aniState->params; if (aniState->noiseImmunityLevel+1 <= params->maxNoiseImmunityLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise NI to %u\n", __func__, aniState->noiseImmunityLevel + 1); ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel + 1); return; } if (ANI_ENA_RSSI(ah)) { int32_t rssi = BEACON_RSSI(ahp); if (rssi > params->rssiThrLow) { /* * Beacon signal in mid and high range, * raise firstep level. */ if (aniState->firstepLevel+1 <= params->maxFirstepLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d raise ST %u\n", __func__, rssi, aniState->firstepLevel+1); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1); } } else { /* * Beacon rssi is low, zero firstep level to maximize * CCK sensitivity in 11b/g mode. */ /* XXX can optimize */ if (IEEE80211_IS_CHAN_B(chan) || IEEE80211_IS_CHAN_G(chan)) { if (aniState->firstepLevel > 0) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d zero ST (was %u)\n", __func__, rssi, aniState->firstepLevel); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); } } } } } static void ar5212AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState) { struct ath_hal_5212 *ahp = AH5212(ah); aniState->listenTime = 0; if (ahp->ah_hasHwPhyCounters) { const struct ar5212AniParams *params = aniState->params; /* * NB: these are written on reset based on the * ini so we must re-write them! */ OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase); OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase); OS_REG_WRITE(ah, AR_PHYCNTMASK1, AR_PHY_ERR_OFDM_TIMING); OS_REG_WRITE(ah, AR_PHYCNTMASK2, AR_PHY_ERR_CCK_TIMING); /* Clear the mib counters and save them in the stats */ ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); } aniState->ofdmPhyErrCount = 0; aniState->cckPhyErrCount = 0; } /* * Restore/reset the ANI parameters and reset the statistics. * This routine must be called for every channel change. */ void ar5212AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan, HAL_OPMODE opmode, int restore) { struct ath_hal_5212 *ahp = AH5212(ah); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); /* XXX bounds check ic_devdata */ struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata]; uint32_t rxfilter; if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) { OS_MEMZERO(aniState, sizeof(*aniState)); if (IEEE80211_IS_CHAN_2GHZ(chan)) aniState->params = &ahp->ah_aniParams24; else aniState->params = &ahp->ah_aniParams5; ichan->privFlags |= CHANNEL_ANI_INIT; HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0); } ahp->ah_curani = aniState; #if 0 ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n", __func__, chan->ic_freq, chan->ic_flags, restore, opmode, ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); #else HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n", __func__, chan->ic_freq, chan->ic_flags, restore, opmode, ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); #endif OS_MARK(ah, AH_MARK_ANI_RESET, opmode); /* * Turn off PHY error frame delivery while we futz with settings. */ rxfilter = ah->ah_getRxFilter(ah); ah->ah_setRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR); /* * If ANI is disabled at this point, don't set the default * ANI parameter settings - leave the HAL settings there. * This is (currently) needed for reliable radar detection. */ if (! ANI_ENA(ah)) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: ANI disabled\n", __func__); goto finish; } /* * Automatic processing is done only in station mode right now. */ if (opmode == HAL_M_STA) ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA; else ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA; /* * Set all ani parameters. We either set them to initial * values or restore the previous ones for the channel. * XXX if ANI follows hardware, we don't care what mode we're * XXX in, we should keep the ani parameters */ if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) { ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel); ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, aniState->spurImmunityLevel); ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, !aniState->ofdmWeakSigDetectOff); ar5212AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, aniState->cckWeakSigThreshold); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel); } else { ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0); ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_TRUE); ar5212AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); ichan->privFlags |= CHANNEL_ANI_SETUP; } /* * In case the counters haven't yet been setup; set them up. */ enableAniMIBCounters(ah, ahp->ah_curani->params); ar5212AniRestart(ah, aniState); finish: /* restore RX filter mask */ ah->ah_setRxFilter(ah, rxfilter); } /* * Process a MIB interrupt. We may potentially be invoked because * any of the MIB counters overflow/trigger so don't assume we're * here because a PHY error counter triggered. */ void ar5212ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats) { struct ath_hal_5212 *ahp = AH5212(ah); uint32_t phyCnt1, phyCnt2; HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x " "filtofdm 0x%x filtcck 0x%x\n", __func__, OS_REG_READ(ah, AR_MIBC), OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2), OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK)); /* * First order of business is to clear whatever caused * the interrupt so we don't keep getting interrupted. * We have the usual mib counters that are reset-on-read * and the additional counters that appeared starting in * Hainan. We collect the mib counters and explicitly * zero additional counters we are not using. Anything * else is reset only if it caused the interrupt. */ /* NB: these are not reset-on-read */ phyCnt1 = OS_REG_READ(ah, AR_PHYCNT1); phyCnt2 = OS_REG_READ(ah, AR_PHYCNT2); /* not used, always reset them in case they are the cause */ OS_REG_WRITE(ah, AR_FILTOFDM, 0); OS_REG_WRITE(ah, AR_FILTCCK, 0); /* Clear the mib counters and save them in the stats */ ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); ahp->ah_stats.ast_nodestats = *stats; /* * Check for an ani stat hitting the trigger threshold. * When this happens we get a MIB interrupt and the top * 2 bits of the counter register will be 0b11, hence * the mask check of phyCnt?. */ if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { struct ar5212AniState *aniState = ahp->ah_curani; const struct ar5212AniParams *params = aniState->params; uint32_t ofdmPhyErrCnt, cckPhyErrCnt; ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; ahp->ah_stats.ast_ani_ofdmerrs += ofdmPhyErrCnt - aniState->ofdmPhyErrCount; aniState->ofdmPhyErrCount = ofdmPhyErrCnt; cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; ahp->ah_stats.ast_ani_cckerrs += cckPhyErrCnt - aniState->cckPhyErrCount; aniState->cckPhyErrCount = cckPhyErrCnt; /* * NB: figure out which counter triggered. If both * trigger we'll only deal with one as the processing * clobbers the error counter so the trigger threshold * check will never be true. */ if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) ar5212AniOfdmErrTrigger(ah); if (aniState->cckPhyErrCount > params->cckTrigHigh) ar5212AniCckErrTrigger(ah); /* NB: always restart to insure the h/w counters are reset */ ar5212AniRestart(ah, aniState); } } void ar5212AniPhyErrReport(struct ath_hal *ah, const struct ath_rx_status *rs) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212AniState *aniState; const struct ar5212AniParams *params; HALASSERT(!ahp->ah_hasHwPhyCounters && rs != AH_NULL); aniState = ahp->ah_curani; params = aniState->params; if (rs->rs_phyerr == HAL_PHYERR_OFDM_TIMING) { aniState->ofdmPhyErrCount++; ahp->ah_stats.ast_ani_ofdmerrs++; if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) { ar5212AniOfdmErrTrigger(ah); ar5212AniRestart(ah, aniState); } } else if (rs->rs_phyerr == HAL_PHYERR_CCK_TIMING) { aniState->cckPhyErrCount++; ahp->ah_stats.ast_ani_cckerrs++; if (aniState->cckPhyErrCount > params->cckTrigHigh) { ar5212AniCckErrTrigger(ah); ar5212AniRestart(ah, aniState); } } } static void ar5212AniLowerImmunity(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212AniState *aniState; const struct ar5212AniParams *params; HALASSERT(ANI_ENA(ah)); aniState = ahp->ah_curani; params = aniState->params; if (ANI_ENA_RSSI(ah)) { int32_t rssi = BEACON_RSSI(ahp); if (rssi > params->rssiThrHigh) { /* * Beacon signal is high, leave ofdm weak signal * detection off or it may oscillate. Let it fall * through. */ } else if (rssi > params->rssiThrLow) { /* * Beacon rssi in mid range, turn on ofdm weak signal * detection or lower firstep level. */ if (aniState->ofdmWeakSigDetectOff) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d OWSD on\n", __func__, rssi); ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_TRUE); return; } if (aniState->firstepLevel > 0) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d lower ST %u\n", __func__, rssi, aniState->firstepLevel-1); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel - 1); return; } } else { /* * Beacon rssi is low, reduce firstep level. */ if (aniState->firstepLevel > 0) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: rssi %d lower ST %u\n", __func__, rssi, aniState->firstepLevel-1); ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel - 1); return; } } } /* then lower spur immunity level, down to zero */ if (aniState->spurImmunityLevel > 0) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower SI %u\n", __func__, aniState->spurImmunityLevel-1); ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, aniState->spurImmunityLevel - 1); return; } /* * if all else fails, lower noise immunity level down to a min value * zero for now */ if (aniState->noiseImmunityLevel > 0) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower NI %u\n", __func__, aniState->noiseImmunityLevel-1); ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel - 1); return; } } #define CLOCK_RATE 44000 /* XXX use mac_usec or similar */ /* convert HW counter values to ms using 11g clock rate, goo9d enough for 11a and Turbo */ /* * Return an approximation of the time spent ``listening'' by * deducting the cycles spent tx'ing and rx'ing from the total * cycle count since our last call. A return value <0 indicates * an invalid/inconsistent time. */ static int32_t ar5212AniGetListenTime(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212AniState *aniState = NULL; int32_t listenTime = 0; int good; HAL_SURVEY_SAMPLE hs; - HAL_CHANNEL_SURVEY *cs = AH_NULL; /* * We shouldn't see ah_curchan be NULL, but just in case.. */ if (AH_PRIVATE(ah)->ah_curchan == AH_NULL) { ath_hal_printf(ah, "%s: ah_curchan = NULL?\n", __func__); return (0); } - cs = &ahp->ah_chansurvey; - /* * Fetch the current statistics, squirrel away the current * sample, bump the sequence/sample counter. */ OS_MEMZERO(&hs, sizeof(hs)); good = ar5212GetMibCycleCounts(ah, &hs); - if (cs != AH_NULL) { - OS_MEMCPY(&cs->samples[cs->cur_sample], &hs, sizeof(hs)); - cs->samples[cs->cur_sample].seq_num = cs->cur_seq; - cs->cur_sample = - (cs->cur_sample + 1) % CHANNEL_SURVEY_SAMPLE_COUNT; - cs->cur_seq++; - } + ath_hal_survey_add_sample(ah, &hs); if (ANI_ENA(ah)) aniState = ahp->ah_curani; if (good == AH_FALSE) { /* * Cycle counter wrap (or initial call); it's not possible * to accurately calculate a value because the registers * right shift rather than wrap--so punt and return 0. */ listenTime = 0; ahp->ah_stats.ast_ani_lzero++; } else if (ANI_ENA(ah)) { /* * Only calculate and update the cycle count if we have * an ANI state. */ int32_t ccdelta = AH5212(ah)->ah_cycleCount - aniState->cycleCount; int32_t rfdelta = AH5212(ah)->ah_rxBusy - aniState->rxFrameCount; int32_t tfdelta = AH5212(ah)->ah_txBusy - aniState->txFrameCount; listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE; } /* * Again, only update ANI state if we have it. */ if (ANI_ENA(ah)) { aniState->cycleCount = AH5212(ah)->ah_cycleCount; aniState->rxFrameCount = AH5212(ah)->ah_rxBusy; aniState->txFrameCount = AH5212(ah)->ah_txBusy; } return listenTime; } /* * Update ani stats in preparation for listen time processing. */ static void updateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState) { struct ath_hal_5212 *ahp = AH5212(ah); const struct ar5212AniParams *params = aniState->params; uint32_t phyCnt1, phyCnt2; int32_t ofdmPhyErrCnt, cckPhyErrCnt; HALASSERT(ahp->ah_hasHwPhyCounters); /* Clear the mib counters and save them in the stats */ ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* NB: these are not reset-on-read */ phyCnt1 = OS_REG_READ(ah, AR_PHYCNT1); phyCnt2 = OS_REG_READ(ah, AR_PHYCNT2); /* NB: these are spec'd to never roll-over */ ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; if (ofdmPhyErrCnt < 0) { HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n", ofdmPhyErrCnt, phyCnt1); ofdmPhyErrCnt = AR_PHY_COUNTMAX; } ahp->ah_stats.ast_ani_ofdmerrs += ofdmPhyErrCnt - aniState->ofdmPhyErrCount; aniState->ofdmPhyErrCount = ofdmPhyErrCnt; cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; if (cckPhyErrCnt < 0) { HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n", cckPhyErrCnt, phyCnt2); cckPhyErrCnt = AR_PHY_COUNTMAX; } ahp->ah_stats.ast_ani_cckerrs += cckPhyErrCnt - aniState->cckPhyErrCount; aniState->cckPhyErrCount = cckPhyErrCnt; } void ar5212RxMonitor(struct ath_hal *ah, const HAL_NODE_STATS *stats, const struct ieee80211_channel *chan) { struct ath_hal_5212 *ahp = AH5212(ah); ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi; } /* * Do periodic processing. This routine is called from the * driver's rx interrupt handler after processing frames. */ void ar5212AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212AniState *aniState = ahp->ah_curani; const struct ar5212AniParams *params; int32_t listenTime; /* Always update from the MIB, for statistics gathering */ listenTime = ar5212AniGetListenTime(ah); /* XXX can aniState be null? */ if (aniState == AH_NULL) return; if (!ANI_ENA(ah)) return; if (listenTime < 0) { ahp->ah_stats.ast_ani_lneg++; /* restart ANI period if listenTime is invalid */ ar5212AniRestart(ah, aniState); } /* XXX beware of overflow? */ aniState->listenTime += listenTime; OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime); params = aniState->params; if (aniState->listenTime > 5*params->period) { /* * Check to see if need to lower immunity if * 5 aniPeriods have passed */ if (ahp->ah_hasHwPhyCounters) updateMIBStats(ah, aniState); if (aniState->ofdmPhyErrCount <= aniState->listenTime * params->ofdmTrigLow/1000 && aniState->cckPhyErrCount <= aniState->listenTime * params->cckTrigLow/1000) ar5212AniLowerImmunity(ah); ar5212AniRestart(ah, aniState); } else if (aniState->listenTime > params->period) { if (ahp->ah_hasHwPhyCounters) updateMIBStats(ah, aniState); /* check to see if need to raise immunity */ if (aniState->ofdmPhyErrCount > aniState->listenTime * params->ofdmTrigHigh / 1000) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: OFDM err %u listenTime %u\n", __func__, aniState->ofdmPhyErrCount, aniState->listenTime); ar5212AniOfdmErrTrigger(ah); ar5212AniRestart(ah, aniState); } else if (aniState->cckPhyErrCount > aniState->listenTime * params->cckTrigHigh / 1000) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: CCK err %u listenTime %u\n", __func__, aniState->cckPhyErrCount, aniState->listenTime); ar5212AniCckErrTrigger(ah); ar5212AniRestart(ah, aniState); } } } Index: head/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c (revision 280827) +++ head/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c (revision 280828) @@ -1,1455 +1,1451 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_desc.h" /* NB: for HAL_PHYERR* */ #include "ar5212/ar5212.h" #include "ar5212/ar5212reg.h" #include "ar5212/ar5212phy.h" #include "ah_eeprom_v3.h" #define AR_NUM_GPIO 6 /* 6 GPIO pins */ #define AR_GPIOD_MASK 0x0000002F /* GPIO data reg r/w mask */ void ar5212GetMacAddress(struct ath_hal *ah, uint8_t *mac) { struct ath_hal_5212 *ahp = AH5212(ah); OS_MEMCPY(mac, ahp->ah_macaddr, IEEE80211_ADDR_LEN); } HAL_BOOL ar5212SetMacAddress(struct ath_hal *ah, const uint8_t *mac) { struct ath_hal_5212 *ahp = AH5212(ah); OS_MEMCPY(ahp->ah_macaddr, mac, IEEE80211_ADDR_LEN); return AH_TRUE; } void ar5212GetBssIdMask(struct ath_hal *ah, uint8_t *mask) { struct ath_hal_5212 *ahp = AH5212(ah); OS_MEMCPY(mask, ahp->ah_bssidmask, IEEE80211_ADDR_LEN); } HAL_BOOL ar5212SetBssIdMask(struct ath_hal *ah, const uint8_t *mask) { struct ath_hal_5212 *ahp = AH5212(ah); /* save it since it must be rewritten on reset */ OS_MEMCPY(ahp->ah_bssidmask, mask, IEEE80211_ADDR_LEN); OS_REG_WRITE(ah, AR_BSSMSKL, LE_READ_4(ahp->ah_bssidmask)); OS_REG_WRITE(ah, AR_BSSMSKU, LE_READ_2(ahp->ah_bssidmask + 4)); return AH_TRUE; } /* * Attempt to change the cards operating regulatory domain to the given value */ HAL_BOOL ar5212SetRegulatoryDomain(struct ath_hal *ah, uint16_t regDomain, HAL_STATUS *status) { HAL_STATUS ecode; if (AH_PRIVATE(ah)->ah_currentRD == regDomain) { ecode = HAL_EINVAL; goto bad; } if (ath_hal_eepromGetFlag(ah, AR_EEP_WRITEPROTECT)) { ecode = HAL_EEWRITE; goto bad; } #ifdef AH_SUPPORT_WRITE_REGDOMAIN if (ath_hal_eepromWrite(ah, AR_EEPROM_REG_DOMAIN, regDomain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: set regulatory domain to %u (0x%x)\n", __func__, regDomain, regDomain); AH_PRIVATE(ah)->ah_currentRD = regDomain; return AH_TRUE; } #endif ecode = HAL_EIO; bad: if (status) *status = ecode; return AH_FALSE; } /* * Return the wireless modes (a,b,g,t) supported by hardware. * * This value is what is actually supported by the hardware * and is unaffected by regulatory/country code settings. */ u_int ar5212GetWirelessModes(struct ath_hal *ah) { u_int mode = 0; if (ath_hal_eepromGetFlag(ah, AR_EEP_AMODE)) { mode = HAL_MODE_11A; if (!ath_hal_eepromGetFlag(ah, AR_EEP_TURBO5DISABLE)) mode |= HAL_MODE_TURBO | HAL_MODE_108A; if (AH_PRIVATE(ah)->ah_caps.halChanHalfRate) mode |= HAL_MODE_11A_HALF_RATE; if (AH_PRIVATE(ah)->ah_caps.halChanQuarterRate) mode |= HAL_MODE_11A_QUARTER_RATE; } if (ath_hal_eepromGetFlag(ah, AR_EEP_BMODE)) mode |= HAL_MODE_11B; if (ath_hal_eepromGetFlag(ah, AR_EEP_GMODE) && AH_PRIVATE(ah)->ah_subvendorid != AR_SUBVENDOR_ID_NOG) { mode |= HAL_MODE_11G; if (!ath_hal_eepromGetFlag(ah, AR_EEP_TURBO2DISABLE)) mode |= HAL_MODE_108G; if (AH_PRIVATE(ah)->ah_caps.halChanHalfRate) mode |= HAL_MODE_11G_HALF_RATE; if (AH_PRIVATE(ah)->ah_caps.halChanQuarterRate) mode |= HAL_MODE_11G_QUARTER_RATE; } return mode; } /* * Set the interrupt and GPIO values so the ISR can disable RF * on a switch signal. Assumes GPIO port and interrupt polarity * are set prior to call. */ void ar5212EnableRfKill(struct ath_hal *ah) { uint16_t rfsilent = AH_PRIVATE(ah)->ah_rfsilent; int select = MS(rfsilent, AR_EEPROM_RFSILENT_GPIO_SEL); int polarity = MS(rfsilent, AR_EEPROM_RFSILENT_POLARITY); /* * Configure the desired GPIO port for input * and enable baseband rf silence. */ ath_hal_gpioCfgInput(ah, select); OS_REG_SET_BIT(ah, AR_PHY(0), 0x00002000); /* * If radio disable switch connection to GPIO bit x is enabled * program GPIO interrupt. * If rfkill bit on eeprom is 1, setupeeprommap routine has already * verified that it is a later version of eeprom, it has a place for * rfkill bit and it is set to 1, indicating that GPIO bit x hardware * connection is present. */ ath_hal_gpioSetIntr(ah, select, (ath_hal_gpioGet(ah, select) == polarity ? !polarity : polarity)); } /* * Change the LED blinking pattern to correspond to the connectivity */ void ar5212SetLedState(struct ath_hal *ah, HAL_LED_STATE state) { static const uint32_t ledbits[8] = { AR_PCICFG_LEDCTL_NONE, /* HAL_LED_INIT */ AR_PCICFG_LEDCTL_PEND, /* HAL_LED_SCAN */ AR_PCICFG_LEDCTL_PEND, /* HAL_LED_AUTH */ AR_PCICFG_LEDCTL_ASSOC, /* HAL_LED_ASSOC*/ AR_PCICFG_LEDCTL_ASSOC, /* HAL_LED_RUN */ AR_PCICFG_LEDCTL_NONE, AR_PCICFG_LEDCTL_NONE, AR_PCICFG_LEDCTL_NONE, }; uint32_t bits; bits = OS_REG_READ(ah, AR_PCICFG); if (IS_2417(ah)) { /* * Enable LED for Nala. There is a bit marked reserved * that must be set and we also turn on the power led. * Because we mark s/w LED control setting the control * status bits below is meangless (the driver must flash * the LED(s) using the GPIO lines). */ bits = (bits &~ AR_PCICFG_LEDMODE) | SM(AR_PCICFG_LEDMODE_POWON, AR_PCICFG_LEDMODE) #if 0 | SM(AR_PCICFG_LEDMODE_NETON, AR_PCICFG_LEDMODE) #endif | 0x08000000; } bits = (bits &~ AR_PCICFG_LEDCTL) | SM(ledbits[state & 0x7], AR_PCICFG_LEDCTL); OS_REG_WRITE(ah, AR_PCICFG, bits); } /* * Change association related fields programmed into the hardware. * Writing a valid BSSID to the hardware effectively enables the hardware * to synchronize its TSF to the correct beacons and receive frames coming * from that BSSID. It is called by the SME JOIN operation. */ void ar5212WriteAssocid(struct ath_hal *ah, const uint8_t *bssid, uint16_t assocId) { struct ath_hal_5212 *ahp = AH5212(ah); /* save bssid for possible re-use on reset */ OS_MEMCPY(ahp->ah_bssid, bssid, IEEE80211_ADDR_LEN); ahp->ah_assocId = assocId; OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid+4) | ((assocId & 0x3fff)<> 32) & 0xffffffff); } /* * Reset the current hardware tsf for stamlme. */ void ar5212ResetTsf(struct ath_hal *ah) { uint32_t val = OS_REG_READ(ah, AR_BEACON); OS_REG_WRITE(ah, AR_BEACON, val | AR_BEACON_RESET_TSF); /* * When resetting the TSF, write twice to the * corresponding register; each write to the RESET_TSF bit toggles * the internal signal to cause a reset of the TSF - but if the signal * is left high, it will reset the TSF on the next chip reset also! * writing the bit an even number of times fixes this issue */ OS_REG_WRITE(ah, AR_BEACON, val | AR_BEACON_RESET_TSF); } /* * Set or clear hardware basic rate bit * Set hardware basic rate set if basic rate is found * and basic rate is equal or less than 2Mbps */ void ar5212SetBasicRate(struct ath_hal *ah, HAL_RATE_SET *rs) { const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; uint32_t reg; uint8_t xset; int i; if (chan == AH_NULL || !IEEE80211_IS_CHAN_CCK(chan)) return; xset = 0; for (i = 0; i < rs->rs_count; i++) { uint8_t rset = rs->rs_rates[i]; /* Basic rate defined? */ if ((rset & 0x80) && (rset &= 0x7f) >= xset) xset = rset; } /* * Set the h/w bit to reflect whether or not the basic * rate is found to be equal or less than 2Mbps. */ reg = OS_REG_READ(ah, AR_STA_ID1); if (xset && xset/2 <= 2) OS_REG_WRITE(ah, AR_STA_ID1, reg | AR_STA_ID1_BASE_RATE_11B); else OS_REG_WRITE(ah, AR_STA_ID1, reg &~ AR_STA_ID1_BASE_RATE_11B); } /* * Grab a semi-random value from hardware registers - may not * change often */ uint32_t ar5212GetRandomSeed(struct ath_hal *ah) { uint32_t nf; nf = (OS_REG_READ(ah, AR_PHY(25)) >> 19) & 0x1ff; if (nf & 0x100) nf = 0 - ((nf ^ 0x1ff) + 1); return (OS_REG_READ(ah, AR_TSF_U32) ^ OS_REG_READ(ah, AR_TSF_L32) ^ nf); } /* * Detect if our card is present */ HAL_BOOL ar5212DetectCardPresent(struct ath_hal *ah) { uint16_t macVersion, macRev; uint32_t v; /* * Read the Silicon Revision register and compare that * to what we read at attach time. If the same, we say * a card/device is present. */ v = OS_REG_READ(ah, AR_SREV) & AR_SREV_ID; macVersion = v >> AR_SREV_ID_S; macRev = v & AR_SREV_REVISION; return (AH_PRIVATE(ah)->ah_macVersion == macVersion && AH_PRIVATE(ah)->ah_macRev == macRev); } void ar5212EnableMibCounters(struct ath_hal *ah) { /* NB: this just resets the mib counter machinery */ OS_REG_WRITE(ah, AR_MIBC, ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS) & 0x0f); } void ar5212DisableMibCounters(struct ath_hal *ah) { OS_REG_WRITE(ah, AR_MIBC, AR_MIBC | AR_MIBC_CMC); } /* * Update MIB Counters */ void ar5212UpdateMibCounters(struct ath_hal *ah, HAL_MIB_STATS* stats) { stats->ackrcv_bad += OS_REG_READ(ah, AR_ACK_FAIL); stats->rts_bad += OS_REG_READ(ah, AR_RTS_FAIL); stats->fcs_bad += OS_REG_READ(ah, AR_FCS_FAIL); stats->rts_good += OS_REG_READ(ah, AR_RTS_OK); stats->beacons += OS_REG_READ(ah, AR_BEACON_CNT); } /* * Detect if the HW supports spreading a CCK signal on channel 14 */ HAL_BOOL ar5212IsJapanChannelSpreadSupported(struct ath_hal *ah) { return AH_TRUE; } /* * Get the rssi of frame curently being received. */ uint32_t ar5212GetCurRssi(struct ath_hal *ah) { return (OS_REG_READ(ah, AR_PHY_CURRENT_RSSI) & 0xff); } u_int ar5212GetDefAntenna(struct ath_hal *ah) { return (OS_REG_READ(ah, AR_DEF_ANTENNA) & 0x7); } void ar5212SetDefAntenna(struct ath_hal *ah, u_int antenna) { OS_REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7)); } HAL_ANT_SETTING ar5212GetAntennaSwitch(struct ath_hal *ah) { return AH5212(ah)->ah_antControl; } HAL_BOOL ar5212SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING setting) { struct ath_hal_5212 *ahp = AH5212(ah); const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; if (!ahp->ah_phyPowerOn || chan == AH_NULL) { /* PHY powered off, just stash settings */ ahp->ah_antControl = setting; ahp->ah_diversity = (setting == HAL_ANT_VARIABLE); return AH_TRUE; } return ar5212SetAntennaSwitchInternal(ah, setting, chan); } HAL_BOOL ar5212IsSleepAfterBeaconBroken(struct ath_hal *ah) { return AH_TRUE; } HAL_BOOL ar5212SetSifsTime(struct ath_hal *ah, u_int us) { struct ath_hal_5212 *ahp = AH5212(ah); if (us > ath_hal_mac_usec(ah, 0xffff)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad SIFS time %u\n", __func__, us); ahp->ah_sifstime = (u_int) -1; /* restore default handling */ return AH_FALSE; } else { /* convert to system clocks */ OS_REG_WRITE(ah, AR_D_GBL_IFS_SIFS, ath_hal_mac_clks(ah, us-2)); ahp->ah_sifstime = us; return AH_TRUE; } } u_int ar5212GetSifsTime(struct ath_hal *ah) { u_int clks = OS_REG_READ(ah, AR_D_GBL_IFS_SIFS) & 0xffff; return ath_hal_mac_usec(ah, clks)+2; /* convert from system clocks */ } HAL_BOOL ar5212SetSlotTime(struct ath_hal *ah, u_int us) { struct ath_hal_5212 *ahp = AH5212(ah); if (us < HAL_SLOT_TIME_6 || us > ath_hal_mac_usec(ah, 0xffff)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad slot time %u\n", __func__, us); ahp->ah_slottime = (u_int) -1; /* restore default handling */ return AH_FALSE; } else { /* convert to system clocks */ OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, ath_hal_mac_clks(ah, us)); ahp->ah_slottime = us; return AH_TRUE; } } u_int ar5212GetSlotTime(struct ath_hal *ah) { u_int clks = OS_REG_READ(ah, AR_D_GBL_IFS_SLOT) & 0xffff; return ath_hal_mac_usec(ah, clks); /* convert from system clocks */ } HAL_BOOL ar5212SetAckTimeout(struct ath_hal *ah, u_int us) { struct ath_hal_5212 *ahp = AH5212(ah); if (us > ath_hal_mac_usec(ah, MS(0xffffffff, AR_TIME_OUT_ACK))) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad ack timeout %u\n", __func__, us); ahp->ah_acktimeout = (u_int) -1; /* restore default handling */ return AH_FALSE; } else { /* convert to system clocks */ OS_REG_RMW_FIELD(ah, AR_TIME_OUT, AR_TIME_OUT_ACK, ath_hal_mac_clks(ah, us)); ahp->ah_acktimeout = us; return AH_TRUE; } } u_int ar5212GetAckTimeout(struct ath_hal *ah) { u_int clks = MS(OS_REG_READ(ah, AR_TIME_OUT), AR_TIME_OUT_ACK); return ath_hal_mac_usec(ah, clks); /* convert from system clocks */ } u_int ar5212GetAckCTSRate(struct ath_hal *ah) { return ((AH5212(ah)->ah_staId1Defaults & AR_STA_ID1_ACKCTS_6MB) == 0); } HAL_BOOL ar5212SetAckCTSRate(struct ath_hal *ah, u_int high) { struct ath_hal_5212 *ahp = AH5212(ah); if (high) { OS_REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_ACKCTS_6MB); ahp->ah_staId1Defaults &= ~AR_STA_ID1_ACKCTS_6MB; } else { OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_ACKCTS_6MB); ahp->ah_staId1Defaults |= AR_STA_ID1_ACKCTS_6MB; } return AH_TRUE; } HAL_BOOL ar5212SetCTSTimeout(struct ath_hal *ah, u_int us) { struct ath_hal_5212 *ahp = AH5212(ah); if (us > ath_hal_mac_usec(ah, MS(0xffffffff, AR_TIME_OUT_CTS))) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad cts timeout %u\n", __func__, us); ahp->ah_ctstimeout = (u_int) -1; /* restore default handling */ return AH_FALSE; } else { /* convert to system clocks */ OS_REG_RMW_FIELD(ah, AR_TIME_OUT, AR_TIME_OUT_CTS, ath_hal_mac_clks(ah, us)); ahp->ah_ctstimeout = us; return AH_TRUE; } } u_int ar5212GetCTSTimeout(struct ath_hal *ah) { u_int clks = MS(OS_REG_READ(ah, AR_TIME_OUT), AR_TIME_OUT_CTS); return ath_hal_mac_usec(ah, clks); /* convert from system clocks */ } /* Setup decompression for given key index */ HAL_BOOL ar5212SetDecompMask(struct ath_hal *ah, uint16_t keyidx, int en) { struct ath_hal_5212 *ahp = AH5212(ah); if (keyidx >= HAL_DECOMP_MASK_SIZE) return AH_FALSE; OS_REG_WRITE(ah, AR_DCM_A, keyidx); OS_REG_WRITE(ah, AR_DCM_D, en ? AR_DCM_D_EN : 0); ahp->ah_decompMask[keyidx] = en; return AH_TRUE; } /* Setup coverage class */ void ar5212SetCoverageClass(struct ath_hal *ah, uint8_t coverageclass, int now) { uint32_t slot, timeout, eifs; u_int clkRate; AH_PRIVATE(ah)->ah_coverageClass = coverageclass; if (now) { if (AH_PRIVATE(ah)->ah_coverageClass == 0) return; /* Don't apply coverage class to non A channels */ if (!IEEE80211_IS_CHAN_A(AH_PRIVATE(ah)->ah_curchan)) return; /* Get core clock rate */ clkRate = ath_hal_mac_clks(ah, 1); /* Compute EIFS */ slot = coverageclass * 3 * clkRate; eifs = coverageclass * 6 * clkRate; if (IEEE80211_IS_CHAN_HALF(AH_PRIVATE(ah)->ah_curchan)) { slot += IFS_SLOT_HALF_RATE; eifs += IFS_EIFS_HALF_RATE; } else if (IEEE80211_IS_CHAN_QUARTER(AH_PRIVATE(ah)->ah_curchan)) { slot += IFS_SLOT_QUARTER_RATE; eifs += IFS_EIFS_QUARTER_RATE; } else { /* full rate */ slot += IFS_SLOT_FULL_RATE; eifs += IFS_EIFS_FULL_RATE; } /* * Add additional time for air propagation for ACK and CTS * timeouts. This value is in core clocks. */ timeout = ACK_CTS_TIMEOUT_11A + (coverageclass * 3 * clkRate); /* * Write the values: slot, eifs, ack/cts timeouts. */ OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, slot); OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, eifs); OS_REG_WRITE(ah, AR_TIME_OUT, SM(timeout, AR_TIME_OUT_CTS) | SM(timeout, AR_TIME_OUT_ACK)); } } HAL_STATUS ar5212SetQuiet(struct ath_hal *ah, uint32_t period, uint32_t duration, uint32_t nextStart, HAL_QUIET_FLAG flag) { OS_REG_WRITE(ah, AR_QUIET2, period | (duration << AR_QUIET2_QUIET_DUR_S)); if (flag & HAL_QUIET_ENABLE) { OS_REG_WRITE(ah, AR_QUIET1, nextStart | (1 << 16)); } else { OS_REG_WRITE(ah, AR_QUIET1, nextStart); } return HAL_OK; } void ar5212SetPCUConfig(struct ath_hal *ah) { ar5212SetOperatingMode(ah, AH_PRIVATE(ah)->ah_opmode); } /* * Return whether an external 32KHz crystal should be used * to reduce power consumption when sleeping. We do so if * the crystal is present (obtained from EEPROM) and if we * are not running as an AP and are configured to use it. */ HAL_BOOL ar5212Use32KHzclock(struct ath_hal *ah, HAL_OPMODE opmode) { if (opmode != HAL_M_HOSTAP) { struct ath_hal_5212 *ahp = AH5212(ah); return ath_hal_eepromGetFlag(ah, AR_EEP_32KHZCRYSTAL) && (ahp->ah_enable32kHzClock == USE_32KHZ || ahp->ah_enable32kHzClock == AUTO_32KHZ); } else return AH_FALSE; } /* * If 32KHz clock exists, use it to lower power consumption during sleep * * Note: If clock is set to 32 KHz, delays on accessing certain * baseband registers (27-31, 124-127) are required. */ void ar5212SetupClock(struct ath_hal *ah, HAL_OPMODE opmode) { if (ar5212Use32KHzclock(ah, opmode)) { /* * Enable clocks to be turned OFF in BB during sleep * and also enable turning OFF 32MHz/40MHz Refclk * from A2. */ OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_CONTROL, 0x1f); OS_REG_WRITE(ah, AR_PHY_REFCLKPD, IS_RAD5112_ANY(ah) || IS_5413(ah) ? 0x14 : 0x18); OS_REG_RMW_FIELD(ah, AR_USEC, AR_USEC_USEC32, 1); OS_REG_WRITE(ah, AR_TSF_PARM, 61); /* 32 KHz TSF incr */ OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_SEL, 1); if (IS_2413(ah) || IS_5413(ah) || IS_2417(ah)) { OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_LIMIT, 0x26); OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x0d); OS_REG_WRITE(ah, AR_PHY_M_SLEEP, 0x07); OS_REG_WRITE(ah, AR_PHY_REFCLKDLY, 0x3f); /* # Set sleep clock rate to 32 KHz. */ OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_RATE_IND, 0x2); } else { OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_LIMIT, 0x0a); OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x0c); OS_REG_WRITE(ah, AR_PHY_M_SLEEP, 0x03); OS_REG_WRITE(ah, AR_PHY_REFCLKDLY, 0x20); OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_RATE_IND, 0x3); } } else { OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_RATE_IND, 0x0); OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_SEL, 0); OS_REG_WRITE(ah, AR_TSF_PARM, 1); /* 32MHz TSF inc */ OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_CONTROL, 0x1f); OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_LIMIT, 0x7f); if (IS_2417(ah)) OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x0a); else if (IS_HB63(ah)) OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x32); else OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x0e); OS_REG_WRITE(ah, AR_PHY_M_SLEEP, 0x0c); OS_REG_WRITE(ah, AR_PHY_REFCLKDLY, 0xff); OS_REG_WRITE(ah, AR_PHY_REFCLKPD, IS_RAD5112_ANY(ah) || IS_5413(ah) || IS_2417(ah) ? 0x14 : 0x18); OS_REG_RMW_FIELD(ah, AR_USEC, AR_USEC_USEC32, IS_RAD5112_ANY(ah) || IS_5413(ah) ? 39 : 31); } } /* * If 32KHz clock exists, turn it off and turn back on the 32Mhz */ void ar5212RestoreClock(struct ath_hal *ah, HAL_OPMODE opmode) { if (ar5212Use32KHzclock(ah, opmode)) { /* # Set sleep clock rate back to 32 MHz. */ OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_RATE_IND, 0); OS_REG_RMW_FIELD(ah, AR_PCICFG, AR_PCICFG_SCLK_SEL, 0); OS_REG_WRITE(ah, AR_TSF_PARM, 1); /* 32 MHz TSF incr */ OS_REG_RMW_FIELD(ah, AR_USEC, AR_USEC_USEC32, IS_RAD5112_ANY(ah) || IS_5413(ah) ? 39 : 31); /* * Restore BB registers to power-on defaults */ OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_CONTROL, 0x1f); OS_REG_WRITE(ah, AR_PHY_SLEEP_CTR_LIMIT, 0x7f); OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x0e); OS_REG_WRITE(ah, AR_PHY_M_SLEEP, 0x0c); OS_REG_WRITE(ah, AR_PHY_REFCLKDLY, 0xff); OS_REG_WRITE(ah, AR_PHY_REFCLKPD, IS_RAD5112_ANY(ah) || IS_5413(ah) ? 0x14 : 0x18); } } /* * Adjust NF based on statistical values for 5GHz frequencies. * Default method: this may be overridden by the rf backend. */ int16_t ar5212GetNfAdjust(struct ath_hal *ah, const HAL_CHANNEL_INTERNAL *c) { static const struct { uint16_t freqLow; int16_t adjust; } adjustDef[] = { { 5790, 11 }, /* NB: ordered high -> low */ { 5730, 10 }, { 5690, 9 }, { 5660, 8 }, { 5610, 7 }, { 5530, 5 }, { 5450, 4 }, { 5379, 2 }, { 5209, 0 }, { 3000, 1 }, { 0, 0 }, }; int i; for (i = 0; c->channel <= adjustDef[i].freqLow; i++) ; return adjustDef[i].adjust; } HAL_STATUS ar5212GetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t *result) { #define MACVERSION(ah) AH_PRIVATE(ah)->ah_macVersion struct ath_hal_5212 *ahp = AH5212(ah); const HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; const struct ar5212AniState *ani; switch (type) { case HAL_CAP_CIPHER: /* cipher handled in hardware */ switch (capability) { case HAL_CIPHER_AES_CCM: return pCap->halCipherAesCcmSupport ? HAL_OK : HAL_ENOTSUPP; case HAL_CIPHER_AES_OCB: case HAL_CIPHER_TKIP: case HAL_CIPHER_WEP: case HAL_CIPHER_MIC: case HAL_CIPHER_CLR: return HAL_OK; default: return HAL_ENOTSUPP; } case HAL_CAP_TKIP_MIC: /* handle TKIP MIC in hardware */ switch (capability) { case 0: /* hardware capability */ return HAL_OK; case 1: return (ahp->ah_staId1Defaults & AR_STA_ID1_CRPT_MIC_ENABLE) ? HAL_OK : HAL_ENXIO; } return HAL_EINVAL; case HAL_CAP_TKIP_SPLIT: /* hardware TKIP uses split keys */ switch (capability) { case 0: /* hardware capability */ return pCap->halTkipMicTxRxKeySupport ? HAL_ENXIO : HAL_OK; case 1: /* current setting */ return (ahp->ah_miscMode & AR_MISC_MODE_MIC_NEW_LOC_ENABLE) ? HAL_ENXIO : HAL_OK; } return HAL_EINVAL; case HAL_CAP_WME_TKIPMIC: /* hardware can do TKIP MIC w/ WMM */ /* XXX move to capability bit */ return MACVERSION(ah) > AR_SREV_VERSION_VENICE || (MACVERSION(ah) == AR_SREV_VERSION_VENICE && AH_PRIVATE(ah)->ah_macRev >= 8) ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_DIVERSITY: /* hardware supports fast diversity */ switch (capability) { case 0: /* hardware capability */ return HAL_OK; case 1: /* current setting */ return ahp->ah_diversity ? HAL_OK : HAL_ENXIO; case HAL_CAP_STRONG_DIV: *result = OS_REG_READ(ah, AR_PHY_RESTART); *result = MS(*result, AR_PHY_RESTART_DIV_GC); return HAL_OK; } return HAL_EINVAL; case HAL_CAP_DIAG: *result = AH_PRIVATE(ah)->ah_diagreg; return HAL_OK; case HAL_CAP_TPC: switch (capability) { case 0: /* hardware capability */ return HAL_OK; case 1: return ahp->ah_tpcEnabled ? HAL_OK : HAL_ENXIO; } return HAL_OK; case HAL_CAP_PHYDIAG: /* radar pulse detection capability */ switch (capability) { case HAL_CAP_RADAR: return ath_hal_eepromGetFlag(ah, AR_EEP_AMODE) ? HAL_OK: HAL_ENXIO; case HAL_CAP_AR: return (ath_hal_eepromGetFlag(ah, AR_EEP_GMODE) || ath_hal_eepromGetFlag(ah, AR_EEP_BMODE)) ? HAL_OK: HAL_ENXIO; } return HAL_ENXIO; case HAL_CAP_MCAST_KEYSRCH: /* multicast frame keycache search */ switch (capability) { case 0: /* hardware capability */ return pCap->halMcastKeySrchSupport ? HAL_OK : HAL_ENXIO; case 1: return (ahp->ah_staId1Defaults & AR_STA_ID1_MCAST_KSRCH) ? HAL_OK : HAL_ENXIO; } return HAL_EINVAL; case HAL_CAP_TSF_ADJUST: /* hardware has beacon tsf adjust */ switch (capability) { case 0: /* hardware capability */ return pCap->halTsfAddSupport ? HAL_OK : HAL_ENOTSUPP; case 1: return (ahp->ah_miscMode & AR_MISC_MODE_TX_ADD_TSF) ? HAL_OK : HAL_ENXIO; } return HAL_EINVAL; case HAL_CAP_TPC_ACK: *result = MS(ahp->ah_macTPC, AR_TPC_ACK); return HAL_OK; case HAL_CAP_TPC_CTS: *result = MS(ahp->ah_macTPC, AR_TPC_CTS); return HAL_OK; case HAL_CAP_INTMIT: /* interference mitigation */ switch (capability) { case HAL_CAP_INTMIT_PRESENT: /* hardware capability */ return HAL_OK; case HAL_CAP_INTMIT_ENABLE: return (ahp->ah_procPhyErr & HAL_ANI_ENA) ? HAL_OK : HAL_ENXIO; case HAL_CAP_INTMIT_NOISE_IMMUNITY_LEVEL: case HAL_CAP_INTMIT_OFDM_WEAK_SIGNAL_LEVEL: case HAL_CAP_INTMIT_CCK_WEAK_SIGNAL_THR: case HAL_CAP_INTMIT_FIRSTEP_LEVEL: case HAL_CAP_INTMIT_SPUR_IMMUNITY_LEVEL: ani = ar5212AniGetCurrentState(ah); if (ani == AH_NULL) return HAL_ENXIO; switch (capability) { case 2: *result = ani->noiseImmunityLevel; break; case 3: *result = !ani->ofdmWeakSigDetectOff; break; case 4: *result = ani->cckWeakSigThreshold; break; case 5: *result = ani->firstepLevel; break; case 6: *result = ani->spurImmunityLevel; break; } return HAL_OK; } return HAL_EINVAL; default: return ath_hal_getcapability(ah, type, capability, result); } #undef MACVERSION } HAL_BOOL ar5212SetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, uint32_t capability, uint32_t setting, HAL_STATUS *status) { #define N(a) (sizeof(a)/sizeof(a[0])) struct ath_hal_5212 *ahp = AH5212(ah); const HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; uint32_t v; switch (type) { case HAL_CAP_TKIP_MIC: /* handle TKIP MIC in hardware */ if (setting) ahp->ah_staId1Defaults |= AR_STA_ID1_CRPT_MIC_ENABLE; else ahp->ah_staId1Defaults &= ~AR_STA_ID1_CRPT_MIC_ENABLE; return AH_TRUE; case HAL_CAP_TKIP_SPLIT: /* hardware TKIP uses split keys */ if (!pCap->halTkipMicTxRxKeySupport) return AH_FALSE; /* NB: true =>'s use split key cache layout */ if (setting) ahp->ah_miscMode &= ~AR_MISC_MODE_MIC_NEW_LOC_ENABLE; else ahp->ah_miscMode |= AR_MISC_MODE_MIC_NEW_LOC_ENABLE; /* NB: write here so keys can be setup w/o a reset */ OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode); return AH_TRUE; case HAL_CAP_DIVERSITY: switch (capability) { case 0: return AH_FALSE; case 1: /* setting */ if (ahp->ah_phyPowerOn) { if (capability == HAL_CAP_STRONG_DIV) { v = OS_REG_READ(ah, AR_PHY_CCK_DETECT); if (setting) v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; else v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, v); } } ahp->ah_diversity = (setting != 0); return AH_TRUE; case HAL_CAP_STRONG_DIV: if (! ahp->ah_phyPowerOn) return AH_FALSE; v = OS_REG_READ(ah, AR_PHY_RESTART); v &= ~AR_PHY_RESTART_DIV_GC; v |= SM(setting, AR_PHY_RESTART_DIV_GC); OS_REG_WRITE(ah, AR_PHY_RESTART, v); return AH_TRUE; default: return AH_FALSE; } case HAL_CAP_DIAG: /* hardware diagnostic support */ /* * NB: could split this up into virtual capabilities, * (e.g. 1 => ACK, 2 => CTS, etc.) but it hardly * seems worth the additional complexity. */ AH_PRIVATE(ah)->ah_diagreg = setting; OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg); return AH_TRUE; case HAL_CAP_TPC: ahp->ah_tpcEnabled = (setting != 0); return AH_TRUE; case HAL_CAP_MCAST_KEYSRCH: /* multicast frame keycache search */ if (setting) ahp->ah_staId1Defaults |= AR_STA_ID1_MCAST_KSRCH; else ahp->ah_staId1Defaults &= ~AR_STA_ID1_MCAST_KSRCH; return AH_TRUE; case HAL_CAP_TPC_ACK: case HAL_CAP_TPC_CTS: setting += ahp->ah_txPowerIndexOffset; if (setting > 63) setting = 63; if (type == HAL_CAP_TPC_ACK) { ahp->ah_macTPC &= AR_TPC_ACK; ahp->ah_macTPC |= MS(setting, AR_TPC_ACK); } else { ahp->ah_macTPC &= AR_TPC_CTS; ahp->ah_macTPC |= MS(setting, AR_TPC_CTS); } OS_REG_WRITE(ah, AR_TPC, ahp->ah_macTPC); return AH_TRUE; case HAL_CAP_INTMIT: { /* interference mitigation */ /* This maps the public ANI commands to the internal ANI commands */ /* Private: HAL_ANI_CMD; Public: HAL_CAP_INTMIT_CMD */ static const HAL_ANI_CMD cmds[] = { HAL_ANI_PRESENT, HAL_ANI_MODE, HAL_ANI_NOISE_IMMUNITY_LEVEL, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, HAL_ANI_CCK_WEAK_SIGNAL_THR, HAL_ANI_FIRSTEP_LEVEL, HAL_ANI_SPUR_IMMUNITY_LEVEL, }; return capability < N(cmds) ? AH5212(ah)->ah_aniControl(ah, cmds[capability], setting) : AH_FALSE; } case HAL_CAP_TSF_ADJUST: /* hardware has beacon tsf adjust */ if (pCap->halTsfAddSupport) { if (setting) ahp->ah_miscMode |= AR_MISC_MODE_TX_ADD_TSF; else ahp->ah_miscMode &= ~AR_MISC_MODE_TX_ADD_TSF; return AH_TRUE; } /* fall thru... */ default: return ath_hal_setcapability(ah, type, capability, setting, status); } #undef N } HAL_BOOL ar5212GetDiagState(struct ath_hal *ah, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize) { struct ath_hal_5212 *ahp = AH5212(ah); (void) ahp; if (ath_hal_getdiagstate(ah, request, args, argsize, result, resultsize)) return AH_TRUE; switch (request) { case HAL_DIAG_EEPROM: case HAL_DIAG_EEPROM_EXP_11A: case HAL_DIAG_EEPROM_EXP_11B: case HAL_DIAG_EEPROM_EXP_11G: case HAL_DIAG_RFGAIN: return ath_hal_eepromDiag(ah, request, args, argsize, result, resultsize); case HAL_DIAG_RFGAIN_CURSTEP: *result = __DECONST(void *, ahp->ah_gainValues.currStep); *resultsize = (*result == AH_NULL) ? 0 : sizeof(GAIN_OPTIMIZATION_STEP); return AH_TRUE; case HAL_DIAG_PCDAC: *result = ahp->ah_pcdacTable; *resultsize = ahp->ah_pcdacTableSize; return AH_TRUE; case HAL_DIAG_TXRATES: *result = &ahp->ah_ratesArray[0]; *resultsize = sizeof(ahp->ah_ratesArray); return AH_TRUE; case HAL_DIAG_ANI_CURRENT: *result = ar5212AniGetCurrentState(ah); *resultsize = (*result == AH_NULL) ? 0 : sizeof(struct ar5212AniState); return AH_TRUE; case HAL_DIAG_ANI_STATS: *result = ar5212AniGetCurrentStats(ah); *resultsize = (*result == AH_NULL) ? 0 : sizeof(struct ar5212Stats); return AH_TRUE; case HAL_DIAG_ANI_CMD: if (argsize != 2*sizeof(uint32_t)) return AH_FALSE; AH5212(ah)->ah_aniControl(ah, ((const uint32_t *)args)[0], ((const uint32_t *)args)[1]); return AH_TRUE; case HAL_DIAG_ANI_PARAMS: /* * NB: We assume struct ar5212AniParams is identical * to HAL_ANI_PARAMS; if they diverge then we'll need * to handle it here */ if (argsize == 0 && args == AH_NULL) { struct ar5212AniState *aniState = ar5212AniGetCurrentState(ah); if (aniState == AH_NULL) return AH_FALSE; *result = __DECONST(void *, aniState->params); *resultsize = sizeof(struct ar5212AniParams); return AH_TRUE; } else { if (argsize != sizeof(struct ar5212AniParams)) return AH_FALSE; return ar5212AniSetParams(ah, args, args); } break; - case HAL_DIAG_CHANSURVEY: - *result = &ahp->ah_chansurvey; - *resultsize = sizeof(HAL_CHANNEL_SURVEY); - return AH_TRUE; } return AH_FALSE; } /* * Check whether there's an in-progress NF completion. * * Returns AH_TRUE if there's a in-progress NF calibration, AH_FALSE * otherwise. */ HAL_BOOL ar5212IsNFCalInProgress(struct ath_hal *ah) { if (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) return AH_TRUE; return AH_FALSE; } /* * Wait for an in-progress NF calibration to complete. * * The completion function waits "i" times 10uS. * It returns AH_TRUE if the NF calibration completed (or was never * in progress); AH_FALSE if it was still in progress after "i" checks. */ HAL_BOOL ar5212WaitNFCalComplete(struct ath_hal *ah, int i) { int j; if (i <= 0) i = 1; /* it should run at least once */ for (j = 0; j < i; j++) { if (! ar5212IsNFCalInProgress(ah)) return AH_TRUE; OS_DELAY(10); } return AH_FALSE; } void ar5212EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) { uint32_t val; val = OS_REG_READ(ah, AR_PHY_RADAR_0); if (pe->pe_firpwr != HAL_PHYERR_PARAM_NOVAL) { val &= ~AR_PHY_RADAR_0_FIRPWR; val |= SM(pe->pe_firpwr, AR_PHY_RADAR_0_FIRPWR); } if (pe->pe_rrssi != HAL_PHYERR_PARAM_NOVAL) { val &= ~AR_PHY_RADAR_0_RRSSI; val |= SM(pe->pe_rrssi, AR_PHY_RADAR_0_RRSSI); } if (pe->pe_height != HAL_PHYERR_PARAM_NOVAL) { val &= ~AR_PHY_RADAR_0_HEIGHT; val |= SM(pe->pe_height, AR_PHY_RADAR_0_HEIGHT); } if (pe->pe_prssi != HAL_PHYERR_PARAM_NOVAL) { val &= ~AR_PHY_RADAR_0_PRSSI; val |= SM(pe->pe_prssi, AR_PHY_RADAR_0_PRSSI); } if (pe->pe_inband != HAL_PHYERR_PARAM_NOVAL) { val &= ~AR_PHY_RADAR_0_INBAND; val |= SM(pe->pe_inband, AR_PHY_RADAR_0_INBAND); } if (pe->pe_enabled) val |= AR_PHY_RADAR_0_ENA; else val &= ~ AR_PHY_RADAR_0_ENA; if (IS_5413(ah)) { if (pe->pe_blockradar == 1) OS_REG_SET_BIT(ah, AR_PHY_RADAR_2, AR_PHY_RADAR_2_BLOCKOFDMWEAK); else OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2, AR_PHY_RADAR_2_BLOCKOFDMWEAK); if (pe->pe_en_relstep_check == 1) OS_REG_SET_BIT(ah, AR_PHY_RADAR_2, AR_PHY_RADAR_2_ENRELSTEPCHK); else OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2, AR_PHY_RADAR_2_ENRELSTEPCHK); if (pe->pe_usefir128 == 1) OS_REG_SET_BIT(ah, AR_PHY_RADAR_2, AR_PHY_RADAR_2_USEFIR128); else OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2, AR_PHY_RADAR_2_USEFIR128); if (pe->pe_enmaxrssi == 1) OS_REG_SET_BIT(ah, AR_PHY_RADAR_2, AR_PHY_RADAR_2_ENMAXRSSI); else OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2, AR_PHY_RADAR_2_ENMAXRSSI); if (pe->pe_enrelpwr == 1) OS_REG_SET_BIT(ah, AR_PHY_RADAR_2, AR_PHY_RADAR_2_ENRELPWRCHK); else OS_REG_CLR_BIT(ah, AR_PHY_RADAR_2, AR_PHY_RADAR_2_ENRELPWRCHK); if (pe->pe_relpwr != HAL_PHYERR_PARAM_NOVAL) OS_REG_RMW_FIELD(ah, AR_PHY_RADAR_2, AR_PHY_RADAR_2_RELPWR, pe->pe_relpwr); if (pe->pe_relstep != HAL_PHYERR_PARAM_NOVAL) OS_REG_RMW_FIELD(ah, AR_PHY_RADAR_2, AR_PHY_RADAR_2_RELSTEP, pe->pe_relstep); if (pe->pe_maxlen != HAL_PHYERR_PARAM_NOVAL) OS_REG_RMW_FIELD(ah, AR_PHY_RADAR_2, AR_PHY_RADAR_2_MAXLEN, pe->pe_maxlen); } OS_REG_WRITE(ah, AR_PHY_RADAR_0, val); } /* * Parameters for the AR5212 PHY. */ #define AR5212_DFS_FIRPWR -35 #define AR5212_DFS_RRSSI 20 #define AR5212_DFS_HEIGHT 14 #define AR5212_DFS_PRSSI 6 #define AR5212_DFS_INBAND 4 /* * Default parameters for the AR5413 PHY. */ #define AR5413_DFS_FIRPWR -34 #define AR5413_DFS_RRSSI 20 #define AR5413_DFS_HEIGHT 10 #define AR5413_DFS_PRSSI 15 #define AR5413_DFS_INBAND 6 #define AR5413_DFS_RELPWR 8 #define AR5413_DFS_RELSTEP 31 #define AR5413_DFS_MAXLEN 255 HAL_BOOL ar5212GetDfsDefaultThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) { if (IS_5413(ah)) { pe->pe_firpwr = AR5413_DFS_FIRPWR; pe->pe_rrssi = AR5413_DFS_RRSSI; pe->pe_height = AR5413_DFS_HEIGHT; pe->pe_prssi = AR5413_DFS_PRSSI; pe->pe_inband = AR5413_DFS_INBAND; pe->pe_relpwr = AR5413_DFS_RELPWR; pe->pe_relstep = AR5413_DFS_RELSTEP; pe->pe_maxlen = AR5413_DFS_MAXLEN; pe->pe_usefir128 = 0; pe->pe_blockradar = 1; pe->pe_enmaxrssi = 1; pe->pe_enrelpwr = 1; pe->pe_en_relstep_check = 0; } else { pe->pe_firpwr = AR5212_DFS_FIRPWR; pe->pe_rrssi = AR5212_DFS_RRSSI; pe->pe_height = AR5212_DFS_HEIGHT; pe->pe_prssi = AR5212_DFS_PRSSI; pe->pe_inband = AR5212_DFS_INBAND; pe->pe_relpwr = 0; pe->pe_relstep = 0; pe->pe_maxlen = 0; pe->pe_usefir128 = 0; pe->pe_blockradar = 0; pe->pe_enmaxrssi = 0; pe->pe_enrelpwr = 0; pe->pe_en_relstep_check = 0; } return (AH_TRUE); } void ar5212GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) { uint32_t val,temp; val = OS_REG_READ(ah, AR_PHY_RADAR_0); temp = MS(val,AR_PHY_RADAR_0_FIRPWR); temp |= 0xFFFFFF80; pe->pe_firpwr = temp; pe->pe_rrssi = MS(val, AR_PHY_RADAR_0_RRSSI); pe->pe_height = MS(val, AR_PHY_RADAR_0_HEIGHT); pe->pe_prssi = MS(val, AR_PHY_RADAR_0_PRSSI); pe->pe_inband = MS(val, AR_PHY_RADAR_0_INBAND); pe->pe_enabled = !! (val & AR_PHY_RADAR_0_ENA); pe->pe_relpwr = 0; pe->pe_relstep = 0; pe->pe_maxlen = 0; pe->pe_usefir128 = 0; pe->pe_blockradar = 0; pe->pe_enmaxrssi = 0; pe->pe_enrelpwr = 0; pe->pe_en_relstep_check = 0; pe->pe_extchannel = AH_FALSE; if (IS_5413(ah)) { val = OS_REG_READ(ah, AR_PHY_RADAR_2); pe->pe_relpwr = !! MS(val, AR_PHY_RADAR_2_RELPWR); pe->pe_relstep = !! MS(val, AR_PHY_RADAR_2_RELSTEP); pe->pe_maxlen = !! MS(val, AR_PHY_RADAR_2_MAXLEN); pe->pe_usefir128 = !! (val & AR_PHY_RADAR_2_USEFIR128); pe->pe_blockradar = !! (val & AR_PHY_RADAR_2_BLOCKOFDMWEAK); pe->pe_enmaxrssi = !! (val & AR_PHY_RADAR_2_ENMAXRSSI); pe->pe_enrelpwr = !! (val & AR_PHY_RADAR_2_ENRELPWRCHK); pe->pe_en_relstep_check = !! (val & AR_PHY_RADAR_2_ENRELSTEPCHK); } } /* * Process the radar phy error and extract the pulse duration. */ HAL_BOOL ar5212ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs, uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event) { uint8_t dur; uint8_t rssi; /* Check whether the given phy error is a radar event */ if ((rxs->rs_phyerr != HAL_PHYERR_RADAR) && (rxs->rs_phyerr != HAL_PHYERR_FALSE_RADAR_EXT)) return AH_FALSE; /* * The first byte is the pulse width - if there's * no data, simply set the duration to 0 */ if (rxs->rs_datalen >= 1) /* The pulse width is byte 0 of the data */ dur = ((uint8_t) buf[0]) & 0xff; else dur = 0; /* Pulse RSSI is the normal reported RSSI */ rssi = (uint8_t) rxs->rs_rssi; /* 0 duration/rssi is not a valid radar event */ if (dur == 0 && rssi == 0) return AH_FALSE; HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, dur=%d\n", __func__, rssi, dur); /* Record the event */ event->re_full_ts = fulltsf; event->re_ts = rxs->rs_tstamp; event->re_rssi = rssi; event->re_dur = dur; event->re_flags = HAL_DFS_EVENT_PRICH; return AH_TRUE; } /* * Return whether 5GHz fast-clock (44MHz) is enabled. * It's always disabled for AR5212 series NICs. */ HAL_BOOL ar5212IsFastClockEnabled(struct ath_hal *ah) { return AH_FALSE; } /* * Return what percentage of the extension channel is busy. * This is always disabled for AR5212 series NICs. */ uint32_t ar5212Get11nExtBusy(struct ath_hal *ah) { return 0; } /* * Channel survey support. */ HAL_BOOL ar5212GetMibCycleCounts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hsample) { struct ath_hal_5212 *ahp = AH5212(ah); u_int32_t good = AH_TRUE; /* XXX freeze/unfreeze mib counters */ uint32_t rc = OS_REG_READ(ah, AR_RCCNT); uint32_t rf = OS_REG_READ(ah, AR_RFCNT); uint32_t tf = OS_REG_READ(ah, AR_TFCNT); uint32_t cc = OS_REG_READ(ah, AR_CCCNT); /* read cycles last */ if (ahp->ah_cycleCount == 0 || ahp->ah_cycleCount > cc) { /* * Cycle counter wrap (or initial call); it's not possible * to accurately calculate a value because the registers * right shift rather than wrap--so punt and return 0. */ HALDEBUG(ah, HAL_DEBUG_ANY, "%s: cycle counter wrap. ExtBusy = 0\n", __func__); good = AH_FALSE; } else { hsample->cycle_count = cc - ahp->ah_cycleCount; hsample->chan_busy = rc - ahp->ah_ctlBusy; hsample->ext_chan_busy = 0; hsample->rx_busy = rf - ahp->ah_rxBusy; hsample->tx_busy = tf - ahp->ah_txBusy; } /* * Keep a copy of the MIB results so the next sample has something * to work from. */ ahp->ah_cycleCount = cc; ahp->ah_rxBusy = rf; ahp->ah_ctlBusy = rc; ahp->ah_txBusy = tf; return (good); } void ar5212SetChainMasks(struct ath_hal *ah, uint32_t tx_chainmask, uint32_t rx_chainmask) { } Index: head/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c (revision 280827) +++ head/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c (revision 280828) @@ -1,2796 +1,2797 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ar5212/ar5212.h" #include "ar5212/ar5212reg.h" #include "ar5212/ar5212phy.h" #include "ah_eeprom_v3.h" /* Additional Time delay to wait after activiting the Base band */ #define BASE_ACTIVATE_DELAY 100 /* 100 usec */ #define PLL_SETTLE_DELAY 300 /* 300 usec */ static HAL_BOOL ar5212SetResetReg(struct ath_hal *, uint32_t resetMask); /* NB: public for 5312 use */ HAL_BOOL ar5212IsSpurChannel(struct ath_hal *, const struct ieee80211_channel *); HAL_BOOL ar5212ChannelChange(struct ath_hal *, const struct ieee80211_channel *); int16_t ar5212GetNf(struct ath_hal *, struct ieee80211_channel *); HAL_BOOL ar5212SetBoardValues(struct ath_hal *, const struct ieee80211_channel *); void ar5212SetDeltaSlope(struct ath_hal *, const struct ieee80211_channel *); HAL_BOOL ar5212SetTransmitPower(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t *rfXpdGain); static HAL_BOOL ar5212SetRateTable(struct ath_hal *, const struct ieee80211_channel *, int16_t tpcScaleReduction, int16_t powerLimit, HAL_BOOL commit, int16_t *minPower, int16_t *maxPower); static void ar5212CorrectGainDelta(struct ath_hal *, int twiceOfdmCckDelta); static void ar5212GetTargetPowers(struct ath_hal *, const struct ieee80211_channel *, const TRGT_POWER_INFO *pPowerInfo, uint16_t numChannels, TRGT_POWER_INFO *pNewPower); static uint16_t ar5212GetMaxEdgePower(uint16_t channel, const RD_EDGES_POWER *pRdEdgesPower); void ar5212SetRateDurationTable(struct ath_hal *, const struct ieee80211_channel *); void ar5212SetIFSTiming(struct ath_hal *, const struct ieee80211_channel *); /* NB: public for RF backend use */ void ar5212GetLowerUpperValues(uint16_t value, uint16_t *pList, uint16_t listSize, uint16_t *pLowerValue, uint16_t *pUpperValue); void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32, uint32_t numBits, uint32_t firstBit, uint32_t column); static int write_common(struct ath_hal *ah, const HAL_INI_ARRAY *ia, HAL_BOOL bChannelChange, int writes) { #define IS_NO_RESET_TIMER_ADDR(x) \ ( (((x) >= AR_BEACON) && ((x) <= AR_CFP_DUR)) || \ (((x) >= AR_SLEEP1) && ((x) <= AR_SLEEP3))) #define V(r, c) (ia)->data[((r)*(ia)->cols) + (c)] int r; /* Write Common Array Parameters */ for (r = 0; r < ia->rows; r++) { uint32_t reg = V(r, 0); /* XXX timer/beacon setup registers? */ /* On channel change, don't reset the PCU registers */ if (!(bChannelChange && IS_NO_RESET_TIMER_ADDR(reg))) { OS_REG_WRITE(ah, reg, V(r, 1)); DMA_YIELD(writes); } } return writes; #undef IS_NO_RESET_TIMER_ADDR #undef V } #define IS_DISABLE_FAST_ADC_CHAN(x) (((x) == 2462) || ((x) == 2467)) /* * XXX NDIS 5.x code had MAX_RESET_WAIT set to 2000 for AP code * and 10 for Client code */ #define MAX_RESET_WAIT 10 #define TX_QUEUEPEND_CHECK 1 #define TX_ENABLE_CHECK 2 #define RX_ENABLE_CHECK 4 /* * Places the device in and out of reset and then places sane * values in the registers based on EEPROM config, initialization * vectors (as determined by the mode), and station configuration * * bChannelChange is used to preserve DMA/PCU registers across * a HW Reset during channel change. */ HAL_BOOL ar5212Reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_BOOL bChannelChange, HAL_STATUS *status) { #define N(a) (sizeof (a) / sizeof (a[0])) #define FAIL(_code) do { ecode = _code; goto bad; } while (0) struct ath_hal_5212 *ahp = AH5212(ah); HAL_CHANNEL_INTERNAL *ichan = AH_NULL; const HAL_EEPROM *ee; uint32_t softLedCfg, softLedState; uint32_t saveFrameSeqCount, saveDefAntenna, saveLedState; uint32_t macStaId1, synthDelay, txFrm2TxDStart; uint16_t rfXpdGain[MAX_NUM_PDGAINS_PER_CHANNEL]; int16_t cckOfdmPwrDelta = 0; u_int modesIndex, freqIndex; HAL_STATUS ecode; int i, regWrites; uint32_t testReg, powerVal; int8_t twiceAntennaGain, twiceAntennaReduction; uint32_t ackTpcPow, ctsTpcPow, chirpTpcPow; HAL_BOOL isBmode = AH_FALSE; HALASSERT(ah->ah_magic == AR5212_MAGIC); ee = AH_PRIVATE(ah)->ah_eeprom; OS_MARK(ah, AH_MARK_RESET, bChannelChange); /* Bring out of sleep mode */ if (!ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip did not wakeup\n", __func__); FAIL(HAL_EIO); } /* * Map public channel to private. */ ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) FAIL(HAL_EINVAL); switch (opmode) { case HAL_M_STA: case HAL_M_IBSS: case HAL_M_HOSTAP: case HAL_M_MONITOR: break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid operating mode %u\n", __func__, opmode); FAIL(HAL_EINVAL); break; } HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER3); SAVE_CCK(ah, chan, isBmode); /* Preserve certain DMA hardware registers on a channel change */ if (bChannelChange) { /* * On Venice, the TSF is almost preserved across a reset; * it requires doubling writes to the RESET_TSF * bit in the AR_BEACON register; it also has the quirk * of the TSF going back in time on the station (station * latches onto the last beacon's tsf during a reset 50% * of the times); the latter is not a problem for adhoc * stations since as long as the TSF is behind, it will * get resynchronized on receiving the next beacon; the * TSF going backwards in time could be a problem for the * sleep operation (supported on infrastructure stations * only) - the best and most general fix for this situation * is to resynchronize the various sleep/beacon timers on * the receipt of the next beacon i.e. when the TSF itself * gets resynchronized to the AP's TSF - power save is * needed to be temporarily disabled until that time * * Need to save the sequence number to restore it after * the reset! */ saveFrameSeqCount = OS_REG_READ(ah, AR_D_SEQNUM); } else saveFrameSeqCount = 0; /* NB: silence compiler */ /* Blank the channel survey statistics */ - OS_MEMZERO(&ahp->ah_chansurvey, sizeof(ahp->ah_chansurvey)); + ath_hal_survey_clear(ah); + #if 0 /* * XXX disable for now; this appears to sometimes cause OFDM * XXX timing error floods when ani is enabled and bg scanning * XXX kicks in */ /* If the channel change is across the same mode - perform a fast channel change */ if (IS_2413(ah) || IS_5413(ah)) { /* * Fast channel change can only be used when: * -channel change requested - so it's not the initial reset. * -it's not a change to the current channel - * often called when switching modes on a channel * -the modes of the previous and requested channel are the * same * XXX opmode shouldn't change either? */ if (bChannelChange && (AH_PRIVATE(ah)->ah_curchan != AH_NULL) && (chan->ic_freq != AH_PRIVATE(ah)->ah_curchan->ic_freq) && ((chan->ic_flags & IEEE80211_CHAN_ALLTURBO) == (AH_PRIVATE(ah)->ah_curchan->ic_flags & IEEE80211_CHAN_ALLTURBO))) { if (ar5212ChannelChange(ah, chan)) { /* If ChannelChange completed - skip the rest of reset */ /* XXX ani? */ goto done; } } } #endif /* * Preserve the antenna on a channel change */ saveDefAntenna = OS_REG_READ(ah, AR_DEF_ANTENNA); if (saveDefAntenna == 0) /* XXX magic constants */ saveDefAntenna = 1; /* Save hardware flag before chip reset clears the register */ macStaId1 = OS_REG_READ(ah, AR_STA_ID1) & (AR_STA_ID1_BASE_RATE_11B | AR_STA_ID1_USE_DEFANT); /* Save led state from pci config register */ saveLedState = OS_REG_READ(ah, AR_PCICFG) & (AR_PCICFG_LEDCTL | AR_PCICFG_LEDMODE | AR_PCICFG_LEDBLINK | AR_PCICFG_LEDSLOW); softLedCfg = OS_REG_READ(ah, AR_GPIOCR); softLedState = OS_REG_READ(ah, AR_GPIODO); ar5212RestoreClock(ah, opmode); /* move to refclk operation */ /* * Adjust gain parameters before reset if * there's an outstanding gain updated. */ (void) ar5212GetRfgain(ah); if (!ar5212ChipReset(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); FAIL(HAL_EIO); } /* Setup the indices for the next set of register array writes */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { freqIndex = 2; if (IEEE80211_IS_CHAN_108G(chan)) modesIndex = 5; else if (IEEE80211_IS_CHAN_G(chan)) modesIndex = 4; else if (IEEE80211_IS_CHAN_B(chan)) modesIndex = 3; else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u/0x%x\n", __func__, chan->ic_freq, chan->ic_flags); FAIL(HAL_EINVAL); } } else { freqIndex = 1; if (IEEE80211_IS_CHAN_TURBO(chan)) modesIndex = 2; else if (IEEE80211_IS_CHAN_A(chan)) modesIndex = 1; else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u/0x%x\n", __func__, chan->ic_freq, chan->ic_flags); FAIL(HAL_EINVAL); } } OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); /* Set correct Baseband to analog shift setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); regWrites = ath_hal_ini_write(ah, &ahp->ah_ini_modes, modesIndex, 0); regWrites = write_common(ah, &ahp->ah_ini_common, bChannelChange, regWrites); #ifdef AH_RXCFG_SDMAMW_4BYTES /* * Nala doesn't work with 128 byte bursts on pb42(hydra) (ar71xx), * use 4 instead. Enabling it on all platforms would hurt performance, * so we only enable it on the ones that are affected by it. */ OS_REG_WRITE(ah, AR_RXCFG, 0); #endif ahp->ah_rfHal->writeRegs(ah, modesIndex, freqIndex, regWrites); OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); if (IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan)) { ar5212SetIFSTiming(ah, chan); if (IS_5413(ah)) { /* * Force window_length for 1/2 and 1/4 rate channels, * the ini file sets this to zero otherwise. */ OS_REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_WINLEN, 3); } } /* Overwrite INI values for revised chipsets */ if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_2) { /* ADC_CTL */ OS_REG_WRITE(ah, AR_PHY_ADC_CTL, SM(2, AR_PHY_ADC_CTL_OFF_INBUFGAIN) | SM(2, AR_PHY_ADC_CTL_ON_INBUFGAIN) | AR_PHY_ADC_CTL_OFF_PWDDAC | AR_PHY_ADC_CTL_OFF_PWDADC); /* TX_PWR_ADJ */ if (ichan->channel == 2484) { cckOfdmPwrDelta = SCALE_OC_DELTA( ee->ee_cckOfdmPwrDelta - ee->ee_scaledCh14FilterCckDelta); } else { cckOfdmPwrDelta = SCALE_OC_DELTA( ee->ee_cckOfdmPwrDelta); } if (IEEE80211_IS_CHAN_G(chan)) { OS_REG_WRITE(ah, AR_PHY_TXPWRADJ, SM((ee->ee_cckOfdmPwrDelta*-1), AR_PHY_TXPWRADJ_CCK_GAIN_DELTA) | SM((cckOfdmPwrDelta*-1), AR_PHY_TXPWRADJ_CCK_PCDAC_INDEX)); } else { OS_REG_WRITE(ah, AR_PHY_TXPWRADJ, 0); } /* Add barker RSSI thresh enable as disabled */ OS_REG_CLR_BIT(ah, AR_PHY_DAG_CTRLCCK, AR_PHY_DAG_CTRLCCK_EN_RSSI_THR); OS_REG_RMW_FIELD(ah, AR_PHY_DAG_CTRLCCK, AR_PHY_DAG_CTRLCCK_RSSI_THR, 2); /* Set the mute mask to the correct default */ OS_REG_WRITE(ah, AR_SEQ_MASK, 0x0000000F); } if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_3) { /* Clear reg to alllow RX_CLEAR line debug */ OS_REG_WRITE(ah, AR_PHY_BLUETOOTH, 0); } if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_4) { #ifdef notyet /* Enable burst prefetch for the data queues */ OS_REG_RMW_FIELD(ah, AR_D_FPCTL, ... ); /* Enable double-buffering */ OS_REG_CLR_BIT(ah, AR_TXCFG, AR_TXCFG_DBL_BUF_DIS); #endif } /* Set ADC/DAC select values */ OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x0e); if (IS_5413(ah) || IS_2417(ah)) { uint32_t newReg = 1; if (IS_DISABLE_FAST_ADC_CHAN(ichan->channel)) newReg = 0; /* As it's a clock changing register, only write when the value needs to be changed */ if (OS_REG_READ(ah, AR_PHY_FAST_ADC) != newReg) OS_REG_WRITE(ah, AR_PHY_FAST_ADC, newReg); } /* Setup the transmit power values. */ if (!ar5212SetTransmitPower(ah, chan, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error init'ing transmit power\n", __func__); FAIL(HAL_EIO); } /* Write the analog registers */ if (!ahp->ah_rfHal->setRfRegs(ah, chan, modesIndex, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: ar5212SetRfRegs failed\n", __func__); FAIL(HAL_EIO); } /* Write delta slope for OFDM enabled modes (A, G, Turbo) */ if (IEEE80211_IS_CHAN_OFDM(chan)) { if (IS_5413(ah) || AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER5_3) ar5212SetSpurMitigation(ah, chan); ar5212SetDeltaSlope(ah, chan); } /* Setup board specific options for EEPROM version 3 */ if (!ar5212SetBoardValues(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error setting board options\n", __func__); FAIL(HAL_EIO); } /* Restore certain DMA hardware registers on a channel change */ if (bChannelChange) OS_REG_WRITE(ah, AR_D_SEQNUM, saveFrameSeqCount); OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); OS_REG_WRITE(ah, AR_STA_ID0, LE_READ_4(ahp->ah_macaddr)); OS_REG_WRITE(ah, AR_STA_ID1, LE_READ_2(ahp->ah_macaddr + 4) | macStaId1 | AR_STA_ID1_RTS_USE_DEF | ahp->ah_staId1Defaults ); ar5212SetOperatingMode(ah, opmode); /* Set Venice BSSID mask according to current state */ OS_REG_WRITE(ah, AR_BSSMSKL, LE_READ_4(ahp->ah_bssidmask)); OS_REG_WRITE(ah, AR_BSSMSKU, LE_READ_2(ahp->ah_bssidmask + 4)); /* Restore previous led state */ OS_REG_WRITE(ah, AR_PCICFG, OS_REG_READ(ah, AR_PCICFG) | saveLedState); /* Restore soft Led state to GPIO */ OS_REG_WRITE(ah, AR_GPIOCR, softLedCfg); OS_REG_WRITE(ah, AR_GPIODO, softLedState); /* Restore previous antenna */ OS_REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna); /* then our BSSID and associate id */ OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4) | (ahp->ah_assocId & 0x3fff) << AR_BSS_ID1_AID_S); /* Restore bmiss rssi & count thresholds */ OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr); OS_REG_WRITE(ah, AR_ISR, ~0); /* cleared on write */ if (!ar5212SetChannel(ah, chan)) FAIL(HAL_EIO); OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); ar5212SetCoverageClass(ah, AH_PRIVATE(ah)->ah_coverageClass, 1); ar5212SetRateDurationTable(ah, chan); /* Set Tx frame start to tx data start delay */ if (IS_RAD5112_ANY(ah) && (IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan))) { txFrm2TxDStart = IEEE80211_IS_CHAN_HALF(chan) ? TX_FRAME_D_START_HALF_RATE: TX_FRAME_D_START_QUARTER_RATE; OS_REG_RMW_FIELD(ah, AR_PHY_TX_CTL, AR_PHY_TX_FRAME_TO_TX_DATA_START, txFrm2TxDStart); } /* * Setup fast diversity. * Fast diversity can be enabled or disabled via regadd.txt. * Default is enabled. * For reference, * Disable: reg val * 0x00009860 0x00009d18 (if 11a / 11g, else no change) * 0x00009970 0x192bb514 * 0x0000a208 0xd03e4648 * * Enable: 0x00009860 0x00009d10 (if 11a / 11g, else no change) * 0x00009970 0x192fb514 * 0x0000a208 0xd03e6788 */ /* XXX Setup pre PHY ENABLE EAR additions */ /* * Wait for the frequency synth to settle (synth goes on * via AR_PHY_ACTIVE_EN). Read the phy active delay register. * Value is in 100ns increments. */ synthDelay = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; if (IEEE80211_IS_CHAN_B(chan)) { synthDelay = (4 * synthDelay) / 22; } else { synthDelay /= 10; } /* Activate the PHY (includes baseband activate and synthesizer on) */ OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); /* * There is an issue if the AP starts the calibration before * the base band timeout completes. This could result in the * rx_clear false triggering. As a workaround we add delay an * extra BASE_ACTIVATE_DELAY usecs to ensure this condition * does not happen. */ if (IEEE80211_IS_CHAN_HALF(chan)) { OS_DELAY((synthDelay << 1) + BASE_ACTIVATE_DELAY); } else if (IEEE80211_IS_CHAN_QUARTER(chan)) { OS_DELAY((synthDelay << 2) + BASE_ACTIVATE_DELAY); } else { OS_DELAY(synthDelay + BASE_ACTIVATE_DELAY); } /* * The udelay method is not reliable with notebooks. * Need to check to see if the baseband is ready */ testReg = OS_REG_READ(ah, AR_PHY_TESTCTRL); /* Selects the Tx hold */ OS_REG_WRITE(ah, AR_PHY_TESTCTRL, AR_PHY_TESTCTRL_TXHOLD); i = 0; while ((i++ < 20) && (OS_REG_READ(ah, 0x9c24) & 0x10)) /* test if baseband not ready */ OS_DELAY(200); OS_REG_WRITE(ah, AR_PHY_TESTCTRL, testReg); /* Calibrate the AGC and start a NF calculation */ OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL | AR_PHY_AGC_CONTROL_NF); if (!IEEE80211_IS_CHAN_B(chan) && ahp->ah_bIQCalibration != IQ_CAL_DONE) { /* Start IQ calibration w/ 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX, INIT_IQCAL_LOG_COUNT_MAX); OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_IQCAL); ahp->ah_bIQCalibration = IQ_CAL_RUNNING; } else ahp->ah_bIQCalibration = IQ_CAL_INACTIVE; /* Setup compression registers */ ar5212SetCompRegs(ah); /* Set 1:1 QCU to DCU mapping for all queues */ for (i = 0; i < AR_NUM_DCU; i++) OS_REG_WRITE(ah, AR_DQCUMASK(i), 1 << i); ahp->ah_intrTxqs = 0; for (i = 0; i < AH_PRIVATE(ah)->ah_caps.halTotalQueues; i++) ar5212ResetTxQueue(ah, i); /* * Setup interrupt handling. Note that ar5212ResetTxQueue * manipulates the secondary IMR's as queues are enabled * and disabled. This is done with RMW ops to insure the * settings we make here are preserved. */ ahp->ah_maskReg = AR_IMR_TXOK | AR_IMR_TXERR | AR_IMR_TXURN | AR_IMR_RXOK | AR_IMR_RXERR | AR_IMR_RXORN | AR_IMR_HIUERR ; if (opmode == HAL_M_HOSTAP) ahp->ah_maskReg |= AR_IMR_MIB; OS_REG_WRITE(ah, AR_IMR, ahp->ah_maskReg); /* Enable bus errors that are OR'd to set the HIUERR bit */ OS_REG_WRITE(ah, AR_IMR_S2, OS_REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_MCABT | AR_IMR_S2_SSERR | AR_IMR_S2_DPERR); if (AH_PRIVATE(ah)->ah_rfkillEnabled) ar5212EnableRfKill(ah); if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: offset calibration failed to complete in 1ms;" " noisy environment?\n", __func__); } /* * Set clocks back to 32kHz if they had been using refClk, then * use an external 32kHz crystal when sleeping, if one exists. */ ar5212SetupClock(ah, opmode); /* * Writing to AR_BEACON will start timers. Hence it should * be the last register to be written. Do not reset tsf, do * not enable beacons at this point, but preserve other values * like beaconInterval. */ OS_REG_WRITE(ah, AR_BEACON, (OS_REG_READ(ah, AR_BEACON) &~ (AR_BEACON_EN | AR_BEACON_RESET_TSF))); /* XXX Setup post reset EAR additions */ /* QoS support */ if (AH_PRIVATE(ah)->ah_macVersion > AR_SREV_VERSION_VENICE || (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_VENICE && AH_PRIVATE(ah)->ah_macRev >= AR_SREV_GRIFFIN_LITE)) { OS_REG_WRITE(ah, AR_QOS_CONTROL, 0x100aa); /* XXX magic */ OS_REG_WRITE(ah, AR_QOS_SELECT, 0x3210); /* XXX magic */ } /* Turn on NOACK Support for QoS packets */ OS_REG_WRITE(ah, AR_NOACK, SM(2, AR_NOACK_2BIT_VALUE) | SM(5, AR_NOACK_BIT_OFFSET) | SM(0, AR_NOACK_BYTE_OFFSET)); /* Get Antenna Gain reduction */ if (IEEE80211_IS_CHAN_5GHZ(chan)) { ath_hal_eepromGet(ah, AR_EEP_ANTGAINMAX_5, &twiceAntennaGain); } else { ath_hal_eepromGet(ah, AR_EEP_ANTGAINMAX_2, &twiceAntennaGain); } twiceAntennaReduction = ath_hal_getantennareduction(ah, chan, twiceAntennaGain); /* TPC for self-generated frames */ ackTpcPow = MS(ahp->ah_macTPC, AR_TPC_ACK); if ((ackTpcPow-ahp->ah_txPowerIndexOffset) > chan->ic_maxpower) ackTpcPow = chan->ic_maxpower+ahp->ah_txPowerIndexOffset; if (ackTpcPow > (2*chan->ic_maxregpower - twiceAntennaReduction)) ackTpcPow = (2*chan->ic_maxregpower - twiceAntennaReduction) + ahp->ah_txPowerIndexOffset; ctsTpcPow = MS(ahp->ah_macTPC, AR_TPC_CTS); if ((ctsTpcPow-ahp->ah_txPowerIndexOffset) > chan->ic_maxpower) ctsTpcPow = chan->ic_maxpower+ahp->ah_txPowerIndexOffset; if (ctsTpcPow > (2*chan->ic_maxregpower - twiceAntennaReduction)) ctsTpcPow = (2*chan->ic_maxregpower - twiceAntennaReduction) + ahp->ah_txPowerIndexOffset; chirpTpcPow = MS(ahp->ah_macTPC, AR_TPC_CHIRP); if ((chirpTpcPow-ahp->ah_txPowerIndexOffset) > chan->ic_maxpower) chirpTpcPow = chan->ic_maxpower+ahp->ah_txPowerIndexOffset; if (chirpTpcPow > (2*chan->ic_maxregpower - twiceAntennaReduction)) chirpTpcPow = (2*chan->ic_maxregpower - twiceAntennaReduction) + ahp->ah_txPowerIndexOffset; if (ackTpcPow > 63) ackTpcPow = 63; if (ctsTpcPow > 63) ctsTpcPow = 63; if (chirpTpcPow > 63) chirpTpcPow = 63; powerVal = SM(ackTpcPow, AR_TPC_ACK) | SM(ctsTpcPow, AR_TPC_CTS) | SM(chirpTpcPow, AR_TPC_CHIRP); OS_REG_WRITE(ah, AR_TPC, powerVal); /* Restore user-specified settings */ if (ahp->ah_miscMode != 0) OS_REG_WRITE(ah, AR_MISC_MODE, ahp->ah_miscMode); if (ahp->ah_sifstime != (u_int) -1) ar5212SetSifsTime(ah, ahp->ah_sifstime); if (ahp->ah_slottime != (u_int) -1) ar5212SetSlotTime(ah, ahp->ah_slottime); if (ahp->ah_acktimeout != (u_int) -1) ar5212SetAckTimeout(ah, ahp->ah_acktimeout); if (ahp->ah_ctstimeout != (u_int) -1) ar5212SetCTSTimeout(ah, ahp->ah_ctstimeout); if (AH_PRIVATE(ah)->ah_diagreg != 0) OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg); AH_PRIVATE(ah)->ah_opmode = opmode; /* record operating mode */ #if 0 done: #endif if (bChannelChange && !IEEE80211_IS_CHAN_DFS(chan)) chan->ic_state &= ~IEEE80211_CHANSTATE_CWINT; HALDEBUG(ah, HAL_DEBUG_RESET, "%s: done\n", __func__); RESTORE_CCK(ah, chan, isBmode); OS_MARK(ah, AH_MARK_RESET_DONE, 0); return AH_TRUE; bad: RESTORE_CCK(ah, chan, isBmode); OS_MARK(ah, AH_MARK_RESET_DONE, ecode); if (status != AH_NULL) *status = ecode; return AH_FALSE; #undef FAIL #undef N } /* * Call the rf backend to change the channel. */ HAL_BOOL ar5212SetChannel(struct ath_hal *ah, const struct ieee80211_channel *chan) { struct ath_hal_5212 *ahp = AH5212(ah); /* Change the synth */ if (!ahp->ah_rfHal->setChannel(ah, chan)) return AH_FALSE; return AH_TRUE; } /* * This channel change evaluates whether the selected hardware can * perform a synthesizer-only channel change (no reset). If the * TX is not stopped, or the RFBus cannot be granted in the given * time, the function returns false as a reset is necessary */ HAL_BOOL ar5212ChannelChange(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t ulCount; uint32_t data, synthDelay, qnum; uint16_t rfXpdGain[MAX_NUM_PDGAINS_PER_CHANNEL]; HAL_BOOL txStopped = AH_TRUE; HAL_CHANNEL_INTERNAL *ichan; /* * Map public channel to private. */ ichan = ath_hal_checkchannel(ah, chan); /* TX must be stopped or RF Bus grant will not work */ for (qnum = 0; qnum < AH_PRIVATE(ah)->ah_caps.halTotalQueues; qnum++) { if (ar5212NumTxPending(ah, qnum)) { txStopped = AH_FALSE; break; } } if (!txStopped) return AH_FALSE; /* Kill last Baseband Rx Frame */ OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_REQUEST); /* Request analog bus grant */ for (ulCount = 0; ulCount < 100; ulCount++) { if (OS_REG_READ(ah, AR_PHY_RFBUS_GNT)) break; OS_DELAY(5); } if (ulCount >= 100) return AH_FALSE; /* Change the synth */ if (!ar5212SetChannel(ah, chan)) return AH_FALSE; /* * Wait for the frequency synth to settle (synth goes on via PHY_ACTIVE_EN). * Read the phy active delay register. Value is in 100ns increments. */ data = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; if (IEEE80211_IS_CHAN_B(chan)) { synthDelay = (4 * data) / 22; } else { synthDelay = data / 10; } OS_DELAY(synthDelay + BASE_ACTIVATE_DELAY); /* Setup the transmit power values. */ if (!ar5212SetTransmitPower(ah, chan, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error init'ing transmit power\n", __func__); return AH_FALSE; } /* Write delta slope for OFDM enabled modes (A, G, Turbo) */ if (IEEE80211_IS_CHAN_OFDM(chan)) { if (IS_5413(ah) || AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER5_3) ar5212SetSpurMitigation(ah, chan); ar5212SetDeltaSlope(ah, chan); } /* Release the RFBus Grant */ OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); /* Start Noise Floor Cal */ OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); return AH_TRUE; } void ar5212SetOperatingMode(struct ath_hal *ah, int opmode) { uint32_t val; val = OS_REG_READ(ah, AR_STA_ID1); val &= ~(AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC); switch (opmode) { case HAL_M_HOSTAP: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_STA_AP | AR_STA_ID1_KSRCH_MODE); OS_REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; case HAL_M_IBSS: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_ADHOC | AR_STA_ID1_KSRCH_MODE); OS_REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; case HAL_M_STA: case HAL_M_MONITOR: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_KSRCH_MODE); break; } } /* * Places the PHY and Radio chips into reset. A full reset * must be called to leave this state. The PCI/MAC/PCU are * not placed into reset as we must receive interrupt to * re-enable the hardware. */ HAL_BOOL ar5212PhyDisable(struct ath_hal *ah) { return ar5212SetResetReg(ah, AR_RC_BB); } /* * Places all of hardware into reset */ HAL_BOOL ar5212Disable(struct ath_hal *ah) { if (!ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) return AH_FALSE; /* * Reset the HW - PCI must be reset after the rest of the * device has been reset. */ return ar5212SetResetReg(ah, AR_RC_MAC | AR_RC_BB | AR_RC_PCI); } /* * Places the hardware into reset and then pulls it out of reset * * TODO: Only write the PLL if we're changing to or from CCK mode * * WARNING: The order of the PLL and mode registers must be correct. */ HAL_BOOL ar5212ChipReset(struct ath_hal *ah, const struct ieee80211_channel *chan) { OS_MARK(ah, AH_MARK_CHIPRESET, chan ? chan->ic_freq : 0); /* * Reset the HW - PCI must be reset after the rest of the * device has been reset */ if (!ar5212SetResetReg(ah, AR_RC_MAC | AR_RC_BB | AR_RC_PCI)) return AH_FALSE; /* Bring out of sleep mode (AGAIN) */ if (!ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) return AH_FALSE; /* Clear warm reset register */ if (!ar5212SetResetReg(ah, 0)) return AH_FALSE; /* * Perform warm reset before the mode/PLL/turbo registers * are changed in order to deactivate the radio. Mode changes * with an active radio can result in corrupted shifts to the * radio device. */ /* * Set CCK and Turbo modes correctly. */ if (chan != AH_NULL) { /* NB: can be null during attach */ uint32_t rfMode, phyPLL = 0, curPhyPLL, turbo; if (IS_5413(ah)) { /* NB: =>'s 5424 also */ rfMode = AR_PHY_MODE_AR5112; if (IEEE80211_IS_CHAN_HALF(chan)) rfMode |= AR_PHY_MODE_HALF; else if (IEEE80211_IS_CHAN_QUARTER(chan)) rfMode |= AR_PHY_MODE_QUARTER; if (IEEE80211_IS_CHAN_CCK(chan)) phyPLL = AR_PHY_PLL_CTL_44_5112; else phyPLL = AR_PHY_PLL_CTL_40_5413; } else if (IS_RAD5111(ah)) { rfMode = AR_PHY_MODE_AR5111; if (IEEE80211_IS_CHAN_CCK(chan)) phyPLL = AR_PHY_PLL_CTL_44; else phyPLL = AR_PHY_PLL_CTL_40; if (IEEE80211_IS_CHAN_HALF(chan)) phyPLL = AR_PHY_PLL_CTL_HALF; else if (IEEE80211_IS_CHAN_QUARTER(chan)) phyPLL = AR_PHY_PLL_CTL_QUARTER; } else { /* 5112, 2413, 2316, 2317 */ rfMode = AR_PHY_MODE_AR5112; if (IEEE80211_IS_CHAN_CCK(chan)) phyPLL = AR_PHY_PLL_CTL_44_5112; else phyPLL = AR_PHY_PLL_CTL_40_5112; if (IEEE80211_IS_CHAN_HALF(chan)) phyPLL |= AR_PHY_PLL_CTL_HALF; else if (IEEE80211_IS_CHAN_QUARTER(chan)) phyPLL |= AR_PHY_PLL_CTL_QUARTER; } if (IEEE80211_IS_CHAN_G(chan)) rfMode |= AR_PHY_MODE_DYNAMIC; else if (IEEE80211_IS_CHAN_OFDM(chan)) rfMode |= AR_PHY_MODE_OFDM; else rfMode |= AR_PHY_MODE_CCK; if (IEEE80211_IS_CHAN_5GHZ(chan)) rfMode |= AR_PHY_MODE_RF5GHZ; else rfMode |= AR_PHY_MODE_RF2GHZ; turbo = IEEE80211_IS_CHAN_TURBO(chan) ? (AR_PHY_FC_TURBO_MODE | AR_PHY_FC_TURBO_SHORT) : 0; curPhyPLL = OS_REG_READ(ah, AR_PHY_PLL_CTL); /* * PLL, Mode, and Turbo values must be written in the correct * order to ensure: * - The PLL cannot be set to 44 unless the CCK or DYNAMIC * mode bit is set * - Turbo cannot be set at the same time as CCK or DYNAMIC */ if (IEEE80211_IS_CHAN_CCK(chan)) { OS_REG_WRITE(ah, AR_PHY_TURBO, turbo); OS_REG_WRITE(ah, AR_PHY_MODE, rfMode); if (curPhyPLL != phyPLL) { OS_REG_WRITE(ah, AR_PHY_PLL_CTL, phyPLL); /* Wait for the PLL to settle */ OS_DELAY(PLL_SETTLE_DELAY); } } else { if (curPhyPLL != phyPLL) { OS_REG_WRITE(ah, AR_PHY_PLL_CTL, phyPLL); /* Wait for the PLL to settle */ OS_DELAY(PLL_SETTLE_DELAY); } OS_REG_WRITE(ah, AR_PHY_TURBO, turbo); OS_REG_WRITE(ah, AR_PHY_MODE, rfMode); } } return AH_TRUE; } /* * Recalibrate the lower PHY chips to account for temperature/environment * changes. */ HAL_BOOL ar5212PerCalibrationN(struct ath_hal *ah, struct ieee80211_channel *chan, u_int chainMask, HAL_BOOL longCal, HAL_BOOL *isCalDone) { #define IQ_CAL_TRIES 10 struct ath_hal_5212 *ahp = AH5212(ah); HAL_CHANNEL_INTERNAL *ichan; int32_t qCoff, qCoffDenom; int32_t iqCorrMeas, iCoff, iCoffDenom; uint32_t powerMeasQ, powerMeasI; HAL_BOOL isBmode = AH_FALSE; OS_MARK(ah, AH_MARK_PERCAL, chan->ic_freq); *isCalDone = AH_FALSE; ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return AH_FALSE; } SAVE_CCK(ah, chan, isBmode); if (ahp->ah_bIQCalibration == IQ_CAL_DONE || ahp->ah_bIQCalibration == IQ_CAL_INACTIVE) *isCalDone = AH_TRUE; /* IQ calibration in progress. Check to see if it has finished. */ if (ahp->ah_bIQCalibration == IQ_CAL_RUNNING && !(OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_DO_IQCAL)) { int i; /* IQ Calibration has finished. */ ahp->ah_bIQCalibration = IQ_CAL_INACTIVE; *isCalDone = AH_TRUE; /* workaround for misgated IQ Cal results */ i = 0; do { /* Read calibration results. */ powerMeasI = OS_REG_READ(ah, AR_PHY_IQCAL_RES_PWR_MEAS_I); powerMeasQ = OS_REG_READ(ah, AR_PHY_IQCAL_RES_PWR_MEAS_Q); iqCorrMeas = OS_REG_READ(ah, AR_PHY_IQCAL_RES_IQ_CORR_MEAS); if (powerMeasI && powerMeasQ) break; /* Do we really need this??? */ OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_IQCAL); } while (++i < IQ_CAL_TRIES); HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: IQ cal finished: %d tries\n", __func__, i); HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: powerMeasI %u powerMeasQ %u iqCorrMeas %d\n", __func__, powerMeasI, powerMeasQ, iqCorrMeas); /* * Prescale these values to remove 64-bit operation * requirement at the loss of a little precision. */ iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 128; qCoffDenom = powerMeasQ / 128; /* Protect against divide-by-0 and loss of sign bits. */ if (iCoffDenom != 0 && qCoffDenom >= 2) { iCoff = (int8_t)(-iqCorrMeas) / iCoffDenom; /* IQCORR_Q_I_COFF is a signed 6 bit number */ if (iCoff < -32) { iCoff = -32; } else if (iCoff > 31) { iCoff = 31; } /* IQCORR_Q_Q_COFF is a signed 5 bit number */ qCoff = (powerMeasI / qCoffDenom) - 128; if (qCoff < -16) { qCoff = -16; } else if (qCoff > 15) { qCoff = 15; } HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: iCoff %d qCoff %d\n", __func__, iCoff, qCoff); /* Write values and enable correction */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, iCoff); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, qCoff); OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCORR_ENABLE); ahp->ah_bIQCalibration = IQ_CAL_DONE; ichan->privFlags |= CHANNEL_IQVALID; ichan->iCoff = iCoff; ichan->qCoff = qCoff; } } else if (!IEEE80211_IS_CHAN_B(chan) && ahp->ah_bIQCalibration == IQ_CAL_DONE && (ichan->privFlags & CHANNEL_IQVALID) == 0) { /* * Start IQ calibration if configured channel has changed. * Use a magic number of 15 based on default value. */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX, INIT_IQCAL_LOG_COUNT_MAX); OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_IQCAL); ahp->ah_bIQCalibration = IQ_CAL_RUNNING; } /* XXX EAR */ if (longCal) { /* Check noise floor results */ ar5212GetNf(ah, chan); if (!IEEE80211_IS_CHAN_CWINT(chan)) { /* Perform cal for 5Ghz channels and any OFDM on 5112 */ if (IEEE80211_IS_CHAN_5GHZ(chan) || (IS_RAD5112(ah) && IEEE80211_IS_CHAN_OFDM(chan))) ar5212RequestRfgain(ah); } } RESTORE_CCK(ah, chan, isBmode); return AH_TRUE; #undef IQ_CAL_TRIES } HAL_BOOL ar5212PerCalibration(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_BOOL *isIQdone) { return ar5212PerCalibrationN(ah, chan, 0x1, AH_TRUE, isIQdone); } HAL_BOOL ar5212ResetCalValid(struct ath_hal *ah, const struct ieee80211_channel *chan) { HAL_CHANNEL_INTERNAL *ichan; ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return AH_FALSE; } ichan->privFlags &= ~CHANNEL_IQVALID; return AH_TRUE; } /************************************************************** * ar5212MacStop * * Disables all active QCUs and ensure that the mac is in a * quiessence state. */ static HAL_BOOL ar5212MacStop(struct ath_hal *ah) { HAL_BOOL status; uint32_t count; uint32_t pendFrameCount; uint32_t macStateFlag; uint32_t queue; status = AH_FALSE; /* Disable Rx Operation ***********************************/ OS_REG_SET_BIT(ah, AR_CR, AR_CR_RXD); /* Disable TX Operation ***********************************/ #ifdef NOT_YET ar5212SetTxdpInvalid(ah); #endif OS_REG_SET_BIT(ah, AR_Q_TXD, AR_Q_TXD_M); /* Polling operation for completion of disable ************/ macStateFlag = TX_ENABLE_CHECK | RX_ENABLE_CHECK; for (count = 0; count < MAX_RESET_WAIT; count++) { if (macStateFlag & RX_ENABLE_CHECK) { if (!OS_REG_IS_BIT_SET(ah, AR_CR, AR_CR_RXE)) { macStateFlag &= ~RX_ENABLE_CHECK; } } if (macStateFlag & TX_ENABLE_CHECK) { if (!OS_REG_IS_BIT_SET(ah, AR_Q_TXE, AR_Q_TXE_M)) { macStateFlag &= ~TX_ENABLE_CHECK; macStateFlag |= TX_QUEUEPEND_CHECK; } } if (macStateFlag & TX_QUEUEPEND_CHECK) { pendFrameCount = 0; for (queue = 0; queue < AR_NUM_DCU; queue++) { pendFrameCount += OS_REG_READ(ah, AR_Q0_STS + (queue * 4)) & AR_Q_STS_PEND_FR_CNT; } if (pendFrameCount == 0) { macStateFlag &= ~TX_QUEUEPEND_CHECK; } } if (macStateFlag == 0) { status = AH_TRUE; break; } OS_DELAY(50); } if (status != AH_TRUE) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s:Failed to stop the MAC state 0x%x\n", __func__, macStateFlag); } return status; } /* * Write the given reset bit mask into the reset register */ static HAL_BOOL ar5212SetResetReg(struct ath_hal *ah, uint32_t resetMask) { uint32_t mask = resetMask ? resetMask : ~0; HAL_BOOL rt; /* Never reset the PCIE core */ if (AH_PRIVATE(ah)->ah_ispcie) { resetMask &= ~AR_RC_PCI; } if (resetMask & (AR_RC_MAC | AR_RC_PCI)) { /* * To ensure that the driver can reset the * MAC, wake up the chip */ rt = ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE); if (rt != AH_TRUE) { return rt; } /* * Disable interrupts */ OS_REG_WRITE(ah, AR_IER, AR_IER_DISABLE); OS_REG_READ(ah, AR_IER); if (ar5212MacStop(ah) != AH_TRUE) { /* * Failed to stop the MAC gracefully; let's be more forceful then */ /* need some delay before flush any pending MMR writes */ OS_DELAY(15); OS_REG_READ(ah, AR_RXDP); resetMask |= AR_RC_MAC | AR_RC_BB; /* _Never_ reset PCI Express core */ if (! AH_PRIVATE(ah)->ah_ispcie) { resetMask |= AR_RC_PCI; } #if 0 /* * Flush the park address of the PCI controller */ /* Read PCI slot information less than Hainan revision */ if (AH_PRIVATE(ah)->ah_bustype == HAL_BUS_TYPE_PCI) { if (!IS_5112_REV5_UP(ah)) { #define PCI_COMMON_CONFIG_STATUS 0x06 u_int32_t i; u_int16_t reg16; for (i = 0; i < 32; i++) { ath_hal_read_pci_config_space(ah, PCI_COMMON_CONFIG_STATUS, ®16, sizeof(reg16)); } } #undef PCI_COMMON_CONFIG_STATUS } #endif } else { /* * MAC stopped gracefully; no need to warm-reset the PCI bus */ resetMask &= ~AR_RC_PCI; /* need some delay before flush any pending MMR writes */ OS_DELAY(15); OS_REG_READ(ah, AR_RXDP); } } (void) OS_REG_READ(ah, AR_RXDP);/* flush any pending MMR writes */ OS_REG_WRITE(ah, AR_RC, resetMask); OS_DELAY(15); /* need to wait at least 128 clocks when reseting PCI before read */ mask &= (AR_RC_MAC | AR_RC_BB); resetMask &= (AR_RC_MAC | AR_RC_BB); rt = ath_hal_wait(ah, AR_RC, mask, resetMask); if ((resetMask & AR_RC_MAC) == 0) { if (isBigEndian()) { /* * Set CFG, little-endian for descriptor accesses. */ mask = INIT_CONFIG_STATUS | AR_CFG_SWRD; #ifndef AH_NEED_DESC_SWAP mask |= AR_CFG_SWTD; #endif OS_REG_WRITE(ah, AR_CFG, mask); } else OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS); if (ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) (void) OS_REG_READ(ah, AR_ISR_RAC); } /* track PHY power state so we don't try to r/w BB registers */ AH5212(ah)->ah_phyPowerOn = ((resetMask & AR_RC_BB) == 0); return rt; } int16_t ar5212GetNoiseFloor(struct ath_hal *ah) { int16_t nf = (OS_REG_READ(ah, AR_PHY(25)) >> 19) & 0x1ff; if (nf & 0x100) nf = 0 - ((nf ^ 0x1ff) + 1); return nf; } static HAL_BOOL getNoiseFloorThresh(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *nft) { const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; HALASSERT(ah->ah_magic == AR5212_MAGIC); switch (chan->ic_flags & IEEE80211_CHAN_ALLFULL) { case IEEE80211_CHAN_A: *nft = ee->ee_noiseFloorThresh[headerInfo11A]; break; case IEEE80211_CHAN_B: *nft = ee->ee_noiseFloorThresh[headerInfo11B]; break; case IEEE80211_CHAN_G: case IEEE80211_CHAN_PUREG: /* NB: really 108G */ *nft = ee->ee_noiseFloorThresh[headerInfo11G]; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags %u/0x%x\n", __func__, chan->ic_freq, chan->ic_flags); return AH_FALSE; } return AH_TRUE; } /* * Setup the noise floor cal history buffer. */ void ar5212InitNfCalHistBuffer(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); int i; ahp->ah_nfCalHist.first_run = 1; ahp->ah_nfCalHist.currIndex = 0; ahp->ah_nfCalHist.privNF = AR5212_CCA_MAX_GOOD_VALUE; ahp->ah_nfCalHist.invalidNFcount = AR512_NF_CAL_HIST_MAX; for (i = 0; i < AR512_NF_CAL_HIST_MAX; i ++) ahp->ah_nfCalHist.nfCalBuffer[i] = AR5212_CCA_MAX_GOOD_VALUE; } /* * Add a noise floor value to the ring buffer. */ static __inline void updateNFHistBuff(struct ar5212NfCalHist *h, int16_t nf) { h->nfCalBuffer[h->currIndex] = nf; if (++h->currIndex >= AR512_NF_CAL_HIST_MAX) h->currIndex = 0; } /* * Return the median noise floor value in the ring buffer. */ int16_t ar5212GetNfHistMid(const int16_t calData[AR512_NF_CAL_HIST_MAX]) { int16_t sort[AR512_NF_CAL_HIST_MAX]; int i, j; OS_MEMCPY(sort, calData, AR512_NF_CAL_HIST_MAX*sizeof(int16_t)); for (i = 0; i < AR512_NF_CAL_HIST_MAX-1; i ++) { for (j = 1; j < AR512_NF_CAL_HIST_MAX-i; j ++) { if (sort[j] > sort[j-1]) { int16_t nf = sort[j]; sort[j] = sort[j-1]; sort[j-1] = nf; } } } return sort[(AR512_NF_CAL_HIST_MAX-1)>>1]; } /* * Read the NF and check it against the noise floor threshhold */ int16_t ar5212GetNf(struct ath_hal *ah, struct ieee80211_channel *chan) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212NfCalHist *h = &ahp->ah_nfCalHist; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); int16_t nf, nfThresh; int32_t val; if (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: NF did not complete in calibration window\n", __func__); ichan->rawNoiseFloor = h->privNF; /* most recent value */ return ichan->rawNoiseFloor; } /* * Finished NF cal, check against threshold. */ nf = ar5212GetNoiseFloor(ah); if (getNoiseFloorThresh(ah, chan, &nfThresh)) { if (nf > nfThresh) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: noise floor failed detected; detected %u, " "threshold %u\n", __func__, nf, nfThresh); /* * NB: Don't discriminate 2.4 vs 5Ghz, if this * happens it indicates a problem regardless * of the band. */ chan->ic_state |= IEEE80211_CHANSTATE_CWINT; nf = 0; } } else nf = 0; /* * Pass through histogram and write median value as * calculated from the accrued window. We require a * full window of in-range values to be seen before we * start using the history. */ updateNFHistBuff(h, nf); if (h->first_run) { if (nf < AR5212_CCA_MIN_BAD_VALUE || nf > AR5212_CCA_MAX_HIGH_VALUE) { nf = AR5212_CCA_MAX_GOOD_VALUE; h->invalidNFcount = AR512_NF_CAL_HIST_MAX; } else if (--(h->invalidNFcount) == 0) { h->first_run = 0; h->privNF = nf = ar5212GetNfHistMid(h->nfCalBuffer); } else { nf = AR5212_CCA_MAX_GOOD_VALUE; } } else { h->privNF = nf = ar5212GetNfHistMid(h->nfCalBuffer); } val = OS_REG_READ(ah, AR_PHY(25)); val &= 0xFFFFFE00; val |= (((uint32_t)nf << 1) & 0x1FF); OS_REG_WRITE(ah, AR_PHY(25), val); OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF, 0)) { #ifdef AH_DEBUG ath_hal_printf(ah, "%s: AGC not ready AGC_CONTROL 0x%x\n", __func__, OS_REG_READ(ah, AR_PHY_AGC_CONTROL)); #endif } /* * Now load a high maxCCAPower value again so that we're * not capped by the median we just loaded */ val &= 0xFFFFFE00; val |= (((uint32_t)(-50) << 1) & 0x1FF); OS_REG_WRITE(ah, AR_PHY(25), val); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); return (ichan->rawNoiseFloor = nf); } /* * Set up compression configuration registers */ void ar5212SetCompRegs(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); int i; /* Check if h/w supports compression */ if (!AH_PRIVATE(ah)->ah_caps.halCompressSupport) return; OS_REG_WRITE(ah, AR_DCCFG, 1); OS_REG_WRITE(ah, AR_CCFG, (AR_COMPRESSION_WINDOW_SIZE >> 8) & AR_CCFG_WIN_M); OS_REG_WRITE(ah, AR_CCFG, OS_REG_READ(ah, AR_CCFG) | AR_CCFG_MIB_INT_EN); OS_REG_WRITE(ah, AR_CCUCFG, AR_CCUCFG_RESET_VAL | AR_CCUCFG_CATCHUP_EN); OS_REG_WRITE(ah, AR_CPCOVF, 0); /* reset decompression mask */ for (i = 0; i < HAL_DECOMP_MASK_SIZE; i++) { OS_REG_WRITE(ah, AR_DCM_A, i); OS_REG_WRITE(ah, AR_DCM_D, ahp->ah_decompMask[i]); } } HAL_BOOL ar5212SetAntennaSwitchInternal(struct ath_hal *ah, HAL_ANT_SETTING settings, const struct ieee80211_channel *chan) { #define ANT_SWITCH_TABLE1 AR_PHY(88) #define ANT_SWITCH_TABLE2 AR_PHY(89) struct ath_hal_5212 *ahp = AH5212(ah); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; uint32_t antSwitchA, antSwitchB; int ix; HALASSERT(ah->ah_magic == AR5212_MAGIC); HALASSERT(ahp->ah_phyPowerOn); switch (chan->ic_flags & IEEE80211_CHAN_ALLFULL) { case IEEE80211_CHAN_A: ix = 0; break; case IEEE80211_CHAN_G: case IEEE80211_CHAN_PUREG: /* NB: 108G */ ix = 2; break; case IEEE80211_CHAN_B: if (IS_2425(ah) || IS_2417(ah)) { /* NB: Nala/Swan: 11b is handled using 11g */ ix = 2; } else ix = 1; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n", __func__, chan->ic_flags); return AH_FALSE; } antSwitchA = ee->ee_antennaControl[1][ix] | (ee->ee_antennaControl[2][ix] << 6) | (ee->ee_antennaControl[3][ix] << 12) | (ee->ee_antennaControl[4][ix] << 18) | (ee->ee_antennaControl[5][ix] << 24) ; antSwitchB = ee->ee_antennaControl[6][ix] | (ee->ee_antennaControl[7][ix] << 6) | (ee->ee_antennaControl[8][ix] << 12) | (ee->ee_antennaControl[9][ix] << 18) | (ee->ee_antennaControl[10][ix] << 24) ; /* * For fixed antenna, give the same setting for both switch banks */ switch (settings) { case HAL_ANT_FIXED_A: antSwitchB = antSwitchA; break; case HAL_ANT_FIXED_B: antSwitchA = antSwitchB; break; case HAL_ANT_VARIABLE: break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad antenna setting %u\n", __func__, settings); return AH_FALSE; } if (antSwitchB == antSwitchA) { HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: Setting fast diversity off.\n", __func__); OS_REG_CLR_BIT(ah,AR_PHY_CCK_DETECT, AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); ahp->ah_diversity = AH_FALSE; } else { HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: Setting fast diversity on.\n", __func__); OS_REG_SET_BIT(ah,AR_PHY_CCK_DETECT, AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); ahp->ah_diversity = AH_TRUE; } ahp->ah_antControl = settings; OS_REG_WRITE(ah, ANT_SWITCH_TABLE1, antSwitchA); OS_REG_WRITE(ah, ANT_SWITCH_TABLE2, antSwitchB); return AH_TRUE; #undef ANT_SWITCH_TABLE2 #undef ANT_SWITCH_TABLE1 } HAL_BOOL ar5212IsSpurChannel(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint16_t freq = ath_hal_gethwchannel(ah, chan); uint32_t clockFreq = ((IS_5413(ah) || IS_RAD5112_ANY(ah) || IS_2417(ah)) ? 40 : 32); return ( ((freq % clockFreq) != 0) && (((freq % clockFreq) < 10) || (((freq) % clockFreq) > 22)) ); } /* * Read EEPROM header info and program the device for correct operation * given the channel value. */ HAL_BOOL ar5212SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) { #define NO_FALSE_DETECT_BACKOFF 2 #define CB22_FALSE_DETECT_BACKOFF 6 #define AR_PHY_BIS(_ah, _reg, _mask, _val) \ OS_REG_WRITE(_ah, AR_PHY(_reg), \ (OS_REG_READ(_ah, AR_PHY(_reg)) & _mask) | (_val)); struct ath_hal_5212 *ahp = AH5212(ah); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; int arrayMode, falseDectectBackoff; int is2GHz = IEEE80211_IS_CHAN_2GHZ(chan); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); int8_t adcDesiredSize, pgaDesiredSize; uint16_t switchSettling, txrxAtten, rxtxMargin; int iCoff, qCoff; HALASSERT(ah->ah_magic == AR5212_MAGIC); switch (chan->ic_flags & IEEE80211_CHAN_ALLTURBOFULL) { case IEEE80211_CHAN_A: case IEEE80211_CHAN_ST: arrayMode = headerInfo11A; if (!IS_RAD5112_ANY(ah) && !IS_2413(ah) && !IS_5413(ah)) OS_REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP, ahp->ah_gainValues.currStep->paramVal[GP_TXCLIP]); break; case IEEE80211_CHAN_B: arrayMode = headerInfo11B; break; case IEEE80211_CHAN_G: case IEEE80211_CHAN_108G: arrayMode = headerInfo11G; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n", __func__, chan->ic_flags); return AH_FALSE; } /* Set the antenna register(s) correctly for the chip revision */ AR_PHY_BIS(ah, 68, 0xFFFFFC06, (ee->ee_antennaControl[0][arrayMode] << 4) | 0x1); ar5212SetAntennaSwitchInternal(ah, ahp->ah_antControl, chan); /* Set the Noise Floor Thresh on ar5211 devices */ OS_REG_WRITE(ah, AR_PHY(90), (ee->ee_noiseFloorThresh[arrayMode] & 0x1FF) | (1 << 9)); if (ee->ee_version >= AR_EEPROM_VER5_0 && IEEE80211_IS_CHAN_TURBO(chan)) { switchSettling = ee->ee_switchSettlingTurbo[is2GHz]; adcDesiredSize = ee->ee_adcDesiredSizeTurbo[is2GHz]; pgaDesiredSize = ee->ee_pgaDesiredSizeTurbo[is2GHz]; txrxAtten = ee->ee_txrxAttenTurbo[is2GHz]; rxtxMargin = ee->ee_rxtxMarginTurbo[is2GHz]; } else { switchSettling = ee->ee_switchSettling[arrayMode]; adcDesiredSize = ee->ee_adcDesiredSize[arrayMode]; pgaDesiredSize = ee->ee_pgaDesiredSize[is2GHz]; txrxAtten = ee->ee_txrxAtten[is2GHz]; rxtxMargin = ee->ee_rxtxMargin[is2GHz]; } OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, switchSettling); OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, adcDesiredSize); OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_PGA, pgaDesiredSize); OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN, AR_PHY_RXGAIN_TXRX_ATTEN, txrxAtten); OS_REG_WRITE(ah, AR_PHY(13), (ee->ee_txEndToXPAOff[arrayMode] << 24) | (ee->ee_txEndToXPAOff[arrayMode] << 16) | (ee->ee_txFrameToXPAOn[arrayMode] << 8) | ee->ee_txFrameToXPAOn[arrayMode]); AR_PHY_BIS(ah, 10, 0xFFFF00FF, ee->ee_txEndToXLNAOn[arrayMode] << 8); AR_PHY_BIS(ah, 25, 0xFFF80FFF, (ee->ee_thresh62[arrayMode] << 12) & 0x7F000); /* * False detect backoff - suspected 32 MHz spur causes false * detects in OFDM, causing Tx Hangs. Decrease weak signal * sensitivity for this card. */ falseDectectBackoff = NO_FALSE_DETECT_BACKOFF; if (ee->ee_version < AR_EEPROM_VER3_3) { /* XXX magic number */ if (AH_PRIVATE(ah)->ah_subvendorid == 0x1022 && IEEE80211_IS_CHAN_OFDM(chan)) falseDectectBackoff += CB22_FALSE_DETECT_BACKOFF; } else { if (ar5212IsSpurChannel(ah, chan)) falseDectectBackoff += ee->ee_falseDetectBackoff[arrayMode]; } AR_PHY_BIS(ah, 73, 0xFFFFFF01, (falseDectectBackoff << 1) & 0xFE); if (ichan->privFlags & CHANNEL_IQVALID) { iCoff = ichan->iCoff; qCoff = ichan->qCoff; } else { iCoff = ee->ee_iqCalI[is2GHz]; qCoff = ee->ee_iqCalQ[is2GHz]; } /* write previous IQ results */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, iCoff); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, qCoff); OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_IQCORR_ENABLE); if (ee->ee_version >= AR_EEPROM_VER4_1) { if (!IEEE80211_IS_CHAN_108G(chan) || ee->ee_version >= AR_EEPROM_VER5_0) OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, AR_PHY_GAIN_2GHZ_RXTX_MARGIN, rxtxMargin); } if (ee->ee_version >= AR_EEPROM_VER5_1) { /* for now always disabled */ OS_REG_WRITE(ah, AR_PHY_HEAVY_CLIP_ENABLE, 0); } return AH_TRUE; #undef AR_PHY_BIS #undef NO_FALSE_DETECT_BACKOFF #undef CB22_FALSE_DETECT_BACKOFF } /* * Apply Spur Immunity to Boards that require it. * Applies only to OFDM RX operation. */ void ar5212SetSpurMitigation(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t pilotMask[2] = {0, 0}, binMagMask[4] = {0, 0, 0 , 0}; uint16_t i, finalSpur, curChanAsSpur, binWidth = 0, spurDetectWidth, spurChan; int32_t spurDeltaPhase = 0, spurFreqSd = 0, spurOffset, binOffsetNumT16, curBinOffset; int16_t numBinOffsets; static const uint16_t magMapFor4[4] = {1, 2, 2, 1}; static const uint16_t magMapFor3[3] = {1, 2, 1}; const uint16_t *pMagMap; HAL_BOOL is2GHz = IEEE80211_IS_CHAN_2GHZ(chan); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); uint32_t val; #define CHAN_TO_SPUR(_f, _freq) ( ((_freq) - ((_f) ? 2300 : 4900)) * 10 ) if (IS_2417(ah)) { HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: no spur mitigation\n", __func__); return; } curChanAsSpur = CHAN_TO_SPUR(is2GHz, ichan->channel); if (ichan->mainSpur) { /* Pull out the saved spur value */ finalSpur = ichan->mainSpur; } else { /* * Check if spur immunity should be performed for this channel * Should only be performed once per channel and then saved */ finalSpur = AR_NO_SPUR; spurDetectWidth = HAL_SPUR_CHAN_WIDTH; if (IEEE80211_IS_CHAN_TURBO(chan)) spurDetectWidth *= 2; /* Decide if any spur affects the current channel */ for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { spurChan = ath_hal_getSpurChan(ah, i, is2GHz); if (spurChan == AR_NO_SPUR) { break; } if ((curChanAsSpur - spurDetectWidth <= (spurChan & HAL_SPUR_VAL_MASK)) && (curChanAsSpur + spurDetectWidth >= (spurChan & HAL_SPUR_VAL_MASK))) { finalSpur = spurChan & HAL_SPUR_VAL_MASK; break; } } /* Save detected spur (or no spur) for this channel */ ichan->mainSpur = finalSpur; } /* Write spur immunity data */ if (finalSpur == AR_NO_SPUR) { /* Disable Spur Immunity Regs if they appear set */ if (OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER) { /* Clear Spur Delta Phase, Spur Freq, and enable bits */ OS_REG_RMW_FIELD(ah, AR_PHY_MASK_CTL, AR_PHY_MASK_CTL_RATE, 0); val = OS_REG_READ(ah, AR_PHY_TIMING_CTRL4); val &= ~(AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); OS_REG_WRITE(ah, AR_PHY_MASK_CTL, val); OS_REG_WRITE(ah, AR_PHY_TIMING11, 0); /* Clear pilot masks */ OS_REG_WRITE(ah, AR_PHY_TIMING7, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING8, AR_PHY_TIMING8_PILOT_MASK_2, 0); OS_REG_WRITE(ah, AR_PHY_TIMING9, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING10, AR_PHY_TIMING10_PILOT_MASK_2, 0); /* Clear magnitude masks */ OS_REG_WRITE(ah, AR_PHY_BIN_MASK_1, 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK_2, 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK_3, 0); OS_REG_RMW_FIELD(ah, AR_PHY_MASK_CTL, AR_PHY_MASK_CTL_MASK_4, 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_1, 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_2, 0); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_3, 0); OS_REG_RMW_FIELD(ah, AR_PHY_BIN_MASK2_4, AR_PHY_BIN_MASK2_4_MASK_4, 0); } } else { spurOffset = finalSpur - curChanAsSpur; /* * Spur calculations: * spurDeltaPhase is (spurOffsetIn100KHz / chipFrequencyIn100KHz) << 21 * spurFreqSd is (spurOffsetIn100KHz / sampleFrequencyIn100KHz) << 11 */ if (IEEE80211_IS_CHAN_TURBO(chan)) { /* Chip Frequency & sampleFrequency are 80 MHz */ spurDeltaPhase = (spurOffset << 16) / 25; spurFreqSd = spurDeltaPhase >> 10; binWidth = HAL_BIN_WIDTH_TURBO_100HZ; } else if (IEEE80211_IS_CHAN_G(chan)) { /* Chip Frequency is 44MHz, sampleFrequency is 40 MHz */ spurFreqSd = (spurOffset << 8) / 55; spurDeltaPhase = (spurOffset << 17) / 25; binWidth = HAL_BIN_WIDTH_BASE_100HZ; } else { HALASSERT(!IEEE80211_IS_CHAN_B(chan)); /* Chip Frequency & sampleFrequency are 40 MHz */ spurDeltaPhase = (spurOffset << 17) / 25; spurFreqSd = spurDeltaPhase >> 10; binWidth = HAL_BIN_WIDTH_BASE_100HZ; } /* Compute Pilot Mask */ binOffsetNumT16 = ((spurOffset * 1000) << 4) / binWidth; /* The spur is on a bin if it's remainder at times 16 is 0 */ if (binOffsetNumT16 & 0xF) { numBinOffsets = 4; pMagMap = magMapFor4; } else { numBinOffsets = 3; pMagMap = magMapFor3; } for (i = 0; i < numBinOffsets; i++) { if ((binOffsetNumT16 >> 4) > HAL_MAX_BINS_ALLOWED) { HALDEBUG(ah, HAL_DEBUG_ANY, "Too man bins in spur mitigation\n"); return; } /* Get Pilot Mask values */ curBinOffset = (binOffsetNumT16 >> 4) + i + 25; if ((curBinOffset >= 0) && (curBinOffset <= 32)) { if (curBinOffset <= 25) pilotMask[0] |= 1 << curBinOffset; else if (curBinOffset >= 27) pilotMask[0] |= 1 << (curBinOffset - 1); } else if ((curBinOffset >= 33) && (curBinOffset <= 52)) pilotMask[1] |= 1 << (curBinOffset - 33); /* Get viterbi values */ if ((curBinOffset >= -1) && (curBinOffset <= 14)) binMagMask[0] |= pMagMap[i] << (curBinOffset + 1) * 2; else if ((curBinOffset >= 15) && (curBinOffset <= 30)) binMagMask[1] |= pMagMap[i] << (curBinOffset - 15) * 2; else if ((curBinOffset >= 31) && (curBinOffset <= 46)) binMagMask[2] |= pMagMap[i] << (curBinOffset -31) * 2; else if((curBinOffset >= 47) && (curBinOffset <= 53)) binMagMask[3] |= pMagMap[i] << (curBinOffset -47) * 2; } /* Write Spur Delta Phase, Spur Freq, and enable bits */ OS_REG_RMW_FIELD(ah, AR_PHY_MASK_CTL, AR_PHY_MASK_CTL_RATE, 0xFF); val = OS_REG_READ(ah, AR_PHY_TIMING_CTRL4); val |= (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4, val); OS_REG_WRITE(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_IN_AGC | SM(spurFreqSd, AR_PHY_TIMING11_SPUR_FREQ_SD) | SM(spurDeltaPhase, AR_PHY_TIMING11_SPUR_DELTA_PHASE)); /* Write pilot masks */ OS_REG_WRITE(ah, AR_PHY_TIMING7, pilotMask[0]); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING8, AR_PHY_TIMING8_PILOT_MASK_2, pilotMask[1]); OS_REG_WRITE(ah, AR_PHY_TIMING9, pilotMask[0]); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING10, AR_PHY_TIMING10_PILOT_MASK_2, pilotMask[1]); /* Write magnitude masks */ OS_REG_WRITE(ah, AR_PHY_BIN_MASK_1, binMagMask[0]); OS_REG_WRITE(ah, AR_PHY_BIN_MASK_2, binMagMask[1]); OS_REG_WRITE(ah, AR_PHY_BIN_MASK_3, binMagMask[2]); OS_REG_RMW_FIELD(ah, AR_PHY_MASK_CTL, AR_PHY_MASK_CTL_MASK_4, binMagMask[3]); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_1, binMagMask[0]); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_2, binMagMask[1]); OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_3, binMagMask[2]); OS_REG_RMW_FIELD(ah, AR_PHY_BIN_MASK2_4, AR_PHY_BIN_MASK2_4_MASK_4, binMagMask[3]); } #undef CHAN_TO_SPUR } /* * Delta slope coefficient computation. * Required for OFDM operation. */ void ar5212SetDeltaSlope(struct ath_hal *ah, const struct ieee80211_channel *chan) { #define COEF_SCALE_S 24 #define INIT_CLOCKMHZSCALED 0x64000000 uint16_t freq = ath_hal_gethwchannel(ah, chan); unsigned long coef_scaled, coef_exp, coef_man, ds_coef_exp, ds_coef_man; unsigned long clockMhzScaled = INIT_CLOCKMHZSCALED; if (IEEE80211_IS_CHAN_TURBO(chan)) clockMhzScaled *= 2; /* half and quarter rate can divide the scaled clock by 2 or 4 respectively */ /* scale for selected channel bandwidth */ if (IEEE80211_IS_CHAN_HALF(chan)) { clockMhzScaled = clockMhzScaled >> 1; } else if (IEEE80211_IS_CHAN_QUARTER(chan)) { clockMhzScaled = clockMhzScaled >> 2; } /* * ALGO -> coef = 1e8/fcarrier*fclock/40; * scaled coef to provide precision for this floating calculation */ coef_scaled = clockMhzScaled / freq; /* * ALGO -> coef_exp = 14-floor(log2(coef)); * floor(log2(x)) is the highest set bit position */ for (coef_exp = 31; coef_exp > 0; coef_exp--) if ((coef_scaled >> coef_exp) & 0x1) break; /* A coef_exp of 0 is a legal bit position but an unexpected coef_exp */ HALASSERT(coef_exp); coef_exp = 14 - (coef_exp - COEF_SCALE_S); /* * ALGO -> coef_man = floor(coef* 2^coef_exp+0.5); * The coefficient is already shifted up for scaling */ coef_man = coef_scaled + (1 << (COEF_SCALE_S - coef_exp - 1)); ds_coef_man = coef_man >> (COEF_SCALE_S - coef_exp); ds_coef_exp = coef_exp - 16; OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_MAN, ds_coef_man); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_EXP, ds_coef_exp); #undef INIT_CLOCKMHZSCALED #undef COEF_SCALE_S } /* * Set a limit on the overall output power. Used for dynamic * transmit power control and the like. * * NB: limit is in units of 0.5 dbM. */ HAL_BOOL ar5212SetTxPowerLimit(struct ath_hal *ah, uint32_t limit) { /* XXX blech, construct local writable copy */ struct ieee80211_channel dummy = *AH_PRIVATE(ah)->ah_curchan; uint16_t dummyXpdGains[2]; HAL_BOOL isBmode; SAVE_CCK(ah, &dummy, isBmode); AH_PRIVATE(ah)->ah_powerLimit = AH_MIN(limit, MAX_RATE_POWER); return ar5212SetTransmitPower(ah, &dummy, dummyXpdGains); } /* * Set the transmit power in the baseband for the given * operating channel and mode. */ HAL_BOOL ar5212SetTransmitPower(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t *rfXpdGain) { #define POW_OFDM(_r, _s) (((0 & 1)<< ((_s)+6)) | (((_r) & 0x3f) << (_s))) #define POW_CCK(_r, _s) (((_r) & 0x3f) << (_s)) #define N(a) (sizeof (a) / sizeof (a[0])) static const uint16_t tpcScaleReductionTable[5] = { 0, 3, 6, 9, MAX_RATE_POWER }; struct ath_hal_5212 *ahp = AH5212(ah); uint16_t freq = ath_hal_gethwchannel(ah, chan); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; int16_t minPower, maxPower, tpcInDb, powerLimit; int i; HALASSERT(ah->ah_magic == AR5212_MAGIC); OS_MEMZERO(ahp->ah_pcdacTable, ahp->ah_pcdacTableSize); OS_MEMZERO(ahp->ah_ratesArray, sizeof(ahp->ah_ratesArray)); powerLimit = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit); if (powerLimit >= MAX_RATE_POWER || powerLimit == 0) tpcInDb = tpcScaleReductionTable[AH_PRIVATE(ah)->ah_tpScale]; else tpcInDb = 0; if (!ar5212SetRateTable(ah, chan, tpcInDb, powerLimit, AH_TRUE, &minPower, &maxPower)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set rate table\n", __func__); return AH_FALSE; } if (!ahp->ah_rfHal->setPowerTable(ah, &minPower, &maxPower, chan, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set power table\n", __func__); return AH_FALSE; } /* * Adjust XR power/rate up by 2 dB to account for greater peak * to avg ratio - except in newer avg power designs */ if (!IS_2413(ah) && !IS_5413(ah)) ahp->ah_ratesArray[15] += 4; /* * txPowerIndexOffset is set by the SetPowerTable() call - * adjust the rate table */ for (i = 0; i < N(ahp->ah_ratesArray); i++) { ahp->ah_ratesArray[i] += ahp->ah_txPowerIndexOffset; if (ahp->ah_ratesArray[i] > 63) ahp->ah_ratesArray[i] = 63; } if (ee->ee_eepMap < 2) { /* * Correct gain deltas for 5212 G operation - * Removed with revised chipset */ if (AH_PRIVATE(ah)->ah_phyRev < AR_PHY_CHIP_ID_REV_2 && IEEE80211_IS_CHAN_G(chan)) { uint16_t cckOfdmPwrDelta; if (freq == 2484) cckOfdmPwrDelta = SCALE_OC_DELTA( ee->ee_cckOfdmPwrDelta - ee->ee_scaledCh14FilterCckDelta); else cckOfdmPwrDelta = SCALE_OC_DELTA( ee->ee_cckOfdmPwrDelta); ar5212CorrectGainDelta(ah, cckOfdmPwrDelta); } /* * Finally, write the power values into the * baseband power table */ for (i = 0; i < (PWR_TABLE_SIZE/2); i++) { OS_REG_WRITE(ah, AR_PHY_PCDAC_TX_POWER(i), ((((ahp->ah_pcdacTable[2*i + 1] << 8) | 0xff) & 0xffff) << 16) | (((ahp->ah_pcdacTable[2*i] << 8) | 0xff) & 0xffff) ); } } /* Write the OFDM power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE1, POW_OFDM(ahp->ah_ratesArray[3], 24) | POW_OFDM(ahp->ah_ratesArray[2], 16) | POW_OFDM(ahp->ah_ratesArray[1], 8) | POW_OFDM(ahp->ah_ratesArray[0], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE2, POW_OFDM(ahp->ah_ratesArray[7], 24) | POW_OFDM(ahp->ah_ratesArray[6], 16) | POW_OFDM(ahp->ah_ratesArray[5], 8) | POW_OFDM(ahp->ah_ratesArray[4], 0) ); /* Write the CCK power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, POW_CCK(ahp->ah_ratesArray[10], 24) | POW_CCK(ahp->ah_ratesArray[9], 16) | POW_CCK(ahp->ah_ratesArray[15], 8) /* XR target power */ | POW_CCK(ahp->ah_ratesArray[8], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, POW_CCK(ahp->ah_ratesArray[14], 24) | POW_CCK(ahp->ah_ratesArray[13], 16) | POW_CCK(ahp->ah_ratesArray[12], 8) | POW_CCK(ahp->ah_ratesArray[11], 0) ); /* * Set max power to 30 dBm and, optionally, * enable TPC in tx descriptors. */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER | (ahp->ah_tpcEnabled ? AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE : 0)); return AH_TRUE; #undef N #undef POW_CCK #undef POW_OFDM } /* * Sets the transmit power in the baseband for the given * operating channel and mode. */ static HAL_BOOL ar5212SetRateTable(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t tpcScaleReduction, int16_t powerLimit, HAL_BOOL commit, int16_t *pMinPower, int16_t *pMaxPower) { struct ath_hal_5212 *ahp = AH5212(ah); uint16_t freq = ath_hal_gethwchannel(ah, chan); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; uint16_t *rpow = ahp->ah_ratesArray; uint16_t twiceMaxEdgePower = MAX_RATE_POWER; uint16_t twiceMaxEdgePowerCck = MAX_RATE_POWER; uint16_t twiceMaxRDPower = MAX_RATE_POWER; int i; uint8_t cfgCtl; int8_t twiceAntennaGain, twiceAntennaReduction; const RD_EDGES_POWER *rep; TRGT_POWER_INFO targetPowerOfdm, targetPowerCck; int16_t scaledPower, maxAvailPower = 0; int16_t r13, r9, r7, r0; HALASSERT(ah->ah_magic == AR5212_MAGIC); twiceMaxRDPower = chan->ic_maxregpower * 2; *pMaxPower = -MAX_RATE_POWER; *pMinPower = MAX_RATE_POWER; /* Get conformance test limit maximum for this channel */ cfgCtl = ath_hal_getctl(ah, chan); for (i = 0; i < ee->ee_numCtls; i++) { uint16_t twiceMinEdgePower; if (ee->ee_ctl[i] == 0) continue; if (ee->ee_ctl[i] == cfgCtl || cfgCtl == ((ee->ee_ctl[i] & CTL_MODE_M) | SD_NO_CTL)) { rep = &ee->ee_rdEdgesPower[i * NUM_EDGES]; twiceMinEdgePower = ar5212GetMaxEdgePower(freq, rep); if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { /* Find the minimum of all CTL edge powers that apply to this channel */ twiceMaxEdgePower = AH_MIN(twiceMaxEdgePower, twiceMinEdgePower); } else { twiceMaxEdgePower = twiceMinEdgePower; break; } } } if (IEEE80211_IS_CHAN_G(chan)) { /* Check for a CCK CTL for 11G CCK powers */ cfgCtl = (cfgCtl & ~CTL_MODE_M) | CTL_11B; for (i = 0; i < ee->ee_numCtls; i++) { uint16_t twiceMinEdgePowerCck; if (ee->ee_ctl[i] == 0) continue; if (ee->ee_ctl[i] == cfgCtl || cfgCtl == ((ee->ee_ctl[i] & CTL_MODE_M) | SD_NO_CTL)) { rep = &ee->ee_rdEdgesPower[i * NUM_EDGES]; twiceMinEdgePowerCck = ar5212GetMaxEdgePower(freq, rep); if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { /* Find the minimum of all CTL edge powers that apply to this channel */ twiceMaxEdgePowerCck = AH_MIN(twiceMaxEdgePowerCck, twiceMinEdgePowerCck); } else { twiceMaxEdgePowerCck = twiceMinEdgePowerCck; break; } } } } else { /* Set the 11B cck edge power to the one found before */ twiceMaxEdgePowerCck = twiceMaxEdgePower; } /* Get Antenna Gain reduction */ if (IEEE80211_IS_CHAN_5GHZ(chan)) { ath_hal_eepromGet(ah, AR_EEP_ANTGAINMAX_5, &twiceAntennaGain); } else { ath_hal_eepromGet(ah, AR_EEP_ANTGAINMAX_2, &twiceAntennaGain); } twiceAntennaReduction = ath_hal_getantennareduction(ah, chan, twiceAntennaGain); if (IEEE80211_IS_CHAN_OFDM(chan)) { /* Get final OFDM target powers */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { ar5212GetTargetPowers(ah, chan, ee->ee_trgtPwr_11g, ee->ee_numTargetPwr_11g, &targetPowerOfdm); } else { ar5212GetTargetPowers(ah, chan, ee->ee_trgtPwr_11a, ee->ee_numTargetPwr_11a, &targetPowerOfdm); } /* Get Maximum OFDM power */ /* Minimum of target and edge powers */ scaledPower = AH_MIN(twiceMaxEdgePower, twiceMaxRDPower - twiceAntennaReduction); /* * If turbo is set, reduce power to keep power * consumption under 2 Watts. Note that we always do * this unless specially configured. Then we limit * power only for non-AP operation. */ if (IEEE80211_IS_CHAN_TURBO(chan) #ifdef AH_ENABLE_AP_SUPPORT && AH_PRIVATE(ah)->ah_opmode != HAL_M_HOSTAP #endif ) { /* * If turbo is set, reduce power to keep power * consumption under 2 Watts */ if (ee->ee_version >= AR_EEPROM_VER3_1) scaledPower = AH_MIN(scaledPower, ee->ee_turbo2WMaxPower5); /* * EEPROM version 4.0 added an additional * constraint on 2.4GHz channels. */ if (ee->ee_version >= AR_EEPROM_VER4_0 && IEEE80211_IS_CHAN_2GHZ(chan)) scaledPower = AH_MIN(scaledPower, ee->ee_turbo2WMaxPower2); } maxAvailPower = AH_MIN(scaledPower, targetPowerOfdm.twicePwr6_24); /* Reduce power by max regulatory domain allowed restrictions */ scaledPower = maxAvailPower - (tpcScaleReduction * 2); scaledPower = (scaledPower < 0) ? 0 : scaledPower; scaledPower = AH_MIN(scaledPower, powerLimit); if (commit) { /* Set OFDM rates 9, 12, 18, 24 */ r0 = rpow[0] = rpow[1] = rpow[2] = rpow[3] = rpow[4] = scaledPower; /* Set OFDM rates 36, 48, 54, XR */ rpow[5] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr36); rpow[6] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr48); r7 = rpow[7] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr54); if (ee->ee_version >= AR_EEPROM_VER4_0) { /* Setup XR target power from EEPROM */ rpow[15] = AH_MIN(scaledPower, IEEE80211_IS_CHAN_2GHZ(chan) ? ee->ee_xrTargetPower2 : ee->ee_xrTargetPower5); } else { /* XR uses 6mb power */ rpow[15] = rpow[0]; } ahp->ah_ofdmTxPower = *pMaxPower; } else { r0 = scaledPower; r7 = AH_MIN(r0, targetPowerOfdm.twicePwr54); } *pMinPower = r7; *pMaxPower = r0; HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: MaxRD: %d TurboMax: %d MaxCTL: %d " "TPC_Reduction %d chan=%d (0x%x) maxAvailPower=%d pwr6_24=%d, maxPower=%d\n", __func__, twiceMaxRDPower, ee->ee_turbo2WMaxPower5, twiceMaxEdgePower, tpcScaleReduction * 2, chan->ic_freq, chan->ic_flags, maxAvailPower, targetPowerOfdm.twicePwr6_24, *pMaxPower); } if (IEEE80211_IS_CHAN_CCK(chan)) { /* Get final CCK target powers */ ar5212GetTargetPowers(ah, chan, ee->ee_trgtPwr_11b, ee->ee_numTargetPwr_11b, &targetPowerCck); /* Reduce power by max regulatory domain allowed restrictions */ scaledPower = AH_MIN(twiceMaxEdgePowerCck, twiceMaxRDPower - twiceAntennaReduction); if (maxAvailPower < AH_MIN(scaledPower, targetPowerCck.twicePwr6_24)) maxAvailPower = AH_MIN(scaledPower, targetPowerCck.twicePwr6_24); /* Reduce power by user selection */ scaledPower = AH_MIN(scaledPower, targetPowerCck.twicePwr6_24) - (tpcScaleReduction * 2); scaledPower = (scaledPower < 0) ? 0 : scaledPower; scaledPower = AH_MIN(scaledPower, powerLimit); if (commit) { /* Set CCK rates 2L, 2S, 5.5L, 5.5S, 11L, 11S */ rpow[8] = AH_MIN(scaledPower, targetPowerCck.twicePwr6_24); r9 = rpow[9] = AH_MIN(scaledPower, targetPowerCck.twicePwr36); rpow[10] = rpow[9]; rpow[11] = AH_MIN(scaledPower, targetPowerCck.twicePwr48); rpow[12] = rpow[11]; r13 = rpow[13] = AH_MIN(scaledPower, targetPowerCck.twicePwr54); rpow[14] = rpow[13]; } else { r9 = AH_MIN(scaledPower, targetPowerCck.twicePwr36); r13 = AH_MIN(scaledPower, targetPowerCck.twicePwr54); } /* Set min/max power based off OFDM values or initialization */ if (r13 < *pMinPower) *pMinPower = r13; if (r9 > *pMaxPower) *pMaxPower = r9; HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: cck: MaxRD: %d MaxCTL: %d " "TPC_Reduction %d chan=%d (0x%x) maxAvailPower=%d pwr6_24=%d, maxPower=%d\n", __func__, twiceMaxRDPower, twiceMaxEdgePowerCck, tpcScaleReduction * 2, chan->ic_freq, chan->ic_flags, maxAvailPower, targetPowerCck.twicePwr6_24, *pMaxPower); } if (commit) { ahp->ah_tx6PowerInHalfDbm = *pMaxPower; AH_PRIVATE(ah)->ah_maxPowerLevel = ahp->ah_tx6PowerInHalfDbm; } return AH_TRUE; } HAL_BOOL ar5212GetChipPowerLimits(struct ath_hal *ah, struct ieee80211_channel *chan) { struct ath_hal_5212 *ahp = AH5212(ah); #if 0 static const uint16_t tpcScaleReductionTable[5] = { 0, 3, 6, 9, MAX_RATE_POWER }; int16_t tpcInDb, powerLimit; #endif int16_t minPower, maxPower; /* * Get Pier table max and min powers. */ if (ahp->ah_rfHal->getChannelMaxMinPower(ah, chan, &maxPower, &minPower)) { /* NB: rf code returns 1/4 dBm units, convert */ chan->ic_maxpower = maxPower / 2; chan->ic_minpower = minPower / 2; } else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no min/max power for %u/0x%x\n", __func__, chan->ic_freq, chan->ic_flags); chan->ic_maxpower = MAX_RATE_POWER; chan->ic_minpower = 0; } #if 0 /* * Now adjust to reflect any global scale and/or CTL's. * (XXX is that correct?) */ powerLimit = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit); if (powerLimit >= MAX_RATE_POWER || powerLimit == 0) tpcInDb = tpcScaleReductionTable[AH_PRIVATE(ah)->ah_tpScale]; else tpcInDb = 0; if (!ar5212SetRateTable(ah, chan, tpcInDb, powerLimit, AH_FALSE, &minPower, &maxPower)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to find max/min power\n",__func__); return AH_FALSE; } if (maxPower < chan->ic_maxpower) chan->ic_maxpower = maxPower; if (minPower < chan->ic_minpower) chan->ic_minpower = minPower; HALDEBUG(ah, HAL_DEBUG_RESET, "Chan %d: MaxPow = %d MinPow = %d\n", chan->ic_freq, chan->ic_maxpower, chans->ic_minpower); #endif return AH_TRUE; } /* * Correct for the gain-delta between ofdm and cck mode target * powers. Write the results to the rate table and the power table. * * Conventions : * 1. rpow[ii] is the integer value of 2*(desired power * for the rate ii in dBm) to provide 0.5dB resolution. rate * mapping is as following : * [0..7] --> ofdm 6, 9, .. 48, 54 * [8..14] --> cck 1L, 2L, 2S, .. 11L, 11S * [15] --> XR (all rates get the same power) * 2. powv[ii] is the pcdac corresponding to ii/2 dBm. */ static void ar5212CorrectGainDelta(struct ath_hal *ah, int twiceOfdmCckDelta) { #define N(_a) (sizeof(_a) / sizeof(_a[0])) struct ath_hal_5212 *ahp = AH5212(ah); const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; int16_t ratesIndex[N(ahp->ah_ratesArray)]; uint16_t ii, jj, iter; int32_t cckIndex; int16_t gainDeltaAdjust; HALASSERT(ah->ah_magic == AR5212_MAGIC); gainDeltaAdjust = ee->ee_cckOfdmGainDelta; /* make a local copy of desired powers as initial indices */ OS_MEMCPY(ratesIndex, ahp->ah_ratesArray, sizeof(ratesIndex)); /* fix only the CCK indices */ for (ii = 8; ii < 15; ii++) { /* apply a gain_delta correction of -15 for CCK */ ratesIndex[ii] -= gainDeltaAdjust; /* Now check for contention with all ofdm target powers */ jj = 0; iter = 0; /* indicates not all ofdm rates checked forcontention yet */ while (jj < 16) { if (ratesIndex[ii] < 0) ratesIndex[ii] = 0; if (jj == 8) { /* skip CCK rates */ jj = 15; continue; } if (ratesIndex[ii] == ahp->ah_ratesArray[jj]) { if (ahp->ah_ratesArray[jj] == 0) ratesIndex[ii]++; else if (iter > 50) { /* * To avoid pathological case of of * dm target powers 0 and 0.5dBm */ ratesIndex[ii]++; } else ratesIndex[ii]--; /* check with all rates again */ jj = 0; iter++; } else jj++; } if (ratesIndex[ii] >= PWR_TABLE_SIZE) ratesIndex[ii] = PWR_TABLE_SIZE -1; cckIndex = ahp->ah_ratesArray[ii] - twiceOfdmCckDelta; if (cckIndex < 0) cckIndex = 0; /* * Validate that the indexes for the powv are not * out of bounds. */ HALASSERT(cckIndex < PWR_TABLE_SIZE); HALASSERT(ratesIndex[ii] < PWR_TABLE_SIZE); ahp->ah_pcdacTable[ratesIndex[ii]] = ahp->ah_pcdacTable[cckIndex]; } /* Override rate per power table with new values */ for (ii = 8; ii < 15; ii++) ahp->ah_ratesArray[ii] = ratesIndex[ii]; #undef N } /* * Find the maximum conformance test limit for the given channel and CTL info */ static uint16_t ar5212GetMaxEdgePower(uint16_t channel, const RD_EDGES_POWER *pRdEdgesPower) { /* temp array for holding edge channels */ uint16_t tempChannelList[NUM_EDGES]; uint16_t clo, chi, twiceMaxEdgePower; int i, numEdges; /* Get the edge power */ for (i = 0; i < NUM_EDGES; i++) { if (pRdEdgesPower[i].rdEdge == 0) break; tempChannelList[i] = pRdEdgesPower[i].rdEdge; } numEdges = i; ar5212GetLowerUpperValues(channel, tempChannelList, numEdges, &clo, &chi); /* Get the index for the lower channel */ for (i = 0; i < numEdges && clo != tempChannelList[i]; i++) ; /* Is lower channel ever outside the rdEdge? */ HALASSERT(i != numEdges); if ((clo == chi && clo == channel) || (pRdEdgesPower[i].flag)) { /* * If there's an exact channel match or an inband flag set * on the lower channel use the given rdEdgePower */ twiceMaxEdgePower = pRdEdgesPower[i].twice_rdEdgePower; HALASSERT(twiceMaxEdgePower > 0); } else twiceMaxEdgePower = MAX_RATE_POWER; return twiceMaxEdgePower; } /* * Returns interpolated or the scaled up interpolated value */ static uint16_t interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight, uint16_t targetLeft, uint16_t targetRight) { uint16_t rv; int16_t lRatio; /* to get an accurate ratio, always scale, if want to scale, then don't scale back down */ if ((targetLeft * targetRight) == 0) return 0; if (srcRight != srcLeft) { /* * Note the ratio always need to be scaled, * since it will be a fraction. */ lRatio = (target - srcLeft) * EEP_SCALE / (srcRight - srcLeft); if (lRatio < 0) { /* Return as Left target if value would be negative */ rv = targetLeft; } else if (lRatio > EEP_SCALE) { /* Return as Right target if Ratio is greater than 100% (SCALE) */ rv = targetRight; } else { rv = (lRatio * targetRight + (EEP_SCALE - lRatio) * targetLeft) / EEP_SCALE; } } else { rv = targetLeft; } return rv; } /* * Return the four rates of target power for the given target power table * channel, and number of channels */ static void ar5212GetTargetPowers(struct ath_hal *ah, const struct ieee80211_channel *chan, const TRGT_POWER_INFO *powInfo, uint16_t numChannels, TRGT_POWER_INFO *pNewPower) { uint16_t freq = ath_hal_gethwchannel(ah, chan); /* temp array for holding target power channels */ uint16_t tempChannelList[NUM_TEST_FREQUENCIES]; uint16_t clo, chi, ixlo, ixhi; int i; /* Copy the target powers into the temp channel list */ for (i = 0; i < numChannels; i++) tempChannelList[i] = powInfo[i].testChannel; ar5212GetLowerUpperValues(freq, tempChannelList, numChannels, &clo, &chi); /* Get the indices for the channel */ ixlo = ixhi = 0; for (i = 0; i < numChannels; i++) { if (clo == tempChannelList[i]) { ixlo = i; } if (chi == tempChannelList[i]) { ixhi = i; break; } } /* * Get the lower and upper channels, target powers, * and interpolate between them. */ pNewPower->twicePwr6_24 = interpolate(freq, clo, chi, powInfo[ixlo].twicePwr6_24, powInfo[ixhi].twicePwr6_24); pNewPower->twicePwr36 = interpolate(freq, clo, chi, powInfo[ixlo].twicePwr36, powInfo[ixhi].twicePwr36); pNewPower->twicePwr48 = interpolate(freq, clo, chi, powInfo[ixlo].twicePwr48, powInfo[ixhi].twicePwr48); pNewPower->twicePwr54 = interpolate(freq, clo, chi, powInfo[ixlo].twicePwr54, powInfo[ixhi].twicePwr54); } static uint32_t udiff(uint32_t u, uint32_t v) { return (u >= v ? u - v : v - u); } /* * Search a list for a specified value v that is within * EEP_DELTA of the search values. Return the closest * values in the list above and below the desired value. * EEP_DELTA is a factional value; everything is scaled * so only integer arithmetic is used. * * NB: the input list is assumed to be sorted in ascending order */ void ar5212GetLowerUpperValues(uint16_t v, uint16_t *lp, uint16_t listSize, uint16_t *vlo, uint16_t *vhi) { uint32_t target = v * EEP_SCALE; uint16_t *ep = lp+listSize; /* * Check first and last elements for out-of-bounds conditions. */ if (target < (uint32_t)(lp[0] * EEP_SCALE - EEP_DELTA)) { *vlo = *vhi = lp[0]; return; } if (target > (uint32_t)(ep[-1] * EEP_SCALE + EEP_DELTA)) { *vlo = *vhi = ep[-1]; return; } /* look for value being near or between 2 values in list */ for (; lp < ep; lp++) { /* * If value is close to the current value of the list * then target is not between values, it is one of the values */ if (udiff(lp[0] * EEP_SCALE, target) < EEP_DELTA) { *vlo = *vhi = lp[0]; return; } /* * Look for value being between current value and next value * if so return these 2 values */ if (target < (uint32_t)(lp[1] * EEP_SCALE - EEP_DELTA)) { *vlo = lp[0]; *vhi = lp[1]; return; } } HALASSERT(AH_FALSE); /* should not reach here */ } /* * Perform analog "swizzling" of parameters into their location * * NB: used by RF backends */ void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32, uint32_t numBits, uint32_t firstBit, uint32_t column) { #define MAX_ANALOG_START 319 /* XXX */ uint32_t tmp32, mask, arrayEntry, lastBit; int32_t bitPosition, bitsLeft; HALASSERT(column <= 3); HALASSERT(numBits <= 32); HALASSERT(firstBit + numBits <= MAX_ANALOG_START); tmp32 = ath_hal_reverseBits(reg32, numBits); arrayEntry = (firstBit - 1) / 8; bitPosition = (firstBit - 1) % 8; bitsLeft = numBits; while (bitsLeft > 0) { lastBit = (bitPosition + bitsLeft > 8) ? 8 : bitPosition + bitsLeft; mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) << (column * 8); rfBuf[arrayEntry] &= ~mask; rfBuf[arrayEntry] |= ((tmp32 << bitPosition) << (column * 8)) & mask; bitsLeft -= 8 - bitPosition; tmp32 = tmp32 >> (8 - bitPosition); bitPosition = 0; arrayEntry++; } #undef MAX_ANALOG_START } /* * Sets the rate to duration values in MAC - used for multi- * rate retry. * The rate duration table needs to cover all valid rate codes; * the 11g table covers all ofdm rates, while the 11b table * covers all cck rates => all valid rates get covered between * these two mode's ratetables! * But if we're turbo, the ofdm phy is replaced by the turbo phy * and cck is not valid with turbo => all rates get covered * by the turbo ratetable only */ void ar5212SetRateDurationTable(struct ath_hal *ah, const struct ieee80211_channel *chan) { const HAL_RATE_TABLE *rt; int i; /* NB: band doesn't matter for 1/2 and 1/4 rate */ if (IEEE80211_IS_CHAN_HALF(chan)) { rt = ar5212GetRateTable(ah, HAL_MODE_11A_HALF_RATE); } else if (IEEE80211_IS_CHAN_QUARTER(chan)) { rt = ar5212GetRateTable(ah, HAL_MODE_11A_QUARTER_RATE); } else { rt = ar5212GetRateTable(ah, IEEE80211_IS_CHAN_TURBO(chan) ? HAL_MODE_TURBO : HAL_MODE_11G); } for (i = 0; i < rt->rateCount; ++i) OS_REG_WRITE(ah, AR_RATE_DURATION(rt->info[i].rateCode), ath_hal_computetxtime(ah, rt, WLAN_CTRL_FRAME_SIZE, rt->info[i].controlRate, AH_FALSE)); if (!IEEE80211_IS_CHAN_TURBO(chan)) { /* 11g Table is used to cover the CCK rates. */ rt = ar5212GetRateTable(ah, HAL_MODE_11G); for (i = 0; i < rt->rateCount; ++i) { uint32_t reg = AR_RATE_DURATION(rt->info[i].rateCode); if (rt->info[i].phy != IEEE80211_T_CCK) continue; OS_REG_WRITE(ah, reg, ath_hal_computetxtime(ah, rt, WLAN_CTRL_FRAME_SIZE, rt->info[i].controlRate, AH_FALSE)); /* cck rates have short preamble option also */ if (rt->info[i].shortPreamble) { reg += rt->info[i].shortPreamble << 2; OS_REG_WRITE(ah, reg, ath_hal_computetxtime(ah, rt, WLAN_CTRL_FRAME_SIZE, rt->info[i].controlRate, AH_TRUE)); } } } } /* Adjust various register settings based on half/quarter rate clock setting. * This includes: +USEC, TX/RX latency, * + IFS params: slot, eifs, misc etc. */ void ar5212SetIFSTiming(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t txLat, rxLat, usec, slot, refClock, eifs, init_usec; HALASSERT(IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan)); refClock = OS_REG_READ(ah, AR_USEC) & AR_USEC_USEC32; if (IEEE80211_IS_CHAN_HALF(chan)) { slot = IFS_SLOT_HALF_RATE; rxLat = RX_NON_FULL_RATE_LATENCY << AR5212_USEC_RX_LAT_S; txLat = TX_HALF_RATE_LATENCY << AR5212_USEC_TX_LAT_S; usec = HALF_RATE_USEC; eifs = IFS_EIFS_HALF_RATE; init_usec = INIT_USEC >> 1; } else { /* quarter rate */ slot = IFS_SLOT_QUARTER_RATE; rxLat = RX_NON_FULL_RATE_LATENCY << AR5212_USEC_RX_LAT_S; txLat = TX_QUARTER_RATE_LATENCY << AR5212_USEC_TX_LAT_S; usec = QUARTER_RATE_USEC; eifs = IFS_EIFS_QUARTER_RATE; init_usec = INIT_USEC >> 2; } OS_REG_WRITE(ah, AR_USEC, (usec | refClock | txLat | rxLat)); OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, slot); OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, eifs); OS_REG_RMW_FIELD(ah, AR_D_GBL_IFS_MISC, AR_D_GBL_IFS_MISC_USEC_DURATION, init_usec); } Index: head/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c (revision 280827) +++ head/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c (revision 280828) @@ -1,1000 +1,991 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" /* * XXX this is virtually the same code as for 5212; we reuse * storage in the 5212 state block; need to refactor. */ #include "ah.h" #include "ah_internal.h" #include "ah_desc.h" #include "ar5416/ar5416.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" /* * Anti noise immunity support. We track phy errors and react * to excessive errors by adjusting the noise immunity parameters. */ #define HAL_EP_RND(x, mul) \ ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) #define BEACON_RSSI(ahp) \ HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \ HAL_RSSI_EP_MULTIPLIER) /* * ANI processing tunes radio parameters according to PHY errors * and related information. This is done for for noise and spur * immunity in all operating modes if the device indicates it's * capable at attach time. In addition, when there is a reference * rssi value (e.g. beacon frames from an ap in station mode) * further tuning is done. * * ANI_ENA indicates whether any ANI processing should be done; * this is specified at attach time. * * ANI_ENA_RSSI indicates whether rssi-based processing should * done, this is enabled based on operating mode and is meaningful * only if ANI_ENA is true. * * ANI parameters are typically controlled only by the hal. The * AniControl interface however permits manual tuning through the * diagnostic api. */ #define ANI_ENA(ah) \ (AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA) #define ANI_ENA_RSSI(ah) \ (AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA) #define ah_mibStats ah_stats.ast_mibstats static void enableAniMIBCounters(struct ath_hal *ah, const struct ar5212AniParams *params) { struct ath_hal_5212 *ahp = AH5212(ah); HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Enable mib counters: " "OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n", __func__, params->ofdmPhyErrBase, params->cckPhyErrBase); OS_REG_WRITE(ah, AR_FILTOFDM, 0); OS_REG_WRITE(ah, AR_FILTCCK, 0); OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase); OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase); OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save+clear counters*/ ar5212EnableMibCounters(ah); /* enable everything */ } static void disableAniMIBCounters(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); HALDEBUG(ah, HAL_DEBUG_ANI, "Disable MIB counters\n"); ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save stats */ ar5212DisableMibCounters(ah); /* disable everything */ OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, 0); OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, 0); } static void setPhyErrBase(struct ath_hal *ah, struct ar5212AniParams *params) { if (params->ofdmTrigHigh >= AR_PHY_COUNTMAX) { HALDEBUG(ah, HAL_DEBUG_ANY, "OFDM Trigger %d is too high for hw counters, using max\n", params->ofdmTrigHigh); params->ofdmPhyErrBase = 0; } else params->ofdmPhyErrBase = AR_PHY_COUNTMAX - params->ofdmTrigHigh; if (params->cckTrigHigh >= AR_PHY_COUNTMAX) { HALDEBUG(ah, HAL_DEBUG_ANY, "CCK Trigger %d is too high for hw counters, using max\n", params->cckTrigHigh); params->cckPhyErrBase = 0; } else params->cckPhyErrBase = AR_PHY_COUNTMAX - params->cckTrigHigh; } /* * Setup ANI handling. Sets all thresholds and reset the * channel statistics. Note that ar5416AniReset should be * called by ar5416Reset before anything else happens and * that's where we force initial settings. */ void ar5416AniAttach(struct ath_hal *ah, const struct ar5212AniParams *params24, const struct ar5212AniParams *params5, HAL_BOOL enable) { struct ath_hal_5212 *ahp = AH5212(ah); if (params24 != AH_NULL) { OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24)); setPhyErrBase(ah, &ahp->ah_aniParams24); } if (params5 != AH_NULL) { OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5)); setPhyErrBase(ah, &ahp->ah_aniParams5); } OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani)); /* Enable MIB Counters */ enableAniMIBCounters(ah, &ahp->ah_aniParams24 /*XXX*/); if (enable) { /* Enable ani now */ HALASSERT(params24 != AH_NULL && params5 != AH_NULL); ahp->ah_procPhyErr |= HAL_ANI_ENA; } else { ahp->ah_procPhyErr &= ~HAL_ANI_ENA; } } /* * Cleanup any ANI state setup. * * This doesn't restore registers to their default settings! */ void ar5416AniDetach(struct ath_hal *ah) { HALDEBUG(ah, HAL_DEBUG_ANI, "Detaching Ani\n"); disableAniMIBCounters(ah); } /* * Control Adaptive Noise Immunity Parameters */ HAL_BOOL ar5416AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param) { typedef int TABLE[]; struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212AniState *aniState = ahp->ah_curani; const struct ar5212AniParams *params = AH_NULL; /* * This function may be called before there's a current * channel (eg to disable ANI.) */ if (aniState != AH_NULL) params = aniState->params; OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd); /* These commands can't be disabled */ if (cmd == HAL_ANI_PRESENT) return AH_TRUE; if (cmd == HAL_ANI_MODE) { if (param == 0) { ahp->ah_procPhyErr &= ~HAL_ANI_ENA; /* Turn off HW counters if we have them */ ar5416AniDetach(ah); } else { /* normal/auto mode */ /* don't mess with state if already enabled */ if (! (ahp->ah_procPhyErr & HAL_ANI_ENA)) { /* Enable MIB Counters */ /* * XXX use 2.4ghz params if no channel is * available */ enableAniMIBCounters(ah, ahp->ah_curani != AH_NULL ? ahp->ah_curani->params: &ahp->ah_aniParams24); ahp->ah_procPhyErr |= HAL_ANI_ENA; } } return AH_TRUE; } /* Check whether the particular function is enabled */ if (((1 << cmd) & AH5416(ah)->ah_ani_function) == 0) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: command %d disabled\n", __func__, cmd); HALDEBUG(ah, HAL_DEBUG_ANI, "%s: cmd %d; mask %x\n", __func__, cmd, AH5416(ah)->ah_ani_function); return AH_FALSE; } switch (cmd) { case HAL_ANI_NOISE_IMMUNITY_LEVEL: { u_int level = param; HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_NOISE_IMMUNITY_LEVEL: set level = %d\n", __func__, level); if (level > params->maxNoiseImmunityLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: immunity level out of range (%u > %u)\n", __func__, level, params->maxNoiseImmunityLevel); return AH_FALSE; } OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_TOT_DES, params->totalSizeDesired[level]); OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, AR_PHY_AGC_CTL1_COARSE_LOW, params->coarseLow[level]); OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, AR_PHY_AGC_CTL1_COARSE_HIGH, params->coarseHigh[level]); OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRPWR, params->firpwr[level]); if (level > aniState->noiseImmunityLevel) ahp->ah_stats.ast_ani_niup++; else if (level < aniState->noiseImmunityLevel) ahp->ah_stats.ast_ani_nidown++; aniState->noiseImmunityLevel = level; break; } case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: { static const TABLE m1ThreshLow = { 127, 50 }; static const TABLE m2ThreshLow = { 127, 40 }; static const TABLE m1Thresh = { 127, 0x4d }; static const TABLE m2Thresh = { 127, 0x40 }; static const TABLE m2CountThr = { 31, 16 }; static const TABLE m2CountThrLow = { 63, 48 }; u_int on = param ? 1 : 0; HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: %s\n", __func__, on ? "enabled" : "disabled"); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow[on]); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow[on]); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M1_THRESH, m1Thresh[on]); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2_THRESH, m2Thresh[on]); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2COUNT_THR, m2CountThr[on]); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow[on]); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLow[on]); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLow[on]); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M1_THRESH, m1Thresh[on]); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M2_THRESH, m2Thresh[on]); if (on) { OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); } else { OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); } if (on) ahp->ah_stats.ast_ani_ofdmon++; else ahp->ah_stats.ast_ani_ofdmoff++; aniState->ofdmWeakSigDetectOff = !on; break; } case HAL_ANI_CCK_WEAK_SIGNAL_THR: { static const TABLE weakSigThrCck = { 8, 6 }; u_int high = param ? 1 : 0; HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_CCK_WEAK_SIGNAL_THR: %s\n", __func__, high ? "high" : "low"); OS_REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT, AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, weakSigThrCck[high]); if (high) ahp->ah_stats.ast_ani_cckhigh++; else ahp->ah_stats.ast_ani_ccklow++; aniState->cckWeakSigThreshold = high; break; } case HAL_ANI_FIRSTEP_LEVEL: { u_int level = param; HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_FIRSTEP_LEVEL: level = %d\n", __func__, level); if (level > params->maxFirstepLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: firstep level out of range (%u > %u)\n", __func__, level, params->maxFirstepLevel); return AH_FALSE; } OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP, params->firstep[level]); if (level > aniState->firstepLevel) ahp->ah_stats.ast_ani_stepup++; else if (level < aniState->firstepLevel) ahp->ah_stats.ast_ani_stepdown++; aniState->firstepLevel = level; break; } case HAL_ANI_SPUR_IMMUNITY_LEVEL: { u_int level = param; HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_SPUR_IMMUNITY_LEVEL: level = %d\n", __func__, level); if (level > params->maxSpurImmunityLevel) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: spur immunity level out of range (%u > %u)\n", __func__, level, params->maxSpurImmunityLevel); return AH_FALSE; } OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]); if (level > aniState->spurImmunityLevel) ahp->ah_stats.ast_ani_spurup++; else if (level < aniState->spurImmunityLevel) ahp->ah_stats.ast_ani_spurdown++; aniState->spurImmunityLevel = level; break; } #ifdef AH_PRIVATE_DIAG case HAL_ANI_PHYERR_RESET: ahp->ah_stats.ast_ani_ofdmerrs = 0; ahp->ah_stats.ast_ani_cckerrs = 0; break; #endif /* AH_PRIVATE_DIAG */ default: HALDEBUG(ah, HAL_DEBUG_ANI, "%s: invalid cmd %u\n", __func__, cmd); return AH_FALSE; } return AH_TRUE; } static void ar5416AniOfdmErrTrigger(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; struct ar5212AniState *aniState; const struct ar5212AniParams *params; HALASSERT(chan != AH_NULL); if (!ANI_ENA(ah)) return; aniState = ahp->ah_curani; params = aniState->params; /* First, raise noise immunity level, up to max */ if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) { if (ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel + 1)) return; } /* then, raise spur immunity level, up to max */ if (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel) { if (ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, aniState->spurImmunityLevel + 1)) return; } /* * In the case of AP mode operation, we cannot bucketize beacons * according to RSSI. Instead, raise Firstep level, up to max, and * simply return. */ if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) { if (aniState->firstepLevel < params->maxFirstepLevel) { if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1)) return; } } if (ANI_ENA_RSSI(ah)) { int32_t rssi = BEACON_RSSI(ahp); if (rssi > params->rssiThrHigh) { /* * Beacon rssi is high, can turn off ofdm * weak sig detect. */ if (!aniState->ofdmWeakSigDetectOff) { ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_FALSE); ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); return; } /* * If weak sig detect is already off, as last resort, * raise firstep level */ if (aniState->firstepLevel < params->maxFirstepLevel) { if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1)) return; } } else if (rssi > params->rssiThrLow) { /* * Beacon rssi in mid range, need ofdm weak signal * detect, but we can raise firststepLevel. */ if (aniState->ofdmWeakSigDetectOff) ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_TRUE); if (aniState->firstepLevel < params->maxFirstepLevel) if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1)) return; } else { /* * Beacon rssi is low, if in 11b/g mode, turn off ofdm * weak signal detection and zero firstepLevel to * maximize CCK sensitivity */ if (IEEE80211_IS_CHAN_CCK(chan)) { if (!aniState->ofdmWeakSigDetectOff) ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_FALSE); if (aniState->firstepLevel > 0) if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0)) return; } } } } static void ar5416AniCckErrTrigger(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; struct ar5212AniState *aniState; const struct ar5212AniParams *params; HALASSERT(chan != AH_NULL); if (!ANI_ENA(ah)) return; /* first, raise noise immunity level, up to max */ aniState = ahp->ah_curani; params = aniState->params; if ((AH5416(ah)->ah_ani_function & (1 << HAL_ANI_NOISE_IMMUNITY_LEVEL) && aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel)) { ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel + 1); return; } if (ANI_ENA_RSSI(ah)) { int32_t rssi = BEACON_RSSI(ahp); if (rssi > params->rssiThrLow) { /* * Beacon signal in mid and high range, * raise firstep level. */ if (aniState->firstepLevel < params->maxFirstepLevel) ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1); } else { /* * Beacon rssi is low, zero firstep level to maximize * CCK sensitivity in 11b/g mode. */ if (IEEE80211_IS_CHAN_CCK(chan)) { if (aniState->firstepLevel > 0) ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); } } } } static void ar5416AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState) { struct ath_hal_5212 *ahp = AH5212(ah); const struct ar5212AniParams *params = aniState->params; aniState->listenTime = 0; /* * NB: these are written on reset based on the * ini so we must re-write them! */ HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Writing ofdmbase=%u cckbase=%u\n", __func__, params->ofdmPhyErrBase, params->cckPhyErrBase); OS_REG_WRITE(ah, AR_PHY_ERR_1, params->ofdmPhyErrBase); OS_REG_WRITE(ah, AR_PHY_ERR_2, params->cckPhyErrBase); OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); /* Clear the mib counters and save them in the stats */ ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); aniState->ofdmPhyErrCount = 0; aniState->cckPhyErrCount = 0; } /* * Restore/reset the ANI parameters and reset the statistics. * This routine must be called for every channel change. * * NOTE: This is where ah_curani is set; other ani code assumes * it is setup to reflect the current channel. */ void ar5416AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan, HAL_OPMODE opmode, int restore) { struct ath_hal_5212 *ahp = AH5212(ah); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); /* XXX bounds check ic_devdata */ struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata]; uint32_t rxfilter; if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) { OS_MEMZERO(aniState, sizeof(*aniState)); if (IEEE80211_IS_CHAN_2GHZ(chan)) aniState->params = &ahp->ah_aniParams24; else aniState->params = &ahp->ah_aniParams5; ichan->privFlags |= CHANNEL_ANI_INIT; HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0); } ahp->ah_curani = aniState; #if 0 ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n", __func__, chan->ic_freq, chan->ic_flags, restore, opmode, ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); #else HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n", __func__, chan->ic_freq, chan->ic_flags, restore, opmode, ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); #endif OS_MARK(ah, AH_MARK_ANI_RESET, opmode); /* * Turn off PHY error frame delivery while we futz with settings. */ rxfilter = ah->ah_getRxFilter(ah); ah->ah_setRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR); /* * If ANI is disabled at this point, don't set the default * ANI parameter settings - leave the HAL settings there. * This is (currently) needed for reliable radar detection. */ if (! ANI_ENA(ah)) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: ANI disabled\n", __func__); goto finish; } /* * Use a restrictive set of ANI parameters for hostap mode. */ if (opmode == HAL_M_HOSTAP) { if (IEEE80211_IS_CHAN_2GHZ(chan)) AH5416(ah)->ah_ani_function = HAL_ANI_SPUR_IMMUNITY_LEVEL | HAL_ANI_FIRSTEP_LEVEL; else AH5416(ah)->ah_ani_function = 0; } /* * Automatic processing is done only in station mode right now. */ if (opmode == HAL_M_STA) ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA; else ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA; /* * Set all ani parameters. We either set them to initial * values or restore the previous ones for the channel. * XXX if ANI follows hardware, we don't care what mode we're * XXX in, we should keep the ani parameters */ if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) { ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel); ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, aniState->spurImmunityLevel); ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, !aniState->ofdmWeakSigDetectOff); ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, aniState->cckWeakSigThreshold); ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel); } else { ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0); ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_FALSE); ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE); ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); ichan->privFlags |= CHANNEL_ANI_SETUP; } /* * In case the counters haven't yet been setup; set them up. */ enableAniMIBCounters(ah, aniState->params); ar5416AniRestart(ah, aniState); finish: /* restore RX filter mask */ ah->ah_setRxFilter(ah, rxfilter); } /* * Process a MIB interrupt. We may potentially be invoked because * any of the MIB counters overflow/trigger so don't assume we're * here because a PHY error counter triggered. */ void ar5416ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats) { struct ath_hal_5212 *ahp = AH5212(ah); uint32_t phyCnt1, phyCnt2; HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x " "filtofdm 0x%x filtcck 0x%x\n", __func__, OS_REG_READ(ah, AR_MIBC), OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2), OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK)); /* * First order of business is to clear whatever caused * the interrupt so we don't keep getting interrupted. * We have the usual mib counters that are reset-on-read * and the additional counters that appeared starting in * Hainan. We collect the mib counters and explicitly * zero additional counters we are not using. Anything * else is reset only if it caused the interrupt. */ /* NB: these are not reset-on-read */ phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); /* not used, always reset them in case they are the cause */ OS_REG_WRITE(ah, AR_FILTOFDM, 0); OS_REG_WRITE(ah, AR_FILTCCK, 0); if ((OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING) == 0) OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR); /* Clear the mib counters and save them in the stats */ ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); ahp->ah_stats.ast_nodestats = *stats; /* * Check for an ani stat hitting the trigger threshold. * When this happens we get a MIB interrupt and the top * 2 bits of the counter register will be 0b11, hence * the mask check of phyCnt?. */ if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { struct ar5212AniState *aniState = ahp->ah_curani; const struct ar5212AniParams *params = aniState->params; uint32_t ofdmPhyErrCnt, cckPhyErrCnt; ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; ahp->ah_stats.ast_ani_ofdmerrs += ofdmPhyErrCnt - aniState->ofdmPhyErrCount; aniState->ofdmPhyErrCount = ofdmPhyErrCnt; cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; ahp->ah_stats.ast_ani_cckerrs += cckPhyErrCnt - aniState->cckPhyErrCount; aniState->cckPhyErrCount = cckPhyErrCnt; /* * NB: figure out which counter triggered. If both * trigger we'll only deal with one as the processing * clobbers the error counter so the trigger threshold * check will never be true. */ if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) ar5416AniOfdmErrTrigger(ah); if (aniState->cckPhyErrCount > params->cckTrigHigh) ar5416AniCckErrTrigger(ah); /* NB: always restart to insure the h/w counters are reset */ ar5416AniRestart(ah, aniState); } } static void ar5416AniLowerImmunity(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212AniState *aniState; const struct ar5212AniParams *params; HALASSERT(ANI_ENA(ah)); aniState = ahp->ah_curani; params = aniState->params; /* * In the case of AP mode operation, we cannot bucketize beacons * according to RSSI. Instead, lower Firstep level, down to min, and * simply return. */ if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) { if (aniState->firstepLevel > 0) { if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel - 1)) return; } } if (ANI_ENA_RSSI(ah)) { int32_t rssi = BEACON_RSSI(ahp); if (rssi > params->rssiThrHigh) { /* * Beacon signal is high, leave ofdm weak signal * detection off or it may oscillate. Let it fall * through. */ } else if (rssi > params->rssiThrLow) { /* * Beacon rssi in mid range, turn on ofdm weak signal * detection or lower firstep level. */ if (aniState->ofdmWeakSigDetectOff) { if (ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_TRUE)) return; } if (aniState->firstepLevel > 0) { if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel - 1)) return; } } else { /* * Beacon rssi is low, reduce firstep level. */ if (aniState->firstepLevel > 0) { if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, aniState->firstepLevel - 1)) return; } } } /* then lower spur immunity level, down to zero */ if (aniState->spurImmunityLevel > 0) { if (ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, aniState->spurImmunityLevel - 1)) return; } /* * if all else fails, lower noise immunity level down to a min value * zero for now */ if (aniState->noiseImmunityLevel > 0) { if (ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, aniState->noiseImmunityLevel - 1)) return; } } #define CLOCK_RATE 44000 /* XXX use mac_usec or similar */ /* convert HW counter values to ms using 11g clock rate, goo9d enough for 11a and Turbo */ /* * Return an approximation of the time spent ``listening'' by * deducting the cycles spent tx'ing and rx'ing from the total * cycle count since our last call. A return value <0 indicates * an invalid/inconsistent time. * * This may be called with ANI disabled; in which case simply keep * the statistics and don't write to the aniState pointer. * * XXX TODO: Make this cleaner! */ static int32_t ar5416AniGetListenTime(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212AniState *aniState = NULL; int32_t listenTime = 0; int good; HAL_SURVEY_SAMPLE hs; - HAL_CHANNEL_SURVEY *cs = AH_NULL; /* * We shouldn't see ah_curchan be NULL, but just in case.. */ if (AH_PRIVATE(ah)->ah_curchan == AH_NULL) { ath_hal_printf(ah, "%s: ah_curchan = NULL?\n", __func__); return (0); } - cs = &ahp->ah_chansurvey; - /* * Fetch the current statistics, squirrel away the current - * sample, bump the sequence/sample counter. + * sample. */ OS_MEMZERO(&hs, sizeof(hs)); good = ar5416GetMibCycleCounts(ah, &hs); - if (cs != AH_NULL) { - OS_MEMCPY(&cs->samples[cs->cur_sample], &hs, sizeof(hs)); - cs->samples[cs->cur_sample].seq_num = cs->cur_seq; - cs->cur_sample = - (cs->cur_sample + 1) % CHANNEL_SURVEY_SAMPLE_COUNT; - cs->cur_seq++; - } + ath_hal_survey_add_sample(ah, &hs); if (ANI_ENA(ah)) aniState = ahp->ah_curani; if (good == AH_FALSE) { /* * Cycle counter wrap (or initial call); it's not possible * to accurately calculate a value because the registers * right shift rather than wrap--so punt and return 0. */ listenTime = 0; ahp->ah_stats.ast_ani_lzero++; } else if (ANI_ENA(ah)) { /* * Only calculate and update the cycle count if we have * an ANI state. */ int32_t ccdelta = AH5416(ah)->ah_cycleCount - aniState->cycleCount; int32_t rfdelta = AH5416(ah)->ah_rxBusy - aniState->rxFrameCount; int32_t tfdelta = AH5416(ah)->ah_txBusy - aniState->txFrameCount; listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE; } /* * Again, only update ANI state if we have it. */ if (ANI_ENA(ah)) { aniState->cycleCount = AH5416(ah)->ah_cycleCount; aniState->rxFrameCount = AH5416(ah)->ah_rxBusy; aniState->txFrameCount = AH5416(ah)->ah_txBusy; } return listenTime; } /* * Update ani stats in preparation for listen time processing. */ static void updateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState) { struct ath_hal_5212 *ahp = AH5212(ah); const struct ar5212AniParams *params = aniState->params; uint32_t phyCnt1, phyCnt2; int32_t ofdmPhyErrCnt, cckPhyErrCnt; /* Clear the mib counters and save them in the stats */ ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* NB: these are not reset-on-read */ phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); /* NB: these are spec'd to never roll-over */ ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; if (ofdmPhyErrCnt < 0) { HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n", ofdmPhyErrCnt, phyCnt1); ofdmPhyErrCnt = AR_PHY_COUNTMAX; } ahp->ah_stats.ast_ani_ofdmerrs += ofdmPhyErrCnt - aniState->ofdmPhyErrCount; aniState->ofdmPhyErrCount = ofdmPhyErrCnt; cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; if (cckPhyErrCnt < 0) { HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n", cckPhyErrCnt, phyCnt2); cckPhyErrCnt = AR_PHY_COUNTMAX; } ahp->ah_stats.ast_ani_cckerrs += cckPhyErrCnt - aniState->cckPhyErrCount; aniState->cckPhyErrCount = cckPhyErrCnt; } void ar5416RxMonitor(struct ath_hal *ah, const HAL_NODE_STATS *stats, const struct ieee80211_channel *chan) { struct ath_hal_5212 *ahp = AH5212(ah); ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi; } /* * Do periodic processing. This routine is called from the * driver's rx interrupt handler after processing frames. */ void ar5416AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan) { struct ath_hal_5212 *ahp = AH5212(ah); struct ar5212AniState *aniState = ahp->ah_curani; const struct ar5212AniParams *params; int32_t listenTime; /* Always update from the MIB, for statistics gathering */ listenTime = ar5416AniGetListenTime(ah); /* XXX can aniState be null? */ if (aniState == AH_NULL) return; if (!ANI_ENA(ah)) return; if (listenTime < 0) { ahp->ah_stats.ast_ani_lneg++; /* restart ANI period if listenTime is invalid */ HALDEBUG(ah, HAL_DEBUG_ANI, "%s: invalid listenTime\n", __func__); ar5416AniRestart(ah, aniState); } /* XXX beware of overflow? */ aniState->listenTime += listenTime; OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime); params = aniState->params; if (aniState->listenTime > 5*params->period) { /* * Check to see if need to lower immunity if * 5 aniPeriods have passed */ updateMIBStats(ah, aniState); if (aniState->ofdmPhyErrCount <= aniState->listenTime * params->ofdmTrigLow/1000 && aniState->cckPhyErrCount <= aniState->listenTime * params->cckTrigLow/1000) ar5416AniLowerImmunity(ah); HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower immunity\n", __func__); ar5416AniRestart(ah, aniState); } else if (aniState->listenTime > params->period) { updateMIBStats(ah, aniState); /* check to see if need to raise immunity */ if (aniState->ofdmPhyErrCount > aniState->listenTime * params->ofdmTrigHigh / 1000) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: OFDM err %u listenTime %u\n", __func__, aniState->ofdmPhyErrCount, aniState->listenTime); ar5416AniOfdmErrTrigger(ah); ar5416AniRestart(ah, aniState); } else if (aniState->cckPhyErrCount > aniState->listenTime * params->cckTrigHigh / 1000) { HALDEBUG(ah, HAL_DEBUG_ANI, "%s: CCK err %u listenTime %u\n", __func__, aniState->cckPhyErrCount, aniState->listenTime); ar5416AniCckErrTrigger(ah); ar5416AniRestart(ah, aniState); } } } Index: head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c =================================================================== --- head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c (revision 280827) +++ head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c (revision 280828) @@ -1,2888 +1,2889 @@ /* * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * Copyright (c) 2002-2008 Atheros Communications, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_eeprom_v14.h" #include "ar5416/ar5416.h" #include "ar5416/ar5416reg.h" #include "ar5416/ar5416phy.h" /* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */ #define EEP_MINOR(_ah) \ (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK) #define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2) #define IS_EEP_MINOR_V3(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3) /* Additional Time delay to wait after activiting the Base band */ #define BASE_ACTIVATE_DELAY 100 /* 100 usec */ #define PLL_SETTLE_DELAY 300 /* 300 usec */ #define RTC_PLL_SETTLE_DELAY 1000 /* 1 ms */ static void ar5416InitDMA(struct ath_hal *ah); static void ar5416InitBB(struct ath_hal *ah, const struct ieee80211_channel *); static void ar5416InitIMR(struct ath_hal *ah, HAL_OPMODE opmode); static void ar5416InitQoS(struct ath_hal *ah); static void ar5416InitUserSettings(struct ath_hal *ah); static void ar5416OverrideIni(struct ath_hal *ah, const struct ieee80211_channel *); #if 0 static HAL_BOOL ar5416ChannelChange(struct ath_hal *, const struct ieee80211_channel *); #endif static void ar5416SetDeltaSlope(struct ath_hal *, const struct ieee80211_channel *); static HAL_BOOL ar5416SetResetPowerOn(struct ath_hal *ah); static HAL_BOOL ar5416SetReset(struct ath_hal *ah, int type); static HAL_BOOL ar5416SetPowerPerRateTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *ratesArray, uint16_t cfgCtl, uint16_t AntennaReduction, uint16_t twiceMaxRegulatoryPower, uint16_t powerLimit); static void ar5416Set11nRegs(struct ath_hal *ah, const struct ieee80211_channel *chan); static void ar5416MarkPhyInactive(struct ath_hal *ah); static void ar5416SetIFSTiming(struct ath_hal *ah, const struct ieee80211_channel *chan); /* * Places the device in and out of reset and then places sane * values in the registers based on EEPROM config, initialization * vectors (as determined by the mode), and station configuration * * bChannelChange is used to preserve DMA/PCU registers across * a HW Reset during channel change. */ HAL_BOOL ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_BOOL bChannelChange, HAL_STATUS *status) { #define N(a) (sizeof (a) / sizeof (a[0])) #define FAIL(_code) do { ecode = _code; goto bad; } while (0) struct ath_hal_5212 *ahp = AH5212(ah); HAL_CHANNEL_INTERNAL *ichan; uint32_t saveDefAntenna, saveLedState; uint32_t macStaId1; uint16_t rfXpdGain[2]; HAL_STATUS ecode; uint32_t powerVal, rssiThrReg; uint32_t ackTpcPow, ctsTpcPow, chirpTpcPow; int i; uint64_t tsf = 0; OS_MARK(ah, AH_MARK_RESET, bChannelChange); /* Bring out of sleep mode */ if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip did not wakeup\n", __func__); FAIL(HAL_EIO); } /* * Map public channel to private. */ ichan = ath_hal_checkchannel(ah, chan); if (ichan == AH_NULL) FAIL(HAL_EINVAL); switch (opmode) { case HAL_M_STA: case HAL_M_IBSS: case HAL_M_HOSTAP: case HAL_M_MONITOR: break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid operating mode %u\n", __func__, opmode); FAIL(HAL_EINVAL); break; } HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); /* Blank the channel survey statistics */ - OS_MEMZERO(&ahp->ah_chansurvey, sizeof(ahp->ah_chansurvey)); + ath_hal_survey_clear(ah); /* XXX Turn on fast channel change for 5416 */ + /* * Preserve the bmiss rssi threshold and count threshold * across resets */ rssiThrReg = OS_REG_READ(ah, AR_RSSI_THR); /* If reg is zero, first time thru set to default val */ if (rssiThrReg == 0) rssiThrReg = INIT_RSSI_THR; /* * Preserve the antenna on a channel change */ saveDefAntenna = OS_REG_READ(ah, AR_DEF_ANTENNA); /* * Don't do this for the AR9285 - it breaks RX for single * antenna designs when diversity is disabled. * * I'm not sure what this was working around; it may be * something to do with the AR5416. Certainly this register * isn't supposed to be used by the MIMO chips for anything * except for defining the default antenna when an external * phase array / smart antenna is connected. * * See PR: kern/179269 . */ if ((! AR_SREV_KITE(ah)) && saveDefAntenna == 0) /* XXX magic constants */ saveDefAntenna = 1; /* Save hardware flag before chip reset clears the register */ macStaId1 = OS_REG_READ(ah, AR_STA_ID1) & (AR_STA_ID1_BASE_RATE_11B | AR_STA_ID1_USE_DEFANT); /* Save led state from pci config register */ saveLedState = OS_REG_READ(ah, AR_MAC_LED) & (AR_MAC_LED_ASSOC | AR_MAC_LED_MODE | AR_MAC_LED_BLINK_THRESH_SEL | AR_MAC_LED_BLINK_SLOW); /* For chips on which the RTC reset is done, save TSF before it gets cleared */ if (AR_SREV_HOWL(ah) || (AR_SREV_MERLIN(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) || (ah->ah_config.ah_force_full_reset)) tsf = ar5416GetTsf64(ah); /* Mark PHY as inactive; marked active in ar5416InitBB() */ ar5416MarkPhyInactive(ah); if (!ar5416ChipReset(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__); FAIL(HAL_EIO); } /* Restore TSF */ if (tsf) ar5416SetTsf64(ah, tsf); OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); if (AR_SREV_MERLIN_10_OR_LATER(ah)) OS_REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); AH5416(ah)->ah_writeIni(ah, chan); if(AR_SREV_KIWI_13_OR_LATER(ah) ) { /* Enable ASYNC FIFO */ OS_REG_SET_BIT(ah, AR_MAC_PCU_ASYNC_FIFO_REG3, AR_MAC_PCU_ASYNC_FIFO_REG3_DATAPATH_SEL); OS_REG_SET_BIT(ah, AR_PHY_MODE, AR_PHY_MODE_ASYNCFIFO); OS_REG_CLR_BIT(ah, AR_MAC_PCU_ASYNC_FIFO_REG3, AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET); OS_REG_SET_BIT(ah, AR_MAC_PCU_ASYNC_FIFO_REG3, AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET); } /* Override ini values (that can be overriden in this fashion) */ ar5416OverrideIni(ah, chan); /* Setup 11n MAC/Phy mode registers */ ar5416Set11nRegs(ah, chan); OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); /* * Some AR91xx SoC devices frequently fail to accept TSF writes * right after the chip reset. When that happens, write a new * value after the initvals have been applied, with an offset * based on measured time difference */ if (AR_SREV_HOWL(ah) && (ar5416GetTsf64(ah) < tsf)) { tsf += 1500; ar5416SetTsf64(ah, tsf); } HALDEBUG(ah, HAL_DEBUG_RESET, ">>>2 %s: AR_PHY_DAG_CTRLCCK=0x%x\n", __func__, OS_REG_READ(ah,AR_PHY_DAG_CTRLCCK)); HALDEBUG(ah, HAL_DEBUG_RESET, ">>>2 %s: AR_PHY_ADC_CTL=0x%x\n", __func__, OS_REG_READ(ah,AR_PHY_ADC_CTL)); /* * This routine swaps the analog chains - it should be done * before any radio register twiddling is done. */ ar5416InitChainMasks(ah); /* Setup the open-loop power calibration if required */ if (ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { AH5416(ah)->ah_olcInit(ah); AH5416(ah)->ah_olcTempCompensation(ah); } /* Setup the transmit power values. */ if (!ah->ah_setTxPower(ah, chan, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error init'ing transmit power\n", __func__); FAIL(HAL_EIO); } /* Write the analog registers */ if (!ahp->ah_rfHal->setRfRegs(ah, chan, IEEE80211_IS_CHAN_2GHZ(chan) ? 2: 1, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: ar5212SetRfRegs failed\n", __func__); FAIL(HAL_EIO); } /* Write delta slope for OFDM enabled modes (A, G, Turbo) */ if (IEEE80211_IS_CHAN_OFDM(chan)|| IEEE80211_IS_CHAN_HT(chan)) ar5416SetDeltaSlope(ah, chan); AH5416(ah)->ah_spurMitigate(ah, chan); /* Setup board specific options for EEPROM version 3 */ if (!ah->ah_setBoardValues(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error setting board options\n", __func__); FAIL(HAL_EIO); } OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); OS_REG_WRITE(ah, AR_STA_ID0, LE_READ_4(ahp->ah_macaddr)); OS_REG_WRITE(ah, AR_STA_ID1, LE_READ_2(ahp->ah_macaddr + 4) | macStaId1 | AR_STA_ID1_RTS_USE_DEF | ahp->ah_staId1Defaults ); ar5212SetOperatingMode(ah, opmode); /* Set Venice BSSID mask according to current state */ OS_REG_WRITE(ah, AR_BSSMSKL, LE_READ_4(ahp->ah_bssidmask)); OS_REG_WRITE(ah, AR_BSSMSKU, LE_READ_2(ahp->ah_bssidmask + 4)); /* Restore previous led state */ if (AR_SREV_HOWL(ah)) OS_REG_WRITE(ah, AR_MAC_LED, AR_MAC_LED_ASSOC_ACTIVE | AR_CFG_SCLK_32KHZ); else OS_REG_WRITE(ah, AR_MAC_LED, OS_REG_READ(ah, AR_MAC_LED) | saveLedState); /* Start TSF2 for generic timer 8-15 */ #ifdef NOTYET if (AR_SREV_KIWI(ah)) ar5416StartTsf2(ah); #endif /* * Enable Bluetooth Coexistence if it's enabled. */ if (AH5416(ah)->ah_btCoexConfigType != HAL_BT_COEX_CFG_NONE) ar5416InitBTCoex(ah); /* Restore previous antenna */ OS_REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna); /* then our BSSID and associate id */ OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4) | (ahp->ah_assocId & 0x3fff) << AR_BSS_ID1_AID_S); /* Restore bmiss rssi & count thresholds */ OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr); OS_REG_WRITE(ah, AR_ISR, ~0); /* cleared on write */ /* Restore bmiss rssi & count thresholds */ OS_REG_WRITE(ah, AR_RSSI_THR, rssiThrReg); if (!ar5212SetChannel(ah, chan)) FAIL(HAL_EIO); OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); /* Set 1:1 QCU to DCU mapping for all queues */ for (i = 0; i < AR_NUM_DCU; i++) OS_REG_WRITE(ah, AR_DQCUMASK(i), 1 << i); ahp->ah_intrTxqs = 0; for (i = 0; i < AH_PRIVATE(ah)->ah_caps.halTotalQueues; i++) ah->ah_resetTxQueue(ah, i); ar5416InitIMR(ah, opmode); ar5416SetCoverageClass(ah, AH_PRIVATE(ah)->ah_coverageClass, 1); ar5416InitQoS(ah); /* This may override the AR_DIAG_SW register */ ar5416InitUserSettings(ah); /* XXX this won't work for AR9287! */ if (IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan)) { ar5416SetIFSTiming(ah, chan); #if 0 /* * AR5413? * Force window_length for 1/2 and 1/4 rate channels, * the ini file sets this to zero otherwise. */ OS_REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_WINLEN, 3); } #endif } if (AR_SREV_KIWI_13_OR_LATER(ah)) { /* * Enable ASYNC FIFO * * If Async FIFO is enabled, the following counters change * as MAC now runs at 117 Mhz instead of 88/44MHz when * async FIFO is disabled. * * Overwrite the delay/timeouts initialized in ProcessIni() * above. */ OS_REG_WRITE(ah, AR_D_GBL_IFS_SIFS, AR_D_GBL_IFS_SIFS_ASYNC_FIFO_DUR); OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, AR_D_GBL_IFS_SLOT_ASYNC_FIFO_DUR); OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, AR_D_GBL_IFS_EIFS_ASYNC_FIFO_DUR); OS_REG_WRITE(ah, AR_TIME_OUT, AR_TIME_OUT_ACK_CTS_ASYNC_FIFO_DUR); OS_REG_WRITE(ah, AR_USEC, AR_USEC_ASYNC_FIFO_DUR); OS_REG_SET_BIT(ah, AR_MAC_PCU_LOGIC_ANALYZER, AR_MAC_PCU_LOGIC_ANALYZER_DISBUG20768); OS_REG_RMW_FIELD(ah, AR_AHB_MODE, AR_AHB_CUSTOM_BURST_EN, AR_AHB_CUSTOM_BURST_ASYNC_FIFO_VAL); } if (AR_SREV_KIWI_13_OR_LATER(ah)) { /* Enable AGGWEP to accelerate encryption engine */ OS_REG_SET_BIT(ah, AR_PCU_MISC_MODE2, AR_PCU_MISC_MODE2_ENABLE_AGGWEP); } /* * disable seq number generation in hw */ OS_REG_WRITE(ah, AR_STA_ID1, OS_REG_READ(ah, AR_STA_ID1) | AR_STA_ID1_PRESERVE_SEQNUM); ar5416InitDMA(ah); /* * program OBS bus to see MAC interrupts */ OS_REG_WRITE(ah, AR_OBS, 8); /* * Disable the "general" TX/RX mitigation timers. */ OS_REG_WRITE(ah, AR_MIRT, 0); #ifdef AH_AR5416_INTERRUPT_MITIGATION /* * This initialises the RX interrupt mitigation timers. * * The mitigation timers begin at idle and are triggered * upon the RXOK of a single frame (or sub-frame, for A-MPDU.) * Then, the RX mitigation interrupt will fire: * * + 250uS after the last RX'ed frame, or * + 700uS after the first RX'ed frame * * Thus, the LAST field dictates the extra latency * induced by the RX mitigation method and the FIRST * field dictates how long to delay before firing an * RX mitigation interrupt. * * Please note this only seems to be for RXOK frames; * not CRC or PHY error frames. * */ OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 250); OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, 700); #endif ar5416InitBB(ah, chan); /* Setup compression registers */ ar5212SetCompRegs(ah); /* XXX not needed? */ /* * 5416 baseband will check the per rate power table * and select the lower of the two */ ackTpcPow = 63; ctsTpcPow = 63; chirpTpcPow = 63; powerVal = SM(ackTpcPow, AR_TPC_ACK) | SM(ctsTpcPow, AR_TPC_CTS) | SM(chirpTpcPow, AR_TPC_CHIRP); OS_REG_WRITE(ah, AR_TPC, powerVal); if (!ar5416InitCal(ah, chan)) FAIL(HAL_ESELFTEST); ar5416RestoreChainMask(ah); AH_PRIVATE(ah)->ah_opmode = opmode; /* record operating mode */ if (bChannelChange && !IEEE80211_IS_CHAN_DFS(chan)) chan->ic_state &= ~IEEE80211_CHANSTATE_CWINT; if (AR_SREV_HOWL(ah)) { /* * Enable the MBSSID block-ack fix for HOWL. * This feature is only supported on Howl 1.4, but it is safe to * set bit 22 of STA_ID1 on other Howl revisions (1.1, 1.2, 1.3), * since bit 22 is unused in those Howl revisions. */ unsigned int reg; reg = (OS_REG_READ(ah, AR_STA_ID1) | (1<<22)); OS_REG_WRITE(ah,AR_STA_ID1, reg); ath_hal_printf(ah, "MBSSID Set bit 22 of AR_STA_ID 0x%x\n", reg); } HALDEBUG(ah, HAL_DEBUG_RESET, "%s: done\n", __func__); OS_MARK(ah, AH_MARK_RESET_DONE, 0); return AH_TRUE; bad: OS_MARK(ah, AH_MARK_RESET_DONE, ecode); if (status != AH_NULL) *status = ecode; return AH_FALSE; #undef FAIL #undef N } #if 0 /* * This channel change evaluates whether the selected hardware can * perform a synthesizer-only channel change (no reset). If the * TX is not stopped, or the RFBus cannot be granted in the given * time, the function returns false as a reset is necessary */ HAL_BOOL ar5416ChannelChange(struct ath_hal *ah, const structu ieee80211_channel *chan) { uint32_t ulCount; uint32_t data, synthDelay, qnum; uint16_t rfXpdGain[4]; struct ath_hal_5212 *ahp = AH5212(ah); HAL_CHANNEL_INTERNAL *ichan; /* * Map public channel to private. */ ichan = ath_hal_checkchannel(ah, chan); /* TX must be stopped or RF Bus grant will not work */ for (qnum = 0; qnum < AH_PRIVATE(ah)->ah_caps.halTotalQueues; qnum++) { if (ar5212NumTxPending(ah, qnum)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: frames pending on queue %d\n", __func__, qnum); return AH_FALSE; } } /* * Kill last Baseband Rx Frame - Request analog bus grant */ OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_REQUEST); if (!ath_hal_wait(ah, AR_PHY_RFBUS_GNT, AR_PHY_RFBUS_GRANT_EN, AR_PHY_RFBUS_GRANT_EN)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: could not kill baseband rx\n", __func__); return AH_FALSE; } ar5416Set11nRegs(ah, chan); /* NB: setup 5416-specific regs */ /* Change the synth */ if (!ar5212SetChannel(ah, chan)) return AH_FALSE; /* Setup the transmit power values. */ if (!ah->ah_setTxPower(ah, chan, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error init'ing transmit power\n", __func__); return AH_FALSE; } /* * Wait for the frequency synth to settle (synth goes on * via PHY_ACTIVE_EN). Read the phy active delay register. * Value is in 100ns increments. */ data = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; if (IS_CHAN_CCK(ichan)) { synthDelay = (4 * data) / 22; } else { synthDelay = data / 10; } OS_DELAY(synthDelay + BASE_ACTIVATE_DELAY); /* Release the RFBus Grant */ OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); /* Write delta slope for OFDM enabled modes (A, G, Turbo) */ if (IEEE80211_IS_CHAN_OFDM(ichan)|| IEEE80211_IS_CHAN_HT(chan)) { HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER5_3); ar5212SetSpurMitigation(ah, chan); ar5416SetDeltaSlope(ah, chan); } /* XXX spur mitigation for Melin */ if (!IEEE80211_IS_CHAN_DFS(chan)) chan->ic_state &= ~IEEE80211_CHANSTATE_CWINT; ichan->channel_time = 0; ichan->tsf_last = ar5416GetTsf64(ah); ar5212TxEnable(ah, AH_TRUE); return AH_TRUE; } #endif static void ar5416InitDMA(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); /* * set AHB_MODE not to do cacheline prefetches */ OS_REG_SET_BIT(ah, AR_AHB_MODE, AR_AHB_PREFETCH_RD_EN); /* * let mac dma reads be in 128 byte chunks */ OS_REG_WRITE(ah, AR_TXCFG, (OS_REG_READ(ah, AR_TXCFG) & ~AR_TXCFG_DMASZ_MASK) | AR_TXCFG_DMASZ_128B); /* * let mac dma writes be in 128 byte chunks */ /* * XXX If you change this, you must change the headroom * assigned in ah_maxTxTrigLev - see ar5416InitState(). */ OS_REG_WRITE(ah, AR_RXCFG, (OS_REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_DMASZ_MASK) | AR_RXCFG_DMASZ_128B); /* restore TX trigger level */ OS_REG_WRITE(ah, AR_TXCFG, (OS_REG_READ(ah, AR_TXCFG) &~ AR_FTRIG) | SM(ahp->ah_txTrigLev, AR_FTRIG)); /* * Setup receive FIFO threshold to hold off TX activities */ OS_REG_WRITE(ah, AR_RXFIFO_CFG, 0x200); /* * reduce the number of usable entries in PCU TXBUF to avoid * wrap around. */ if (AR_SREV_KITE(ah)) /* * For AR9285 the number of Fifos are reduced to half. * So set the usable tx buf size also to half to * avoid data/delimiter underruns */ OS_REG_WRITE(ah, AR_PCU_TXBUF_CTRL, AR_9285_PCU_TXBUF_CTRL_USABLE_SIZE); else OS_REG_WRITE(ah, AR_PCU_TXBUF_CTRL, AR_PCU_TXBUF_CTRL_USABLE_SIZE); } static void ar5416InitBB(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t synthDelay; /* * Wait for the frequency synth to settle (synth goes on * via AR_PHY_ACTIVE_EN). Read the phy active delay register. * Value is in 100ns increments. */ synthDelay = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; if (IEEE80211_IS_CHAN_CCK(chan)) { synthDelay = (4 * synthDelay) / 22; } else { synthDelay /= 10; } /* Turn on PLL on 5416 */ HALDEBUG(ah, HAL_DEBUG_RESET, "%s %s channel\n", __func__, IEEE80211_IS_CHAN_5GHZ(chan) ? "5GHz" : "2GHz"); /* Activate the PHY (includes baseband activate and synthesizer on) */ OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); /* * If the AP starts the calibration before the base band timeout * completes we could get rx_clear false triggering. Add an * extra BASE_ACTIVATE_DELAY usecs to ensure this condition * does not happen. */ if (IEEE80211_IS_CHAN_HALF(chan)) { OS_DELAY((synthDelay << 1) + BASE_ACTIVATE_DELAY); } else if (IEEE80211_IS_CHAN_QUARTER(chan)) { OS_DELAY((synthDelay << 2) + BASE_ACTIVATE_DELAY); } else { OS_DELAY(synthDelay + BASE_ACTIVATE_DELAY); } } static void ar5416InitIMR(struct ath_hal *ah, HAL_OPMODE opmode) { struct ath_hal_5212 *ahp = AH5212(ah); /* * Setup interrupt handling. Note that ar5212ResetTxQueue * manipulates the secondary IMR's as queues are enabled * and disabled. This is done with RMW ops to insure the * settings we make here are preserved. */ ahp->ah_maskReg = AR_IMR_TXERR | AR_IMR_TXURN | AR_IMR_RXERR | AR_IMR_RXORN | AR_IMR_BCNMISC; #ifdef AH_AR5416_INTERRUPT_MITIGATION ahp->ah_maskReg |= AR_IMR_RXINTM | AR_IMR_RXMINTR; #else ahp->ah_maskReg |= AR_IMR_RXOK; #endif ahp->ah_maskReg |= AR_IMR_TXOK; if (opmode == HAL_M_HOSTAP) ahp->ah_maskReg |= AR_IMR_MIB; OS_REG_WRITE(ah, AR_IMR, ahp->ah_maskReg); #ifdef ADRIAN_NOTYET /* This is straight from ath9k */ if (! AR_SREV_HOWL(ah)) { OS_REG_WRITE(ah, AR_INTR_SYNC_CAUSE, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_INTR_SYNC_ENABLE, AR_INTR_SYNC_DEFAULT); OS_REG_WRITE(ah, AR_INTR_SYNC_MASK, 0); } #endif /* Enable bus errors that are OR'd to set the HIUERR bit */ #if 0 OS_REG_WRITE(ah, AR_IMR_S2, OS_REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_GTT | AR_IMR_S2_CST); #endif } static void ar5416InitQoS(struct ath_hal *ah) { /* QoS support */ OS_REG_WRITE(ah, AR_QOS_CONTROL, 0x100aa); /* XXX magic */ OS_REG_WRITE(ah, AR_QOS_SELECT, 0x3210); /* XXX magic */ /* Turn on NOACK Support for QoS packets */ OS_REG_WRITE(ah, AR_NOACK, SM(2, AR_NOACK_2BIT_VALUE) | SM(5, AR_NOACK_BIT_OFFSET) | SM(0, AR_NOACK_BYTE_OFFSET)); /* * initialize TXOP for all TIDs */ OS_REG_WRITE(ah, AR_TXOP_X, AR_TXOP_X_VAL); OS_REG_WRITE(ah, AR_TXOP_0_3, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_4_7, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_8_11, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF); } static void ar5416InitUserSettings(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); /* Restore user-specified settings */ if (ahp->ah_miscMode != 0) OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode); if (ahp->ah_sifstime != (u_int) -1) ar5212SetSifsTime(ah, ahp->ah_sifstime); if (ahp->ah_slottime != (u_int) -1) ar5212SetSlotTime(ah, ahp->ah_slottime); if (ahp->ah_acktimeout != (u_int) -1) ar5212SetAckTimeout(ah, ahp->ah_acktimeout); if (ahp->ah_ctstimeout != (u_int) -1) ar5212SetCTSTimeout(ah, ahp->ah_ctstimeout); if (AH_PRIVATE(ah)->ah_diagreg != 0) OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg); if (AH5416(ah)->ah_globaltxtimeout != (u_int) -1) ar5416SetGlobalTxTimeout(ah, AH5416(ah)->ah_globaltxtimeout); } static void ar5416SetRfMode(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t rfMode; if (chan == AH_NULL) return; /* treat channel B as channel G , no B mode suport in owl */ rfMode = IEEE80211_IS_CHAN_CCK(chan) ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; if (AR_SREV_MERLIN_20(ah) && IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { /* phy mode bits for 5GHz channels require Fast Clock */ rfMode |= AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE; } else if (!AR_SREV_MERLIN_10_OR_LATER(ah)) { rfMode |= IEEE80211_IS_CHAN_5GHZ(chan) ? AR_PHY_MODE_RF5GHZ : AR_PHY_MODE_RF2GHZ; } OS_REG_WRITE(ah, AR_PHY_MODE, rfMode); } /* * Places the hardware into reset and then pulls it out of reset */ HAL_BOOL ar5416ChipReset(struct ath_hal *ah, const struct ieee80211_channel *chan) { OS_MARK(ah, AH_MARK_CHIPRESET, chan ? chan->ic_freq : 0); /* * Warm reset is optimistic for open-loop TX power control. */ if (AR_SREV_MERLIN(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) return AH_FALSE; } else if (ah->ah_config.ah_force_full_reset) { if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) return AH_FALSE; } else { if (!ar5416SetResetReg(ah, HAL_RESET_WARM)) return AH_FALSE; } /* Bring out of sleep mode (AGAIN) */ if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) return AH_FALSE; #ifdef notyet ahp->ah_chipFullSleep = AH_FALSE; #endif AH5416(ah)->ah_initPLL(ah, chan); /* * Perform warm reset before the mode/PLL/turbo registers * are changed in order to deactivate the radio. Mode changes * with an active radio can result in corrupted shifts to the * radio device. */ ar5416SetRfMode(ah, chan); return AH_TRUE; } /* * Delta slope coefficient computation. * Required for OFDM operation. */ static void ar5416GetDeltaSlopeValues(struct ath_hal *ah, uint32_t coef_scaled, uint32_t *coef_mantissa, uint32_t *coef_exponent) { #define COEF_SCALE_S 24 uint32_t coef_exp, coef_man; /* * ALGO -> coef_exp = 14-floor(log2(coef)); * floor(log2(x)) is the highest set bit position */ for (coef_exp = 31; coef_exp > 0; coef_exp--) if ((coef_scaled >> coef_exp) & 0x1) break; /* A coef_exp of 0 is a legal bit position but an unexpected coef_exp */ HALASSERT(coef_exp); coef_exp = 14 - (coef_exp - COEF_SCALE_S); /* * ALGO -> coef_man = floor(coef* 2^coef_exp+0.5); * The coefficient is already shifted up for scaling */ coef_man = coef_scaled + (1 << (COEF_SCALE_S - coef_exp - 1)); *coef_mantissa = coef_man >> (COEF_SCALE_S - coef_exp); *coef_exponent = coef_exp - 16; #undef COEF_SCALE_S } void ar5416SetDeltaSlope(struct ath_hal *ah, const struct ieee80211_channel *chan) { #define INIT_CLOCKMHZSCALED 0x64000000 uint32_t coef_scaled, ds_coef_exp, ds_coef_man; uint32_t clockMhzScaled; CHAN_CENTERS centers; /* half and quarter rate can divide the scaled clock by 2 or 4 respectively */ /* scale for selected channel bandwidth */ clockMhzScaled = INIT_CLOCKMHZSCALED; if (IEEE80211_IS_CHAN_TURBO(chan)) clockMhzScaled <<= 1; else if (IEEE80211_IS_CHAN_HALF(chan)) clockMhzScaled >>= 1; else if (IEEE80211_IS_CHAN_QUARTER(chan)) clockMhzScaled >>= 2; /* * ALGO -> coef = 1e8/fcarrier*fclock/40; * scaled coef to provide precision for this floating calculation */ ar5416GetChannelCenters(ah, chan, ¢ers); coef_scaled = clockMhzScaled / centers.synth_center; ar5416GetDeltaSlopeValues(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_MAN, ds_coef_man); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_EXP, ds_coef_exp); /* * For Short GI, * scaled coeff is 9/10 that of normal coeff */ coef_scaled = (9 * coef_scaled)/10; ar5416GetDeltaSlopeValues(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); /* for short gi */ OS_REG_RMW_FIELD(ah, AR_PHY_HALFGI, AR_PHY_HALFGI_DSC_MAN, ds_coef_man); OS_REG_RMW_FIELD(ah, AR_PHY_HALFGI, AR_PHY_HALFGI_DSC_EXP, ds_coef_exp); #undef INIT_CLOCKMHZSCALED } /* * Set a limit on the overall output power. Used for dynamic * transmit power control and the like. * * NB: limit is in units of 0.5 dbM. */ HAL_BOOL ar5416SetTxPowerLimit(struct ath_hal *ah, uint32_t limit) { uint16_t dummyXpdGains[2]; AH_PRIVATE(ah)->ah_powerLimit = AH_MIN(limit, MAX_RATE_POWER); return ah->ah_setTxPower(ah, AH_PRIVATE(ah)->ah_curchan, dummyXpdGains); } HAL_BOOL ar5416GetChipPowerLimits(struct ath_hal *ah, struct ieee80211_channel *chan) { struct ath_hal_5212 *ahp = AH5212(ah); int16_t minPower, maxPower; /* * Get Pier table max and min powers. */ if (ahp->ah_rfHal->getChannelMaxMinPower(ah, chan, &maxPower, &minPower)) { /* NB: rf code returns 1/4 dBm units, convert */ chan->ic_maxpower = maxPower / 2; chan->ic_minpower = minPower / 2; } else { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no min/max power for %u/0x%x\n", __func__, chan->ic_freq, chan->ic_flags); chan->ic_maxpower = AR5416_MAX_RATE_POWER; chan->ic_minpower = 0; } HALDEBUG(ah, HAL_DEBUG_RESET, "Chan %d: MaxPow = %d MinPow = %d\n", chan->ic_freq, chan->ic_maxpower, chan->ic_minpower); return AH_TRUE; } /************************************************************** * ar5416WriteTxPowerRateRegisters * * Write the TX power rate registers from the raw values given * in ratesArray[]. * * The CCK and HT40 rate registers are only written if needed. * HT20 and 11g/11a OFDM rate registers are always written. * * The values written are raw values which should be written * to the registers - so it's up to the caller to pre-adjust * them (eg CCK power offset value, or Merlin TX power offset, * etc.) */ void ar5416WriteTxPowerRateRegisters(struct ath_hal *ah, const struct ieee80211_channel *chan, const int16_t ratesArray[]) { #define POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) /* Write the OFDM power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE1, POW_SM(ratesArray[rate18mb], 24) | POW_SM(ratesArray[rate12mb], 16) | POW_SM(ratesArray[rate9mb], 8) | POW_SM(ratesArray[rate6mb], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE2, POW_SM(ratesArray[rate54mb], 24) | POW_SM(ratesArray[rate48mb], 16) | POW_SM(ratesArray[rate36mb], 8) | POW_SM(ratesArray[rate24mb], 0) ); if (IEEE80211_IS_CHAN_2GHZ(chan)) { /* Write the CCK power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, POW_SM(ratesArray[rate2s], 24) | POW_SM(ratesArray[rate2l], 16) | POW_SM(ratesArray[rateXr], 8) /* XR target power */ | POW_SM(ratesArray[rate1l], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, POW_SM(ratesArray[rate11s], 24) | POW_SM(ratesArray[rate11l], 16) | POW_SM(ratesArray[rate5_5s], 8) | POW_SM(ratesArray[rate5_5l], 0) ); HALDEBUG(ah, HAL_DEBUG_RESET, "%s AR_PHY_POWER_TX_RATE3=0x%x AR_PHY_POWER_TX_RATE4=0x%x\n", __func__, OS_REG_READ(ah,AR_PHY_POWER_TX_RATE3), OS_REG_READ(ah,AR_PHY_POWER_TX_RATE4)); } /* Write the HT20 power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE5, POW_SM(ratesArray[rateHt20_3], 24) | POW_SM(ratesArray[rateHt20_2], 16) | POW_SM(ratesArray[rateHt20_1], 8) | POW_SM(ratesArray[rateHt20_0], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE6, POW_SM(ratesArray[rateHt20_7], 24) | POW_SM(ratesArray[rateHt20_6], 16) | POW_SM(ratesArray[rateHt20_5], 8) | POW_SM(ratesArray[rateHt20_4], 0) ); if (IEEE80211_IS_CHAN_HT40(chan)) { /* Write the HT40 power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE7, POW_SM(ratesArray[rateHt40_3], 24) | POW_SM(ratesArray[rateHt40_2], 16) | POW_SM(ratesArray[rateHt40_1], 8) | POW_SM(ratesArray[rateHt40_0], 0) ); OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE8, POW_SM(ratesArray[rateHt40_7], 24) | POW_SM(ratesArray[rateHt40_6], 16) | POW_SM(ratesArray[rateHt40_5], 8) | POW_SM(ratesArray[rateHt40_4], 0) ); /* Write the Dup/Ext 40 power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE9, POW_SM(ratesArray[rateExtOfdm], 24) | POW_SM(ratesArray[rateExtCck], 16) | POW_SM(ratesArray[rateDupOfdm], 8) | POW_SM(ratesArray[rateDupCck], 0) ); } /* * Set max power to 30 dBm and, optionally, * enable TPC in tx descriptors. */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER | (AH5212(ah)->ah_tpcEnabled ? AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE : 0)); #undef POW_SM } /************************************************************** * ar5416SetTransmitPower * * Set the transmit power in the baseband for the given * operating channel and mode. */ HAL_BOOL ar5416SetTransmitPower(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t *rfXpdGain) { #define N(a) (sizeof (a) / sizeof (a[0])) #define POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) MODAL_EEP_HEADER *pModal; struct ath_hal_5212 *ahp = AH5212(ah); int16_t txPowerIndexOffset = 0; int i; uint16_t cfgCtl; uint16_t powerLimit; uint16_t twiceAntennaReduction; uint16_t twiceMaxRegulatoryPower; int16_t maxPower; HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom; struct ar5416eeprom *pEepData = &ee->ee_base; HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); /* * Default to 2, is overridden based on the EEPROM version / value. */ AH5416(ah)->ah_ht40PowerIncForPdadc = 2; /* Setup info for the actual eeprom */ OS_MEMZERO(AH5416(ah)->ah_ratesArray, sizeof(AH5416(ah)->ah_ratesArray)); cfgCtl = ath_hal_getctl(ah, chan); powerLimit = chan->ic_maxregpower * 2; twiceAntennaReduction = chan->ic_maxantgain; twiceMaxRegulatoryPower = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit); pModal = &pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)]; HALDEBUG(ah, HAL_DEBUG_RESET, "%s Channel=%u CfgCtl=%u\n", __func__,chan->ic_freq, cfgCtl ); if (IS_EEP_MINOR_V2(ah)) { AH5416(ah)->ah_ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; } if (!ar5416SetPowerPerRateTable(ah, pEepData, chan, &AH5416(ah)->ah_ratesArray[0], cfgCtl, twiceAntennaReduction, twiceMaxRegulatoryPower, powerLimit)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set tx power per rate table\n", __func__); return AH_FALSE; } if (!AH5416(ah)->ah_setPowerCalTable(ah, pEepData, chan, &txPowerIndexOffset)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set power table\n", __func__); return AH_FALSE; } maxPower = AH_MAX(AH5416(ah)->ah_ratesArray[rate6mb], AH5416(ah)->ah_ratesArray[rateHt20_0]); if (IEEE80211_IS_CHAN_2GHZ(chan)) { maxPower = AH_MAX(maxPower, AH5416(ah)->ah_ratesArray[rate1l]); } if (IEEE80211_IS_CHAN_HT40(chan)) { maxPower = AH_MAX(maxPower, AH5416(ah)->ah_ratesArray[rateHt40_0]); } ahp->ah_tx6PowerInHalfDbm = maxPower; AH_PRIVATE(ah)->ah_maxPowerLevel = maxPower; ahp->ah_txPowerIndexOffset = txPowerIndexOffset; /* * txPowerIndexOffset is set by the SetPowerTable() call - * adjust the rate table (0 offset if rates EEPROM not loaded) */ for (i = 0; i < N(AH5416(ah)->ah_ratesArray); i++) { AH5416(ah)->ah_ratesArray[i] = (int16_t)(txPowerIndexOffset + AH5416(ah)->ah_ratesArray[i]); if (AH5416(ah)->ah_ratesArray[i] > AR5416_MAX_RATE_POWER) AH5416(ah)->ah_ratesArray[i] = AR5416_MAX_RATE_POWER; } #ifdef AH_EEPROM_DUMP /* * Dump the rate array whilst it represents the intended dBm*2 * values versus what's being adjusted before being programmed * in. Keep this in mind if you code up this function and enable * this debugging; the values won't necessarily be what's being * programmed into the hardware. */ ar5416PrintPowerPerRate(ah, AH5416(ah)->ah_ratesArray); #endif /* * Merlin and later have a power offset, so subtract * pwr_table_offset * 2 from each value. The default * power offset is -5 dBm - ie, a register value of 0 * equates to a TX power of -5 dBm. */ if (AR_SREV_MERLIN_20_OR_LATER(ah)) { int8_t pwr_table_offset; (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset); /* Underflow power gets clamped at raw value 0 */ /* Overflow power gets camped at AR5416_MAX_RATE_POWER */ for (i = 0; i < N(AH5416(ah)->ah_ratesArray); i++) { /* * + pwr_table_offset is in dBm * + ratesArray is in 1/2 dBm */ AH5416(ah)->ah_ratesArray[i] -= (pwr_table_offset * 2); if (AH5416(ah)->ah_ratesArray[i] < 0) AH5416(ah)->ah_ratesArray[i] = 0; else if (AH5416(ah)->ah_ratesArray[i] > AR5416_MAX_RATE_POWER) AH5416(ah)->ah_ratesArray[i] = AR5416_MAX_RATE_POWER; } } /* * Adjust rates for OLC where needed * * The following CCK rates need adjusting when doing 2.4ghz * CCK transmission. * * + rate2s, rate2l, rate1l, rate11s, rate11l, rate5_5s, rate5_5l * + rateExtCck, rateDupCck * * They're adjusted here regardless. The hardware then gets * programmed as needed. 5GHz operation doesn't program in CCK * rates for legacy mode but they seem to be initialised for * HT40 regardless of channel type. */ if (AR_SREV_MERLIN_20_OR_LATER(ah) && ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { int adj[] = { rate2s, rate2l, rate1l, rate11s, rate11l, rate5_5s, rate5_5l, rateExtCck, rateDupCck }; int cck_ofdm_delta = 2; int i; for (i = 0; i < N(adj); i++) { AH5416(ah)->ah_ratesArray[adj[i]] -= cck_ofdm_delta; if (AH5416(ah)->ah_ratesArray[adj[i]] < 0) AH5416(ah)->ah_ratesArray[adj[i]] = 0; } } /* * Adjust the HT40 power to meet the correct target TX power * for 40MHz mode, based on TX power curves that are established * for 20MHz mode. * * XXX handle overflow/too high power level? */ if (IEEE80211_IS_CHAN_HT40(chan)) { AH5416(ah)->ah_ratesArray[rateHt40_0] += AH5416(ah)->ah_ht40PowerIncForPdadc; AH5416(ah)->ah_ratesArray[rateHt40_1] += AH5416(ah)->ah_ht40PowerIncForPdadc; AH5416(ah)->ah_ratesArray[rateHt40_2] += AH5416(ah)->ah_ht40PowerIncForPdadc; AH5416(ah)->ah_ratesArray[rateHt40_3] += AH5416(ah)->ah_ht40PowerIncForPdadc; AH5416(ah)->ah_ratesArray[rateHt40_4] += AH5416(ah)->ah_ht40PowerIncForPdadc; AH5416(ah)->ah_ratesArray[rateHt40_5] += AH5416(ah)->ah_ht40PowerIncForPdadc; AH5416(ah)->ah_ratesArray[rateHt40_6] += AH5416(ah)->ah_ht40PowerIncForPdadc; AH5416(ah)->ah_ratesArray[rateHt40_7] += AH5416(ah)->ah_ht40PowerIncForPdadc; } /* Write the TX power rate registers */ ar5416WriteTxPowerRateRegisters(ah, chan, AH5416(ah)->ah_ratesArray); /* Write the Power subtraction for dynamic chain changing, for per-packet powertx */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_SUB, POW_SM(pModal->pwrDecreaseFor3Chain, 6) | POW_SM(pModal->pwrDecreaseFor2Chain, 0) ); return AH_TRUE; #undef POW_SM #undef N } /* * Exported call to check for a recent gain reading and return * the current state of the thermal calibration gain engine. */ HAL_RFGAIN ar5416GetRfgain(struct ath_hal *ah) { return (HAL_RFGAIN_INACTIVE); } /* * Places all of hardware into reset */ HAL_BOOL ar5416Disable(struct ath_hal *ah) { if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) return AH_FALSE; if (! ar5416SetResetReg(ah, HAL_RESET_COLD)) return AH_FALSE; AH5416(ah)->ah_initPLL(ah, AH_NULL); return (AH_TRUE); } /* * Places the PHY and Radio chips into reset. A full reset * must be called to leave this state. The PCI/MAC/PCU are * not placed into reset as we must receive interrupt to * re-enable the hardware. */ HAL_BOOL ar5416PhyDisable(struct ath_hal *ah) { if (! ar5416SetResetReg(ah, HAL_RESET_WARM)) return AH_FALSE; AH5416(ah)->ah_initPLL(ah, AH_NULL); return (AH_TRUE); } /* * Write the given reset bit mask into the reset register */ HAL_BOOL ar5416SetResetReg(struct ath_hal *ah, uint32_t type) { /* * Set force wake */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); switch (type) { case HAL_RESET_POWER_ON: return ar5416SetResetPowerOn(ah); case HAL_RESET_WARM: case HAL_RESET_COLD: return ar5416SetReset(ah, type); default: HALASSERT(AH_FALSE); return AH_FALSE; } } static HAL_BOOL ar5416SetResetPowerOn(struct ath_hal *ah) { /* Power On Reset (Hard Reset) */ /* * Set force wake * * If the MAC was running, previously calling * reset will wake up the MAC but it may go back to sleep * before we can start polling. * Set force wake stops that * This must be called before initiating a hard reset. */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); /* * PowerOn reset can be used in open loop power control or failure recovery. * If we do RTC reset while DMA is still running, hardware may corrupt memory. * Therefore, we need to reset AHB first to stop DMA. */ if (! AR_SREV_HOWL(ah)) OS_REG_WRITE(ah, AR_RC, AR_RC_AHB); /* * RTC reset and clear */ OS_REG_WRITE(ah, AR_RTC_RESET, 0); OS_DELAY(20); if (! AR_SREV_HOWL(ah)) OS_REG_WRITE(ah, AR_RC, 0); OS_REG_WRITE(ah, AR_RTC_RESET, 1); /* * Poll till RTC is ON */ if (!ath_hal_wait(ah, AR_RTC_STATUS, AR_RTC_PM_STATUS_M, AR_RTC_STATUS_ON)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RTC not waking up\n", __func__); return AH_FALSE; } return ar5416SetReset(ah, HAL_RESET_COLD); } static HAL_BOOL ar5416SetReset(struct ath_hal *ah, int type) { uint32_t tmpReg, mask; uint32_t rst_flags; #ifdef AH_SUPPORT_AR9130 /* Because of the AR9130 specific registers */ if (AR_SREV_HOWL(ah)) { HALDEBUG(ah, HAL_DEBUG_ANY, "[ath] HOWL: Fiddling with derived clk!\n"); uint32_t val = OS_REG_READ(ah, AR_RTC_DERIVED_CLK); val &= ~AR_RTC_DERIVED_CLK_PERIOD; val |= SM(1, AR_RTC_DERIVED_CLK_PERIOD); OS_REG_WRITE(ah, AR_RTC_DERIVED_CLK, val); (void) OS_REG_READ(ah, AR_RTC_DERIVED_CLK); } #endif /* AH_SUPPORT_AR9130 */ /* * Force wake */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); #ifdef AH_SUPPORT_AR9130 if (AR_SREV_HOWL(ah)) { rst_flags = AR_RTC_RC_MAC_WARM | AR_RTC_RC_MAC_COLD | AR_RTC_RC_COLD_RESET | AR_RTC_RC_WARM_RESET; } else { #endif /* AH_SUPPORT_AR9130 */ /* * Reset AHB * * (In case the last interrupt source was a bus timeout.) * XXX TODO: this is not the way to do it! It should be recorded * XXX by the interrupt handler and passed _into_ the * XXX reset path routine so this occurs. */ tmpReg = OS_REG_READ(ah, AR_INTR_SYNC_CAUSE); if (tmpReg & (AR_INTR_SYNC_LOCAL_TIMEOUT|AR_INTR_SYNC_RADM_CPL_TIMEOUT)) { OS_REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0); OS_REG_WRITE(ah, AR_RC, AR_RC_AHB|AR_RC_HOSTIF); } else { OS_REG_WRITE(ah, AR_RC, AR_RC_AHB); } rst_flags = AR_RTC_RC_MAC_WARM; if (type == HAL_RESET_COLD) rst_flags |= AR_RTC_RC_MAC_COLD; #ifdef AH_SUPPORT_AR9130 } #endif /* AH_SUPPORT_AR9130 */ OS_REG_WRITE(ah, AR_RTC_RC, rst_flags); if (AR_SREV_HOWL(ah)) OS_DELAY(10000); else OS_DELAY(100); /* * Clear resets and force wakeup */ OS_REG_WRITE(ah, AR_RTC_RC, 0); if (!ath_hal_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RTC stuck in MAC reset\n", __func__); return AH_FALSE; } /* Clear AHB reset */ if (! AR_SREV_HOWL(ah)) OS_REG_WRITE(ah, AR_RC, 0); if (AR_SREV_HOWL(ah)) OS_DELAY(50); if (AR_SREV_HOWL(ah)) { uint32_t mask; mask = OS_REG_READ(ah, AR_CFG); if (mask & (AR_CFG_SWRB | AR_CFG_SWTB | AR_CFG_SWRG)) { HALDEBUG(ah, HAL_DEBUG_RESET, "CFG Byte Swap Set 0x%x\n", mask); } else { mask = INIT_CONFIG_STATUS | AR_CFG_SWRB | AR_CFG_SWTB; OS_REG_WRITE(ah, AR_CFG, mask); HALDEBUG(ah, HAL_DEBUG_RESET, "Setting CFG 0x%x\n", OS_REG_READ(ah, AR_CFG)); } } else { if (type == HAL_RESET_COLD) { if (isBigEndian()) { /* * Set CFG, little-endian for descriptor accesses. */ mask = INIT_CONFIG_STATUS | AR_CFG_SWRD; #ifndef AH_NEED_DESC_SWAP mask |= AR_CFG_SWTD; #endif HALDEBUG(ah, HAL_DEBUG_RESET, "%s Applying descriptor swap\n", __func__); OS_REG_WRITE(ah, AR_CFG, mask); } else OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS); } } return AH_TRUE; } void ar5416InitChainMasks(struct ath_hal *ah) { int rx_chainmask = AH5416(ah)->ah_rx_chainmask; /* Flip this for this chainmask regardless of chip */ if (rx_chainmask == 0x5) OS_REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); /* * Workaround for OWL 1.0 calibration failure; enable multi-chain; * then set true mask after calibration. */ if (IS_5416V1(ah) && (rx_chainmask == 0x5 || rx_chainmask == 0x3)) { OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, 0x7); OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7); } else { OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, AH5416(ah)->ah_rx_chainmask); OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, AH5416(ah)->ah_rx_chainmask); } OS_REG_WRITE(ah, AR_SELFGEN_MASK, AH5416(ah)->ah_tx_chainmask); if (AH5416(ah)->ah_tx_chainmask == 0x5) OS_REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); if (AR_SREV_HOWL(ah)) { OS_REG_WRITE(ah, AR_PHY_ANALOG_SWAP, OS_REG_READ(ah, AR_PHY_ANALOG_SWAP) | 0x00000001); } } /* * Work-around for Owl 1.0 calibration failure. * * ar5416InitChainMasks sets the RX chainmask to 0x7 if it's Owl 1.0 * due to init calibration failures. ar5416RestoreChainMask restores * these registers to the correct setting. */ void ar5416RestoreChainMask(struct ath_hal *ah) { int rx_chainmask = AH5416(ah)->ah_rx_chainmask; if (IS_5416V1(ah) && (rx_chainmask == 0x5 || rx_chainmask == 0x3)) { OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); } } void ar5416InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t pll = AR_RTC_PLL_REFDIV_5 | AR_RTC_PLL_DIV2; if (chan != AH_NULL) { if (IEEE80211_IS_CHAN_HALF(chan)) pll |= SM(0x1, AR_RTC_PLL_CLKSEL); else if (IEEE80211_IS_CHAN_QUARTER(chan)) pll |= SM(0x2, AR_RTC_PLL_CLKSEL); if (IEEE80211_IS_CHAN_5GHZ(chan)) pll |= SM(0xa, AR_RTC_PLL_DIV); else pll |= SM(0xb, AR_RTC_PLL_DIV); } else pll |= SM(0xb, AR_RTC_PLL_DIV); OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, pll); /* TODO: * For multi-band owl, switch between bands by reiniting the PLL. */ OS_DELAY(RTC_PLL_SETTLE_DELAY); OS_REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_SLEEP_DERIVED_CLK); } static void ar5416SetDefGainValues(struct ath_hal *ah, const MODAL_EEP_HEADER *pModal, const struct ar5416eeprom *eep, uint8_t txRxAttenLocal, int regChainOffset, int i) { if (IS_EEP_MINOR_V3(ah)) { txRxAttenLocal = pModal->txRxAttenCh[i]; if (AR_SREV_MERLIN_10_OR_LATER(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[i]); OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[i]); OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, pModal->xatten2Margin[i]); OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[i]); } else { OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_BSW_MARGIN, pModal->bswMargin[i]); OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_BSW_ATTEN, pModal->bswAtten[i]); } } if (AR_SREV_MERLIN_10_OR_LATER(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[i]); } else { OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, AR_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, AR_PHY_GAIN_2GHZ_RXTX_MARGIN, pModal->rxTxMarginCh[i]); } } /* * Get the register chain offset for the given chain. * * Take into account the register chain swapping with AR5416 v2.0. * * XXX make sure that the reg chain swapping is only done for * XXX AR5416 v2.0 or greater, and not later chips? */ int ar5416GetRegChainOffset(struct ath_hal *ah, int i) { int regChainOffset; if (AR_SREV_5416_V20_OR_LATER(ah) && (AH5416(ah)->ah_rx_chainmask == 0x5 || AH5416(ah)->ah_tx_chainmask == 0x5) && (i != 0)) { /* Regs are swapped from chain 2 to 1 for 5416 2_0 with * only chains 0 and 2 populated */ regChainOffset = (i == 1) ? 0x2000 : 0x1000; } else { regChainOffset = i * 0x1000; } return regChainOffset; } /* * Read EEPROM header info and program the device for correct operation * given the channel value. */ HAL_BOOL ar5416SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) { const HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom; const struct ar5416eeprom *eep = &ee->ee_base; const MODAL_EEP_HEADER *pModal; int i, regChainOffset; uint8_t txRxAttenLocal; /* workaround for eeprom versions <= 14.2 */ HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); pModal = &eep->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)]; /* NB: workaround for eeprom versions <= 14.2 */ txRxAttenLocal = IEEE80211_IS_CHAN_2GHZ(chan) ? 23 : 44; OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, pModal->antCtrlCommon); for (i = 0; i < AR5416_MAX_CHAINS; i++) { if (AR_SREV_MERLIN(ah)) { if (i >= 2) break; } regChainOffset = ar5416GetRegChainOffset(ah, i); OS_REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset, pModal->antCtrlChain[i]); OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4 + regChainOffset, (OS_REG_READ(ah, AR_PHY_TIMING_CTRL4 + regChainOffset) & ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | SM(pModal->iqCalICh[i], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | SM(pModal->iqCalQCh[i], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); /* * Large signal upgrade, * If 14.3 or later EEPROM, use * txRxAttenLocal = pModal->txRxAttenCh[i] * else txRxAttenLocal is fixed value above. */ if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) ar5416SetDefGainValues(ah, pModal, eep, txRxAttenLocal, regChainOffset, i); } if (AR_SREV_MERLIN_10_OR_LATER(ah)) { if (IEEE80211_IS_CHAN_2GHZ(chan)) { OS_A_REG_RMW_FIELD(ah, AR_AN_RF2G1_CH0, AR_AN_RF2G1_CH0_OB, pModal->ob); OS_A_REG_RMW_FIELD(ah, AR_AN_RF2G1_CH0, AR_AN_RF2G1_CH0_DB, pModal->db); OS_A_REG_RMW_FIELD(ah, AR_AN_RF2G1_CH1, AR_AN_RF2G1_CH1_OB, pModal->ob_ch1); OS_A_REG_RMW_FIELD(ah, AR_AN_RF2G1_CH1, AR_AN_RF2G1_CH1_DB, pModal->db_ch1); } else { OS_A_REG_RMW_FIELD(ah, AR_AN_RF5G1_CH0, AR_AN_RF5G1_CH0_OB5, pModal->ob); OS_A_REG_RMW_FIELD(ah, AR_AN_RF5G1_CH0, AR_AN_RF5G1_CH0_DB5, pModal->db); OS_A_REG_RMW_FIELD(ah, AR_AN_RF5G1_CH1, AR_AN_RF5G1_CH1_OB5, pModal->ob_ch1); OS_A_REG_RMW_FIELD(ah, AR_AN_RF5G1_CH1, AR_AN_RF5G1_CH1_DB5, pModal->db_ch1); } OS_A_REG_RMW_FIELD(ah, AR_AN_TOP2, AR_AN_TOP2_XPABIAS_LVL, pModal->xpaBiasLvl); OS_A_REG_RMW_FIELD(ah, AR_AN_TOP2, AR_AN_TOP2_LOCALBIAS, !!(pModal->flagBits & AR5416_EEP_FLAG_LOCALBIAS)); OS_A_REG_RMW_FIELD(ah, AR_PHY_XPA_CFG, AR_PHY_FORCE_XPA_CFG, !!(pModal->flagBits & AR5416_EEP_FLAG_FORCEXPAON)); } OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->switchSettling); OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, pModal->adcDesiredSize); if (! AR_SREV_MERLIN_10_OR_LATER(ah)) OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_PGA, pModal->pgaDesiredSize); OS_REG_WRITE(ah, AR_PHY_RF_CTL4, SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) | SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) | SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON) | SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON)); OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, pModal->txEndToRxOn); if (AR_SREV_MERLIN_10_OR_LATER(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62, pModal->thresh62); OS_REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, AR_PHY_EXT_CCA0_THRESH62, pModal->thresh62); } else { OS_REG_RMW_FIELD(ah, AR_PHY_CCA, AR_PHY_CCA_THRESH62, pModal->thresh62); OS_REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_CCA_THRESH62, pModal->thresh62); } /* Minor Version Specific application */ if (IS_EEP_MINOR_V2(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_FRAME_TO_DATA_START, pModal->txFrameToDataStart); OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_FRAME_TO_PA_ON, pModal->txFrameToPaOn); } if (IS_EEP_MINOR_V3(ah) && IEEE80211_IS_CHAN_HT40(chan)) /* Overwrite switch settling with HT40 value */ OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->swSettleHt40); if (AR_SREV_MERLIN_20_OR_LATER(ah) && EEP_MINOR(ah) >= AR5416_EEP_MINOR_VER_19) OS_REG_RMW_FIELD(ah, AR_PHY_CCK_TX_CTRL, AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK, pModal->miscBits); if (AR_SREV_MERLIN_20(ah) && EEP_MINOR(ah) >= AR5416_EEP_MINOR_VER_20) { if (IEEE80211_IS_CHAN_2GHZ(chan)) OS_A_REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, eep->baseEepHeader.dacLpMode); else if (eep->baseEepHeader.dacHiPwrMode_5G) OS_A_REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, 0); else OS_A_REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, eep->baseEepHeader.dacLpMode); OS_DELAY(100); OS_REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP, pModal->miscBits >> 2); OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL9, AR_PHY_TX_DESIRED_SCALE_CCK, eep->baseEepHeader.desiredScaleCCK); } return (AH_TRUE); } /* * Helper functions common for AP/CB/XB */ /* * Set the target power array "ratesArray" from the * given set of target powers. * * This is used by the various chipset/EEPROM TX power * setup routines. */ void ar5416SetRatesArrayFromTargetPower(struct ath_hal *ah, const struct ieee80211_channel *chan, int16_t *ratesArray, const CAL_TARGET_POWER_LEG *targetPowerCck, const CAL_TARGET_POWER_LEG *targetPowerCckExt, const CAL_TARGET_POWER_LEG *targetPowerOfdm, const CAL_TARGET_POWER_LEG *targetPowerOfdmExt, const CAL_TARGET_POWER_HT *targetPowerHt20, const CAL_TARGET_POWER_HT *targetPowerHt40) { #define N(a) (sizeof(a)/sizeof(a[0])) int i; /* Blank the rates array, to be consistent */ for (i = 0; i < Ar5416RateSize; i++) ratesArray[i] = 0; /* Set rates Array from collected data */ ratesArray[rate6mb] = ratesArray[rate9mb] = ratesArray[rate12mb] = ratesArray[rate18mb] = ratesArray[rate24mb] = targetPowerOfdm->tPow2x[0]; ratesArray[rate36mb] = targetPowerOfdm->tPow2x[1]; ratesArray[rate48mb] = targetPowerOfdm->tPow2x[2]; ratesArray[rate54mb] = targetPowerOfdm->tPow2x[3]; ratesArray[rateXr] = targetPowerOfdm->tPow2x[0]; for (i = 0; i < N(targetPowerHt20->tPow2x); i++) { ratesArray[rateHt20_0 + i] = targetPowerHt20->tPow2x[i]; } if (IEEE80211_IS_CHAN_2GHZ(chan)) { ratesArray[rate1l] = targetPowerCck->tPow2x[0]; ratesArray[rate2s] = ratesArray[rate2l] = targetPowerCck->tPow2x[1]; ratesArray[rate5_5s] = ratesArray[rate5_5l] = targetPowerCck->tPow2x[2]; ratesArray[rate11s] = ratesArray[rate11l] = targetPowerCck->tPow2x[3]; } if (IEEE80211_IS_CHAN_HT40(chan)) { for (i = 0; i < N(targetPowerHt40->tPow2x); i++) { ratesArray[rateHt40_0 + i] = targetPowerHt40->tPow2x[i]; } ratesArray[rateDupOfdm] = targetPowerHt40->tPow2x[0]; ratesArray[rateDupCck] = targetPowerHt40->tPow2x[0]; ratesArray[rateExtOfdm] = targetPowerOfdmExt->tPow2x[0]; if (IEEE80211_IS_CHAN_2GHZ(chan)) { ratesArray[rateExtCck] = targetPowerCckExt->tPow2x[0]; } } #undef N } /* * ar5416SetPowerPerRateTable * * Sets the transmit power in the baseband for the given * operating channel and mode. */ static HAL_BOOL ar5416SetPowerPerRateTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *ratesArray, uint16_t cfgCtl, uint16_t AntennaReduction, uint16_t twiceMaxRegulatoryPower, uint16_t powerLimit) { #define N(a) (sizeof(a)/sizeof(a[0])) /* Local defines to distinguish between extension and control CTL's */ #define EXT_ADDITIVE (0x8000) #define CTL_11A_EXT (CTL_11A | EXT_ADDITIVE) #define CTL_11G_EXT (CTL_11G | EXT_ADDITIVE) #define CTL_11B_EXT (CTL_11B | EXT_ADDITIVE) uint16_t twiceMaxEdgePower = AR5416_MAX_RATE_POWER; int i; int16_t twiceLargestAntenna; CAL_CTL_DATA *rep; CAL_TARGET_POWER_LEG targetPowerOfdm, targetPowerCck = {0, {0, 0, 0, 0}}; CAL_TARGET_POWER_LEG targetPowerOfdmExt = {0, {0, 0, 0, 0}}, targetPowerCckExt = {0, {0, 0, 0, 0}}; CAL_TARGET_POWER_HT targetPowerHt20, targetPowerHt40 = {0, {0, 0, 0, 0}}; int16_t scaledPower, minCtlPower; #define SUB_NUM_CTL_MODES_AT_5G_40 2 /* excluding HT40, EXT-OFDM */ #define SUB_NUM_CTL_MODES_AT_2G_40 3 /* excluding HT40, EXT-OFDM, EXT-CCK */ static const uint16_t ctlModesFor11a[] = { CTL_11A, CTL_5GHT20, CTL_11A_EXT, CTL_5GHT40 }; static const uint16_t ctlModesFor11g[] = { CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT, CTL_2GHT40 }; const uint16_t *pCtlMode; uint16_t numCtlModes, ctlMode, freq; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); /* Compute TxPower reduction due to Antenna Gain */ twiceLargestAntenna = AH_MAX(AH_MAX( pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[0], pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[1]), pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[2]); #if 0 /* Turn it back on if we need to calculate per chain antenna gain reduction */ /* Use only if the expected gain > 6dbi */ /* Chain 0 is always used */ twiceLargestAntenna = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[0]; /* Look at antenna gains of Chains 1 and 2 if the TX mask is set */ if (ahp->ah_tx_chainmask & 0x2) twiceLargestAntenna = AH_MAX(twiceLargestAntenna, pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[1]); if (ahp->ah_tx_chainmask & 0x4) twiceLargestAntenna = AH_MAX(twiceLargestAntenna, pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].antennaGainCh[2]); #endif twiceLargestAntenna = (int16_t)AH_MIN((AntennaReduction) - twiceLargestAntenna, 0); /* XXX setup for 5212 use (really used?) */ ath_hal_eepromSet(ah, IEEE80211_IS_CHAN_2GHZ(chan) ? AR_EEP_ANTGAINMAX_2 : AR_EEP_ANTGAINMAX_5, twiceLargestAntenna); /* * scaledPower is the minimum of the user input power level and * the regulatory allowed power level */ scaledPower = AH_MIN(powerLimit, twiceMaxRegulatoryPower + twiceLargestAntenna); /* Reduce scaled Power by number of chains active to get to per chain tx power level */ /* TODO: better value than these? */ switch (owl_get_ntxchains(AH5416(ah)->ah_tx_chainmask)) { case 1: break; case 2: scaledPower -= pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pwrDecreaseFor2Chain; break; case 3: scaledPower -= pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pwrDecreaseFor3Chain; break; default: return AH_FALSE; /* Unsupported number of chains */ } scaledPower = AH_MAX(0, scaledPower); /* Get target powers from EEPROM - our baseline for TX Power */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { /* Setup for CTL modes */ numCtlModes = N(ctlModesFor11g) - SUB_NUM_CTL_MODES_AT_2G_40; /* CTL_11B, CTL_11G, CTL_2GHT20 */ pCtlMode = ctlModesFor11g; ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPowerCck, AR5416_NUM_2G_CCK_TARGET_POWERS, &targetPowerCck, 4, AH_FALSE); ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower2G, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerOfdm, 4, AH_FALSE); ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower2GHT20, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerHt20, 8, AH_FALSE); if (IEEE80211_IS_CHAN_HT40(chan)) { numCtlModes = N(ctlModesFor11g); /* All 2G CTL's */ ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower2GHT40, AR5416_NUM_2G_40_TARGET_POWERS, &targetPowerHt40, 8, AH_TRUE); /* Get target powers for extension channels */ ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPowerCck, AR5416_NUM_2G_CCK_TARGET_POWERS, &targetPowerCckExt, 4, AH_TRUE); ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower2G, AR5416_NUM_2G_20_TARGET_POWERS, &targetPowerOfdmExt, 4, AH_TRUE); } } else { /* Setup for CTL modes */ numCtlModes = N(ctlModesFor11a) - SUB_NUM_CTL_MODES_AT_5G_40; /* CTL_11A, CTL_5GHT20 */ pCtlMode = ctlModesFor11a; ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower5G, AR5416_NUM_5G_20_TARGET_POWERS, &targetPowerOfdm, 4, AH_FALSE); ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower5GHT20, AR5416_NUM_5G_20_TARGET_POWERS, &targetPowerHt20, 8, AH_FALSE); if (IEEE80211_IS_CHAN_HT40(chan)) { numCtlModes = N(ctlModesFor11a); /* All 5G CTL's */ ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower5GHT40, AR5416_NUM_5G_40_TARGET_POWERS, &targetPowerHt40, 8, AH_TRUE); ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower5G, AR5416_NUM_5G_20_TARGET_POWERS, &targetPowerOfdmExt, 4, AH_TRUE); } } /* * For MIMO, need to apply regulatory caps individually across dynamically * running modes: CCK, OFDM, HT20, HT40 * * The outer loop walks through each possible applicable runtime mode. * The inner loop walks through each ctlIndex entry in EEPROM. * The ctl value is encoded as [7:4] == test group, [3:0] == test mode. * */ for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) { HAL_BOOL isHt40CtlMode = (pCtlMode[ctlMode] == CTL_5GHT40) || (pCtlMode[ctlMode] == CTL_2GHT40); if (isHt40CtlMode) { freq = centers.ctl_center; } else if (pCtlMode[ctlMode] & EXT_ADDITIVE) { freq = centers.ext_center; } else { freq = centers.ctl_center; } /* walk through each CTL index stored in EEPROM */ for (i = 0; (i < AR5416_NUM_CTLS) && pEepData->ctlIndex[i]; i++) { uint16_t twiceMinEdgePower; /* compare test group from regulatory channel list with test mode from pCtlMode list */ if ((((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == pEepData->ctlIndex[i]) || (((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == ((pEepData->ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL))) { rep = &(pEepData->ctlData[i]); twiceMinEdgePower = ar5416GetMaxEdgePower(freq, rep->ctlEdges[owl_get_ntxchains(AH5416(ah)->ah_tx_chainmask) - 1], IEEE80211_IS_CHAN_2GHZ(chan)); if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { /* Find the minimum of all CTL edge powers that apply to this channel */ twiceMaxEdgePower = AH_MIN(twiceMaxEdgePower, twiceMinEdgePower); } else { /* specific */ twiceMaxEdgePower = twiceMinEdgePower; break; } } } minCtlPower = (uint8_t)AH_MIN(twiceMaxEdgePower, scaledPower); /* Apply ctl mode to correct target power set */ switch(pCtlMode[ctlMode]) { case CTL_11B: for (i = 0; i < N(targetPowerCck.tPow2x); i++) { targetPowerCck.tPow2x[i] = (uint8_t)AH_MIN(targetPowerCck.tPow2x[i], minCtlPower); } break; case CTL_11A: case CTL_11G: for (i = 0; i < N(targetPowerOfdm.tPow2x); i++) { targetPowerOfdm.tPow2x[i] = (uint8_t)AH_MIN(targetPowerOfdm.tPow2x[i], minCtlPower); } break; case CTL_5GHT20: case CTL_2GHT20: for (i = 0; i < N(targetPowerHt20.tPow2x); i++) { targetPowerHt20.tPow2x[i] = (uint8_t)AH_MIN(targetPowerHt20.tPow2x[i], minCtlPower); } break; case CTL_11B_EXT: targetPowerCckExt.tPow2x[0] = (uint8_t)AH_MIN(targetPowerCckExt.tPow2x[0], minCtlPower); break; case CTL_11A_EXT: case CTL_11G_EXT: targetPowerOfdmExt.tPow2x[0] = (uint8_t)AH_MIN(targetPowerOfdmExt.tPow2x[0], minCtlPower); break; case CTL_5GHT40: case CTL_2GHT40: for (i = 0; i < N(targetPowerHt40.tPow2x); i++) { targetPowerHt40.tPow2x[i] = (uint8_t)AH_MIN(targetPowerHt40.tPow2x[i], minCtlPower); } break; default: return AH_FALSE; break; } } /* end ctl mode checking */ /* Set rates Array from collected data */ ar5416SetRatesArrayFromTargetPower(ah, chan, ratesArray, &targetPowerCck, &targetPowerCckExt, &targetPowerOfdm, &targetPowerOfdmExt, &targetPowerHt20, &targetPowerHt40); return AH_TRUE; #undef EXT_ADDITIVE #undef CTL_11A_EXT #undef CTL_11G_EXT #undef CTL_11B_EXT #undef SUB_NUM_CTL_MODES_AT_5G_40 #undef SUB_NUM_CTL_MODES_AT_2G_40 #undef N } /************************************************************************** * fbin2freq * * Get channel value from binary representation held in eeprom * RETURNS: the frequency in MHz */ static uint16_t fbin2freq(uint8_t fbin, HAL_BOOL is2GHz) { /* * Reserved value 0xFF provides an empty definition both as * an fbin and as a frequency - do not convert */ if (fbin == AR5416_BCHAN_UNUSED) { return fbin; } return (uint16_t)((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin)); } /* * ar5416GetMaxEdgePower * * Find the maximum conformance test limit for the given channel and CTL info */ uint16_t ar5416GetMaxEdgePower(uint16_t freq, CAL_CTL_EDGES *pRdEdgesPower, HAL_BOOL is2GHz) { uint16_t twiceMaxEdgePower = AR5416_MAX_RATE_POWER; int i; /* Get the edge power */ for (i = 0; (i < AR5416_NUM_BAND_EDGES) && (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED) ; i++) { /* * If there's an exact channel match or an inband flag set * on the lower channel use the given rdEdgePower */ if (freq == fbin2freq(pRdEdgesPower[i].bChannel, is2GHz)) { twiceMaxEdgePower = MS(pRdEdgesPower[i].tPowerFlag, CAL_CTL_EDGES_POWER); break; } else if ((i > 0) && (freq < fbin2freq(pRdEdgesPower[i].bChannel, is2GHz))) { if (fbin2freq(pRdEdgesPower[i - 1].bChannel, is2GHz) < freq && (pRdEdgesPower[i - 1].tPowerFlag & CAL_CTL_EDGES_FLAG) != 0) { twiceMaxEdgePower = MS(pRdEdgesPower[i - 1].tPowerFlag, CAL_CTL_EDGES_POWER); } /* Leave loop - no more affecting edges possible in this monotonic increasing list */ break; } } HALASSERT(twiceMaxEdgePower > 0); return twiceMaxEdgePower; } /************************************************************** * ar5416GetTargetPowers * * Return the rates of target power for the given target power table * channel, and number of channels */ void ar5416GetTargetPowers(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_TARGET_POWER_HT *powInfo, uint16_t numChannels, CAL_TARGET_POWER_HT *pNewPower, uint16_t numRates, HAL_BOOL isHt40Target) { uint16_t clo, chi; int i; int matchIndex = -1, lowIndex = -1; uint16_t freq; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); freq = isHt40Target ? centers.synth_center : centers.ctl_center; /* Copy the target powers into the temp channel list */ if (freq <= fbin2freq(powInfo[0].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) { matchIndex = 0; } else { for (i = 0; (i < numChannels) && (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { if (freq == fbin2freq(powInfo[i].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) { matchIndex = i; break; } else if ((freq < fbin2freq(powInfo[i].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) && (freq > fbin2freq(powInfo[i - 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)))) { lowIndex = i - 1; break; } } if ((matchIndex == -1) && (lowIndex == -1)) { HALASSERT(freq > fbin2freq(powInfo[i - 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))); matchIndex = i - 1; } } if (matchIndex != -1) { OS_MEMCPY(pNewPower, &powInfo[matchIndex], sizeof(*pNewPower)); } else { HALASSERT(lowIndex != -1); /* * Get the lower and upper channels, target powers, * and interpolate between them. */ clo = fbin2freq(powInfo[lowIndex].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)); chi = fbin2freq(powInfo[lowIndex + 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)); for (i = 0; i < numRates; i++) { pNewPower->tPow2x[i] = (uint8_t)ath_ee_interpolate(freq, clo, chi, powInfo[lowIndex].tPow2x[i], powInfo[lowIndex + 1].tPow2x[i]); } } } /************************************************************** * ar5416GetTargetPowersLeg * * Return the four rates of target power for the given target power table * channel, and number of channels */ void ar5416GetTargetPowersLeg(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_TARGET_POWER_LEG *powInfo, uint16_t numChannels, CAL_TARGET_POWER_LEG *pNewPower, uint16_t numRates, HAL_BOOL isExtTarget) { uint16_t clo, chi; int i; int matchIndex = -1, lowIndex = -1; uint16_t freq; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); freq = (isExtTarget) ? centers.ext_center :centers.ctl_center; /* Copy the target powers into the temp channel list */ if (freq <= fbin2freq(powInfo[0].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) { matchIndex = 0; } else { for (i = 0; (i < numChannels) && (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { if (freq == fbin2freq(powInfo[i].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) { matchIndex = i; break; } else if ((freq < fbin2freq(powInfo[i].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))) && (freq > fbin2freq(powInfo[i - 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)))) { lowIndex = i - 1; break; } } if ((matchIndex == -1) && (lowIndex == -1)) { HALASSERT(freq > fbin2freq(powInfo[i - 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan))); matchIndex = i - 1; } } if (matchIndex != -1) { OS_MEMCPY(pNewPower, &powInfo[matchIndex], sizeof(*pNewPower)); } else { HALASSERT(lowIndex != -1); /* * Get the lower and upper channels, target powers, * and interpolate between them. */ clo = fbin2freq(powInfo[lowIndex].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)); chi = fbin2freq(powInfo[lowIndex + 1].bChannel, IEEE80211_IS_CHAN_2GHZ(chan)); for (i = 0; i < numRates; i++) { pNewPower->tPow2x[i] = (uint8_t)ath_ee_interpolate(freq, clo, chi, powInfo[lowIndex].tPow2x[i], powInfo[lowIndex + 1].tPow2x[i]); } } } /* * Set the gain boundaries for the given radio chain. * * The gain boundaries tell the hardware at what point in the * PDADC array to "switch over" from one PD gain setting * to another. There's also a gain overlap between two * PDADC array gain curves where there's valid PD values * for 2 gain settings. * * The hardware uses the gain overlap and gain boundaries * to determine which gain curve to use for the given * target TX power. */ void ar5416SetGainBoundariesClosedLoop(struct ath_hal *ah, int i, uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[]) { int regChainOffset; regChainOffset = ar5416GetRegChainOffset(ah, i); HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: chain %d: gainOverlap_t2: %d," " gainBoundaries: %d, %d, %d, %d\n", __func__, i, pdGainOverlap_t2, gainBoundaries[0], gainBoundaries[1], gainBoundaries[2], gainBoundaries[3]); OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); } /* * Get the gain values and the number of gain levels given * in xpdMask. * * The EEPROM xpdMask determines which power detector gain * levels were used during calibration. Each of these mask * bits maps to a fixed gain level in hardware. */ uint16_t ar5416GetXpdGainValues(struct ath_hal *ah, uint16_t xpdMask, uint16_t xpdGainValues[]) { int i; uint16_t numXpdGain = 0; for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) { if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) { if (numXpdGain >= AR5416_NUM_PD_GAINS) { HALASSERT(0); break; } xpdGainValues[numXpdGain] = (uint16_t)(AR5416_PD_GAINS_IN_MASK - i); numXpdGain++; } } return numXpdGain; } /* * Write the detector gain and biases. * * There are four power detector gain levels. The xpdMask in the EEPROM * determines which power detector gain levels have TX power calibration * data associated with them. This function writes the number of * PD gain levels and their values into the hardware. * * This is valid for all TX chains - the calibration data itself however * will likely differ per-chain. */ void ar5416WriteDetectorGainBiases(struct ath_hal *ah, uint16_t numXpdGain, uint16_t xpdGainValues[]) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: numXpdGain: %d," " xpdGainValues: %d, %d, %d\n", __func__, numXpdGain, xpdGainValues[0], xpdGainValues[1], xpdGainValues[2]); OS_REG_WRITE(ah, AR_PHY_TPCRG1, (OS_REG_READ(ah, AR_PHY_TPCRG1) & ~(AR_PHY_TPCRG1_NUM_PD_GAIN | AR_PHY_TPCRG1_PD_GAIN_1 | AR_PHY_TPCRG1_PD_GAIN_2 | AR_PHY_TPCRG1_PD_GAIN_3)) | SM(numXpdGain - 1, AR_PHY_TPCRG1_NUM_PD_GAIN) | SM(xpdGainValues[0], AR_PHY_TPCRG1_PD_GAIN_1 ) | SM(xpdGainValues[1], AR_PHY_TPCRG1_PD_GAIN_2) | SM(xpdGainValues[2], AR_PHY_TPCRG1_PD_GAIN_3)); } /* * Write the PDADC array to the given radio chain i. * * The 32 PDADC registers are written without any care about * their contents - so if various chips treat values as "special", * this routine will not care. */ void ar5416WritePdadcValues(struct ath_hal *ah, int i, uint8_t pdadcValues[]) { int regOffset, regChainOffset; int j; int reg32; regChainOffset = ar5416GetRegChainOffset(ah, i); regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset; for (j = 0; j < 32; j++) { reg32 = ((pdadcValues[4*j + 0] & 0xFF) << 0) | ((pdadcValues[4*j + 1] & 0xFF) << 8) | ((pdadcValues[4*j + 2] & 0xFF) << 16) | ((pdadcValues[4*j + 3] & 0xFF) << 24) ; OS_REG_WRITE(ah, regOffset, reg32); HALDEBUG(ah, HAL_DEBUG_EEPROM, "PDADC: Chain %d |" " PDADC %3d Value %3d | PDADC %3d Value %3d | PDADC %3d" " Value %3d | PDADC %3d Value %3d |\n", i, 4*j, pdadcValues[4*j], 4*j+1, pdadcValues[4*j + 1], 4*j+2, pdadcValues[4*j + 2], 4*j+3, pdadcValues[4*j + 3]); regOffset += 4; } } /************************************************************** * ar5416SetPowerCalTable * * Pull the PDADC piers from cal data and interpolate them across the given * points as well as from the nearest pier(s) to get a power detector * linear voltage to power level table. */ HAL_BOOL ar5416SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset) { CAL_DATA_PER_FREQ *pRawDataset; uint8_t *pCalBChans = AH_NULL; uint16_t pdGainOverlap_t2; static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES]; uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK]; uint16_t numPiers, i; int16_t tMinCalPower; uint16_t numXpdGain, xpdMask; uint16_t xpdGainValues[AR5416_NUM_PD_GAINS]; uint32_t regChainOffset; OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues)); xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain; if (IS_EEP_MINOR_V2(ah)) { pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap; } else { pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); } if (IEEE80211_IS_CHAN_2GHZ(chan)) { pCalBChans = pEepData->calFreqPier2G; numPiers = AR5416_NUM_2G_CAL_PIERS; } else { pCalBChans = pEepData->calFreqPier5G; numPiers = AR5416_NUM_5G_CAL_PIERS; } /* Calculate the value of xpdgains from the xpdGain Mask */ numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues); /* Write the detector gain biases and their number */ ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues); for (i = 0; i < AR5416_MAX_CHAINS; i++) { regChainOffset = ar5416GetRegChainOffset(ah, i); if (pEepData->baseEepHeader.txMask & (1 << i)) { if (IEEE80211_IS_CHAN_2GHZ(chan)) { pRawDataset = pEepData->calPierData2G[i]; } else { pRawDataset = pEepData->calPierData5G[i]; } /* Fetch the gain boundaries and the PDADC values */ ar5416GetGainBoundariesAndPdadcs(ah, chan, pRawDataset, pCalBChans, numPiers, pdGainOverlap_t2, &tMinCalPower, gainBoundaries, pdadcValues, numXpdGain); if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) { ar5416SetGainBoundariesClosedLoop(ah, i, pdGainOverlap_t2, gainBoundaries); } /* Write the power values into the baseband power table */ ar5416WritePdadcValues(ah, i, pdadcValues); } } *pTxPowerIndexOffset = 0; return AH_TRUE; } /************************************************************** * ar5416GetGainBoundariesAndPdadcs * * Uses the data points read from EEPROM to reconstruct the pdadc power table * Called by ar5416SetPowerCalTable only. */ void ar5416GetGainBoundariesAndPdadcs(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_DATA_PER_FREQ *pRawDataSet, uint8_t * bChans, uint16_t availPiers, uint16_t tPdGainOverlap, int16_t *pMinCalPower, uint16_t * pPdGainBoundaries, uint8_t * pPDADCValues, uint16_t numXpdGains) { int i, j, k; int16_t ss; /* potentially -ve index for taking care of pdGainOverlap */ uint16_t idxL, idxR, numPiers; /* Pier indexes */ /* filled out Vpd table for all pdGains (chanL) */ static uint8_t vpdTableL[AR5416_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (chanR) */ static uint8_t vpdTableR[AR5416_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; /* filled out Vpd table for all pdGains (interpolated) */ static uint8_t vpdTableI[AR5416_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; uint8_t *pVpdL, *pVpdR, *pPwrL, *pPwrR; uint8_t minPwrT4[AR5416_NUM_PD_GAINS]; uint8_t maxPwrT4[AR5416_NUM_PD_GAINS]; int16_t vpdStep; int16_t tmpVal; uint16_t sizeCurrVpdTable, maxIndex, tgtIndex; HAL_BOOL match; int16_t minDelta = 0; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); /* Trim numPiers for the number of populated channel Piers */ for (numPiers = 0; numPiers < availPiers; numPiers++) { if (bChans[numPiers] == AR5416_BCHAN_UNUSED) { break; } } /* Find pier indexes around the current channel */ match = ath_ee_getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center, IEEE80211_IS_CHAN_2GHZ(chan)), bChans, numPiers, &idxL, &idxR); if (match) { /* Directly fill both vpd tables from the matching index */ for (i = 0; i < numXpdGains; i++) { minPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][0]; maxPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][4]; ath_ee_FillVpdTable(minPwrT4[i], maxPwrT4[i], pRawDataSet[idxL].pwrPdg[i], pRawDataSet[idxL].vpdPdg[i], AR5416_PD_GAIN_ICEPTS, vpdTableI[i]); } } else { for (i = 0; i < numXpdGains; i++) { pVpdL = pRawDataSet[idxL].vpdPdg[i]; pPwrL = pRawDataSet[idxL].pwrPdg[i]; pVpdR = pRawDataSet[idxR].vpdPdg[i]; pPwrR = pRawDataSet[idxR].pwrPdg[i]; /* Start Vpd interpolation from the max of the minimum powers */ minPwrT4[i] = AH_MAX(pPwrL[0], pPwrR[0]); /* End Vpd interpolation from the min of the max powers */ maxPwrT4[i] = AH_MIN(pPwrL[AR5416_PD_GAIN_ICEPTS - 1], pPwrR[AR5416_PD_GAIN_ICEPTS - 1]); HALASSERT(maxPwrT4[i] > minPwrT4[i]); /* Fill pier Vpds */ ath_ee_FillVpdTable(minPwrT4[i], maxPwrT4[i], pPwrL, pVpdL, AR5416_PD_GAIN_ICEPTS, vpdTableL[i]); ath_ee_FillVpdTable(minPwrT4[i], maxPwrT4[i], pPwrR, pVpdR, AR5416_PD_GAIN_ICEPTS, vpdTableR[i]); /* Interpolate the final vpd */ for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) { vpdTableI[i][j] = (uint8_t)(ath_ee_interpolate((uint16_t)FREQ2FBIN(centers.synth_center, IEEE80211_IS_CHAN_2GHZ(chan)), bChans[idxL], bChans[idxR], vpdTableL[i][j], vpdTableR[i][j])); } } } *pMinCalPower = (int16_t)(minPwrT4[0] / 2); k = 0; /* index for the final table */ for (i = 0; i < numXpdGains; i++) { if (i == (numXpdGains - 1)) { pPdGainBoundaries[i] = (uint16_t)(maxPwrT4[i] / 2); } else { pPdGainBoundaries[i] = (uint16_t)((maxPwrT4[i] + minPwrT4[i+1]) / 4); } pPdGainBoundaries[i] = (uint16_t)AH_MIN(AR5416_MAX_RATE_POWER, pPdGainBoundaries[i]); /* NB: only applies to owl 1.0 */ if ((i == 0) && !AR_SREV_5416_V20_OR_LATER(ah) ) { /* * fix the gain delta, but get a delta that can be applied to min to * keep the upper power values accurate, don't think max needs to * be adjusted because should not be at that area of the table? */ minDelta = pPdGainBoundaries[0] - 23; pPdGainBoundaries[0] = 23; } else { minDelta = 0; } /* Find starting index for this pdGain */ if (i == 0) { if (AR_SREV_MERLIN_10_OR_LATER(ah)) ss = (int16_t)(0 - (minPwrT4[i] / 2)); else ss = 0; /* for the first pdGain, start from index 0 */ } else { /* need overlap entries extrapolated below. */ ss = (int16_t)((pPdGainBoundaries[i-1] - (minPwrT4[i] / 2)) - tPdGainOverlap + 1 + minDelta); } vpdStep = (int16_t)(vpdTableI[i][1] - vpdTableI[i][0]); vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); /* *-ve ss indicates need to extrapolate data below for this pdGain */ while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { tmpVal = (int16_t)(vpdTableI[i][0] + ss * vpdStep); pPDADCValues[k++] = (uint8_t)((tmpVal < 0) ? 0 : tmpVal); ss++; } sizeCurrVpdTable = (uint8_t)((maxPwrT4[i] - minPwrT4[i]) / 2 +1); tgtIndex = (uint8_t)(pPdGainBoundaries[i] + tPdGainOverlap - (minPwrT4[i] / 2)); maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable; while ((ss < maxIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { pPDADCValues[k++] = vpdTableI[i][ss++]; } vpdStep = (int16_t)(vpdTableI[i][sizeCurrVpdTable - 1] - vpdTableI[i][sizeCurrVpdTable - 2]); vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); /* * for last gain, pdGainBoundary == Pmax_t2, so will * have to extrapolate */ if (tgtIndex >= maxIndex) { /* need to extrapolate above */ while ((ss <= tgtIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] + (ss - maxIndex +1) * vpdStep)); pPDADCValues[k++] = (uint8_t)((tmpVal > 255) ? 255 : tmpVal); ss++; } } /* extrapolated above */ } /* for all pdGainUsed */ /* Fill out pdGainBoundaries - only up to 2 allowed here, but hardware allows up to 4 */ while (i < AR5416_PD_GAINS_IN_MASK) { pPdGainBoundaries[i] = pPdGainBoundaries[i-1]; i++; } while (k < AR5416_NUM_PDADC_VALUES) { pPDADCValues[k] = pPDADCValues[k-1]; k++; } return; } /* * The linux ath9k driver and (from what I've been told) the reference * Atheros driver enables the 11n PHY by default whether or not it's * configured. */ static void ar5416Set11nRegs(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t phymode; uint32_t enableDacFifo = 0; HAL_HT_MACMODE macmode; /* MAC - 20/40 mode */ if (AR_SREV_KITE_10_OR_LATER(ah)) enableDacFifo = (OS_REG_READ(ah, AR_PHY_TURBO) & AR_PHY_FC_ENABLE_DAC_FIFO); /* Enable 11n HT, 20 MHz */ phymode = AR_PHY_FC_HT_EN | AR_PHY_FC_SHORT_GI_40 | AR_PHY_FC_SINGLE_HT_LTF1 | AR_PHY_FC_WALSH | enableDacFifo; /* Configure baseband for dynamic 20/40 operation */ if (IEEE80211_IS_CHAN_HT40(chan)) { phymode |= AR_PHY_FC_DYN2040_EN; /* Configure control (primary) channel at +-10MHz */ if (IEEE80211_IS_CHAN_HT40U(chan)) phymode |= AR_PHY_FC_DYN2040_PRI_CH; #if 0 /* Configure 20/25 spacing */ if (ht->ht_extprotspacing == HAL_HT_EXTPROTSPACING_25) phymode |= AR_PHY_FC_DYN2040_EXT_CH; #endif macmode = HAL_HT_MACMODE_2040; } else macmode = HAL_HT_MACMODE_20; OS_REG_WRITE(ah, AR_PHY_TURBO, phymode); /* Configure MAC for 20/40 operation */ ar5416Set11nMac2040(ah, macmode); /* global transmit timeout (25 TUs default)*/ /* XXX - put this elsewhere??? */ OS_REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S) ; /* carrier sense timeout */ OS_REG_SET_BIT(ah, AR_GTTM, AR_GTTM_CST_USEC); OS_REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S); } void ar5416GetChannelCenters(struct ath_hal *ah, const struct ieee80211_channel *chan, CHAN_CENTERS *centers) { uint16_t freq = ath_hal_gethwchannel(ah, chan); centers->ctl_center = freq; centers->synth_center = freq; /* * In 20/40 phy mode, the center frequency is * "between" the control and extension channels. */ if (IEEE80211_IS_CHAN_HT40U(chan)) { centers->synth_center += HT40_CHANNEL_CENTER_SHIFT; centers->ext_center = centers->synth_center + HT40_CHANNEL_CENTER_SHIFT; } else if (IEEE80211_IS_CHAN_HT40D(chan)) { centers->synth_center -= HT40_CHANNEL_CENTER_SHIFT; centers->ext_center = centers->synth_center - HT40_CHANNEL_CENTER_SHIFT; } else { centers->ext_center = freq; } } /* * Override the INI vals being programmed. */ static void ar5416OverrideIni(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t val; /* * Set the RX_ABORT and RX_DIS and clear if off only after * RXE is set for MAC. This prevents frames with corrupted * descriptor status. */ OS_REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); if (AR_SREV_MERLIN_10_OR_LATER(ah)) { val = OS_REG_READ(ah, AR_PCU_MISC_MODE2); val &= (~AR_PCU_MISC_MODE2_ADHOC_MCAST_KEYID_ENABLE); if (!AR_SREV_9271(ah)) val &= ~AR_PCU_MISC_MODE2_HWWAR1; if (AR_SREV_KIWI_10_OR_LATER(ah)) val = val & (~AR_PCU_MISC_MODE2_HWWAR2); OS_REG_WRITE(ah, AR_PCU_MISC_MODE2, val); } /* * Disable RIFS search on some chips to avoid baseband * hang issues. */ if (AR_SREV_HOWL(ah) || AR_SREV_SOWL(ah)) (void) ar5416SetRifsDelay(ah, chan, AH_FALSE); if (!AR_SREV_5416_V20_OR_LATER(ah) || AR_SREV_MERLIN(ah)) return; /* * Disable BB clock gating * Necessary to avoid issues on AR5416 2.0 */ OS_REG_WRITE(ah, 0x9800 + (651 << 2), 0x11); } struct ini { uint32_t *data; /* NB: !const */ int rows, cols; }; /* * Override XPA bias level based on operating frequency. * This is a v14 EEPROM specific thing for the AR9160. */ void ar5416EepromSetAddac(struct ath_hal *ah, const struct ieee80211_channel *chan) { #define XPA_LVL_FREQ(cnt) (pModal->xpaBiasLvlFreq[cnt]) MODAL_EEP_HEADER *pModal; HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom; struct ar5416eeprom *eep = &ee->ee_base; uint8_t biaslevel; if (! AR_SREV_SOWL(ah)) return; if (EEP_MINOR(ah) < AR5416_EEP_MINOR_VER_7) return; pModal = &(eep->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)]); if (pModal->xpaBiasLvl != 0xff) biaslevel = pModal->xpaBiasLvl; else { uint16_t resetFreqBin, freqBin, freqCount = 0; CHAN_CENTERS centers; ar5416GetChannelCenters(ah, chan, ¢ers); resetFreqBin = FREQ2FBIN(centers.synth_center, IEEE80211_IS_CHAN_2GHZ(chan)); freqBin = XPA_LVL_FREQ(0) & 0xff; biaslevel = (uint8_t) (XPA_LVL_FREQ(0) >> 14); freqCount++; while (freqCount < 3) { if (XPA_LVL_FREQ(freqCount) == 0x0) break; freqBin = XPA_LVL_FREQ(freqCount) & 0xff; if (resetFreqBin >= freqBin) biaslevel = (uint8_t)(XPA_LVL_FREQ(freqCount) >> 14); else break; freqCount++; } } HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: overriding XPA bias level = %d\n", __func__, biaslevel); /* * This is a dirty workaround for the const initval data, * which will upset multiple AR9160's on the same board. * * The HAL should likely just have a private copy of the addac * data per instance. */ if (IEEE80211_IS_CHAN_2GHZ(chan)) HAL_INI_VAL((struct ini *) &AH5416(ah)->ah_ini_addac, 7, 1) = (HAL_INI_VAL(&AH5416(ah)->ah_ini_addac, 7, 1) & (~0x18)) | biaslevel << 3; else HAL_INI_VAL((struct ini *) &AH5416(ah)->ah_ini_addac, 6, 1) = (HAL_INI_VAL(&AH5416(ah)->ah_ini_addac, 6, 1) & (~0xc0)) | biaslevel << 6; #undef XPA_LVL_FREQ } static void ar5416MarkPhyInactive(struct ath_hal *ah) { OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); } #define AR5416_IFS_SLOT_FULL_RATE_40 0x168 /* 9 us half, 40 MHz core clock (9*40) */ #define AR5416_IFS_SLOT_HALF_RATE_40 0x104 /* 13 us half, 20 MHz core clock (13*20) */ #define AR5416_IFS_SLOT_QUARTER_RATE_40 0xD2 /* 21 us quarter, 10 MHz core clock (21*10) */ #define AR5416_IFS_EIFS_FULL_RATE_40 0xE60 /* (74 + (2 * 9)) * 40MHz core clock */ #define AR5416_IFS_EIFS_HALF_RATE_40 0xDAC /* (149 + (2 * 13)) * 20MHz core clock */ #define AR5416_IFS_EIFS_QUARTER_RATE_40 0xD48 /* (298 + (2 * 21)) * 10MHz core clock */ #define AR5416_IFS_SLOT_FULL_RATE_44 0x18c /* 9 us half, 44 MHz core clock (9*44) */ #define AR5416_IFS_SLOT_HALF_RATE_44 0x11e /* 13 us half, 22 MHz core clock (13*22) */ #define AR5416_IFS_SLOT_QUARTER_RATE_44 0xe7 /* 21 us quarter, 11 MHz core clock (21*11) */ #define AR5416_IFS_EIFS_FULL_RATE_44 0xfd0 /* (74 + (2 * 9)) * 44MHz core clock */ #define AR5416_IFS_EIFS_HALF_RATE_44 0xf0a /* (149 + (2 * 13)) * 22MHz core clock */ #define AR5416_IFS_EIFS_QUARTER_RATE_44 0xe9c /* (298 + (2 * 21)) * 11MHz core clock */ #define AR5416_INIT_USEC_40 40 #define AR5416_HALF_RATE_USEC_40 19 /* ((40 / 2) - 1 ) */ #define AR5416_QUARTER_RATE_USEC_40 9 /* ((40 / 4) - 1 ) */ #define AR5416_INIT_USEC_44 44 #define AR5416_HALF_RATE_USEC_44 21 /* ((44 / 2) - 1 ) */ #define AR5416_QUARTER_RATE_USEC_44 10 /* ((44 / 4) - 1 ) */ /* XXX What should these be for 40/44MHz clocks (and half/quarter) ? */ #define AR5416_RX_NON_FULL_RATE_LATENCY 63 #define AR5416_TX_HALF_RATE_LATENCY 108 #define AR5416_TX_QUARTER_RATE_LATENCY 216 /* * Adjust various register settings based on half/quarter rate clock setting. * This includes: * * + USEC, TX/RX latency, * + IFS params: slot, eifs, misc etc. * * TODO: * * + Verify which other registers need to be tweaked; * + Verify the behaviour of this for 5GHz fast and non-fast clock mode; * + This just plain won't work for long distance links - the coverage class * code isn't aware of the slot/ifs/ACK/RTS timeout values that need to * change; * + Verify whether the 32KHz USEC value needs to be kept for the 802.11n * series chips? * + Calculate/derive values for 2GHz, 5GHz, 5GHz fast clock */ static void ar5416SetIFSTiming(struct ath_hal *ah, const struct ieee80211_channel *chan) { uint32_t txLat, rxLat, usec, slot, refClock, eifs, init_usec; int clk_44 = 0; HALASSERT(IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan)); /* 2GHz and 5GHz fast clock - 44MHz; else 40MHz */ if (IEEE80211_IS_CHAN_2GHZ(chan)) clk_44 = 1; else if (IEEE80211_IS_CHAN_5GHZ(chan) && IS_5GHZ_FAST_CLOCK_EN(ah, chan)) clk_44 = 1; /* XXX does this need save/restoring for the 11n chips? */ refClock = OS_REG_READ(ah, AR_USEC) & AR_USEC_USEC32; /* * XXX This really should calculate things, not use * hard coded values! Ew. */ if (IEEE80211_IS_CHAN_HALF(chan)) { if (clk_44) { slot = AR5416_IFS_SLOT_HALF_RATE_44; rxLat = AR5416_RX_NON_FULL_RATE_LATENCY << AR5416_USEC_RX_LAT_S; txLat = AR5416_TX_HALF_RATE_LATENCY << AR5416_USEC_TX_LAT_S; usec = AR5416_HALF_RATE_USEC_44; eifs = AR5416_IFS_EIFS_HALF_RATE_44; init_usec = AR5416_INIT_USEC_44 >> 1; } else { slot = AR5416_IFS_SLOT_HALF_RATE_40; rxLat = AR5416_RX_NON_FULL_RATE_LATENCY << AR5416_USEC_RX_LAT_S; txLat = AR5416_TX_HALF_RATE_LATENCY << AR5416_USEC_TX_LAT_S; usec = AR5416_HALF_RATE_USEC_40; eifs = AR5416_IFS_EIFS_HALF_RATE_40; init_usec = AR5416_INIT_USEC_40 >> 1; } } else { /* quarter rate */ if (clk_44) { slot = AR5416_IFS_SLOT_QUARTER_RATE_44; rxLat = AR5416_RX_NON_FULL_RATE_LATENCY << AR5416_USEC_RX_LAT_S; txLat = AR5416_TX_QUARTER_RATE_LATENCY << AR5416_USEC_TX_LAT_S; usec = AR5416_QUARTER_RATE_USEC_44; eifs = AR5416_IFS_EIFS_QUARTER_RATE_44; init_usec = AR5416_INIT_USEC_44 >> 2; } else { slot = AR5416_IFS_SLOT_QUARTER_RATE_40; rxLat = AR5416_RX_NON_FULL_RATE_LATENCY << AR5416_USEC_RX_LAT_S; txLat = AR5416_TX_QUARTER_RATE_LATENCY << AR5416_USEC_TX_LAT_S; usec = AR5416_QUARTER_RATE_USEC_40; eifs = AR5416_IFS_EIFS_QUARTER_RATE_40; init_usec = AR5416_INIT_USEC_40 >> 2; } } /* XXX verify these! */ OS_REG_WRITE(ah, AR_USEC, (usec | refClock | txLat | rxLat)); OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, slot); OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, eifs); OS_REG_RMW_FIELD(ah, AR_D_GBL_IFS_MISC, AR_D_GBL_IFS_MISC_USEC_DURATION, init_usec); }