1185380Ssam/* 2187831Ssam * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 3185380Ssam * Copyright (c) 2002-2008 Atheros Communications, Inc. 4185380Ssam * 5185380Ssam * Permission to use, copy, modify, and/or distribute this software for any 6185380Ssam * purpose with or without fee is hereby granted, provided that the above 7185380Ssam * copyright notice and this permission notice appear in all copies. 8185380Ssam * 9185380Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10185380Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11185380Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12185380Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13185380Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14185380Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15185380Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16185380Ssam * 17187831Ssam * $FreeBSD$ 18185380Ssam */ 19185380Ssam#include "opt_ah.h" 20185380Ssam 21185380Ssam/* 22185380Ssam * XXX this is virtually the same code as for 5212; we reuse 23185380Ssam * storage in the 5212 state block; need to refactor. 24185380Ssam */ 25185380Ssam#include "ah.h" 26185380Ssam#include "ah_internal.h" 27185380Ssam#include "ah_desc.h" 28185380Ssam 29185380Ssam#include "ar5416/ar5416.h" 30185380Ssam#include "ar5416/ar5416reg.h" 31185380Ssam#include "ar5416/ar5416phy.h" 32185380Ssam 33185380Ssam/* 34185380Ssam * Anti noise immunity support. We track phy errors and react 35185380Ssam * to excessive errors by adjusting the noise immunity parameters. 36185380Ssam */ 37185380Ssam 38185380Ssam#define HAL_EP_RND(x, mul) \ 39185380Ssam ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) 40185380Ssam#define BEACON_RSSI(ahp) \ 41185380Ssam HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \ 42185380Ssam HAL_RSSI_EP_MULTIPLIER) 43185380Ssam 44185380Ssam/* 45185380Ssam * ANI processing tunes radio parameters according to PHY errors 46185380Ssam * and related information. This is done for for noise and spur 47185380Ssam * immunity in all operating modes if the device indicates it's 48185380Ssam * capable at attach time. In addition, when there is a reference 49185380Ssam * rssi value (e.g. beacon frames from an ap in station mode) 50185380Ssam * further tuning is done. 51185380Ssam * 52185380Ssam * ANI_ENA indicates whether any ANI processing should be done; 53185380Ssam * this is specified at attach time. 54185380Ssam * 55185380Ssam * ANI_ENA_RSSI indicates whether rssi-based processing should 56185380Ssam * done, this is enabled based on operating mode and is meaningful 57185380Ssam * only if ANI_ENA is true. 58185380Ssam * 59185380Ssam * ANI parameters are typically controlled only by the hal. The 60185380Ssam * AniControl interface however permits manual tuning through the 61185380Ssam * diagnostic api. 62185380Ssam */ 63185380Ssam#define ANI_ENA(ah) \ 64185380Ssam (AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA) 65185380Ssam#define ANI_ENA_RSSI(ah) \ 66185380Ssam (AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA) 67185380Ssam 68185380Ssam#define ah_mibStats ah_stats.ast_mibstats 69185380Ssam 70185380Ssamstatic void 71185380SsamenableAniMIBCounters(struct ath_hal *ah, const struct ar5212AniParams *params) 72185380Ssam{ 73185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 74185380Ssam 75185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Enable mib counters: " 76185380Ssam "OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n", 77185380Ssam __func__, params->ofdmPhyErrBase, params->cckPhyErrBase); 78185380Ssam 79185380Ssam OS_REG_WRITE(ah, AR_FILTOFDM, 0); 80185380Ssam OS_REG_WRITE(ah, AR_FILTCCK, 0); 81185380Ssam 82185380Ssam OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase); 83185380Ssam OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase); 84185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); 85185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); 86185380Ssam 87185380Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save+clear counters*/ 88185380Ssam ar5212EnableMibCounters(ah); /* enable everything */ 89185380Ssam} 90185380Ssam 91185380Ssamstatic void 92185380SsamdisableAniMIBCounters(struct ath_hal *ah) 93185380Ssam{ 94185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 95185380Ssam 96185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "Disable MIB counters\n"); 97185380Ssam 98185380Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save stats */ 99185380Ssam ar5212DisableMibCounters(ah); /* disable everything */ 100185380Ssam 101185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, 0); 102185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, 0); 103185380Ssam} 104185380Ssam 105185380Ssamstatic void 106185380SsamsetPhyErrBase(struct ath_hal *ah, struct ar5212AniParams *params) 107185380Ssam{ 108185380Ssam if (params->ofdmTrigHigh >= AR_PHY_COUNTMAX) { 109185380Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 110185380Ssam "OFDM Trigger %d is too high for hw counters, using max\n", 111185380Ssam params->ofdmTrigHigh); 112185380Ssam params->ofdmPhyErrBase = 0; 113185380Ssam } else 114185380Ssam params->ofdmPhyErrBase = AR_PHY_COUNTMAX - params->ofdmTrigHigh; 115185380Ssam if (params->cckTrigHigh >= AR_PHY_COUNTMAX) { 116185380Ssam HALDEBUG(ah, HAL_DEBUG_ANY, 117185380Ssam "CCK Trigger %d is too high for hw counters, using max\n", 118185380Ssam params->cckTrigHigh); 119185380Ssam params->cckPhyErrBase = 0; 120185380Ssam } else 121185380Ssam params->cckPhyErrBase = AR_PHY_COUNTMAX - params->cckTrigHigh; 122185380Ssam} 123185380Ssam 124185380Ssam/* 125185380Ssam * Setup ANI handling. Sets all thresholds and reset the 126185380Ssam * channel statistics. Note that ar5416AniReset should be 127185380Ssam * called by ar5416Reset before anything else happens and 128185380Ssam * that's where we force initial settings. 129185380Ssam */ 130185380Ssamvoid 131185380Ssamar5416AniAttach(struct ath_hal *ah, const struct ar5212AniParams *params24, 132185380Ssam const struct ar5212AniParams *params5, HAL_BOOL enable) 133185380Ssam{ 134185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 135185380Ssam 136185380Ssam if (params24 != AH_NULL) { 137185380Ssam OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24)); 138185380Ssam setPhyErrBase(ah, &ahp->ah_aniParams24); 139185380Ssam } 140185380Ssam if (params5 != AH_NULL) { 141185380Ssam OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5)); 142185380Ssam setPhyErrBase(ah, &ahp->ah_aniParams5); 143185380Ssam } 144185380Ssam 145185380Ssam OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani)); 146185380Ssam /* Enable MIB Counters */ 147185380Ssam enableAniMIBCounters(ah, &ahp->ah_aniParams24 /*XXX*/); 148185380Ssam 149185380Ssam if (enable) { /* Enable ani now */ 150185380Ssam HALASSERT(params24 != AH_NULL && params5 != AH_NULL); 151185380Ssam ahp->ah_procPhyErr |= HAL_ANI_ENA; 152185380Ssam } else { 153185380Ssam ahp->ah_procPhyErr &= ~HAL_ANI_ENA; 154185380Ssam } 155185380Ssam} 156185380Ssam 157185380Ssam/* 158185380Ssam * Cleanup any ANI state setup. 159224514Sadrian * 160224514Sadrian * This doesn't restore registers to their default settings! 161185380Ssam */ 162185380Ssamvoid 163185380Ssamar5416AniDetach(struct ath_hal *ah) 164185380Ssam{ 165185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "Detaching Ani\n"); 166185380Ssam disableAniMIBCounters(ah); 167185380Ssam} 168185380Ssam 169185380Ssam/* 170185380Ssam * Control Adaptive Noise Immunity Parameters 171185380Ssam */ 172185380SsamHAL_BOOL 173185380Ssamar5416AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param) 174185380Ssam{ 175185380Ssam typedef int TABLE[]; 176185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 177185380Ssam struct ar5212AniState *aniState = ahp->ah_curani; 178224514Sadrian const struct ar5212AniParams *params = AH_NULL; 179185380Ssam 180224514Sadrian /* 181224514Sadrian * This function may be called before there's a current 182224514Sadrian * channel (eg to disable ANI.) 183224514Sadrian */ 184224514Sadrian if (aniState != AH_NULL) 185224514Sadrian params = aniState->params; 186224514Sadrian 187224514Sadrian OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd); 188224514Sadrian 189224514Sadrian /* These commands can't be disabled */ 190224514Sadrian if (cmd == HAL_ANI_PRESENT) 191224514Sadrian return AH_TRUE; 192224514Sadrian 193224514Sadrian if (cmd == HAL_ANI_MODE) { 194224514Sadrian if (param == 0) { 195224514Sadrian ahp->ah_procPhyErr &= ~HAL_ANI_ENA; 196224514Sadrian /* Turn off HW counters if we have them */ 197224514Sadrian ar5416AniDetach(ah); 198224514Sadrian } else { /* normal/auto mode */ 199224514Sadrian /* don't mess with state if already enabled */ 200224514Sadrian if (! (ahp->ah_procPhyErr & HAL_ANI_ENA)) { 201224514Sadrian /* Enable MIB Counters */ 202224514Sadrian /* 203224514Sadrian * XXX use 2.4ghz params if no channel is 204224514Sadrian * available 205224514Sadrian */ 206224514Sadrian enableAniMIBCounters(ah, 207224514Sadrian ahp->ah_curani != AH_NULL ? 208224514Sadrian ahp->ah_curani->params: 209224514Sadrian &ahp->ah_aniParams24); 210224514Sadrian ahp->ah_procPhyErr |= HAL_ANI_ENA; 211224514Sadrian } 212224514Sadrian } 213224514Sadrian return AH_TRUE; 214224514Sadrian } 215224514Sadrian 216222276Sadrian /* Check whether the particular function is enabled */ 217222276Sadrian if (((1 << cmd) & AH5416(ah)->ah_ani_function) == 0) { 218222276Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: command %d disabled\n", 219222276Sadrian __func__, cmd); 220222276Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: cmd %d; mask %x\n", __func__, cmd, AH5416(ah)->ah_ani_function); 221222276Sadrian return AH_FALSE; 222222276Sadrian } 223222276Sadrian 224185380Ssam 225222276Sadrian switch (cmd) { 226185380Ssam case HAL_ANI_NOISE_IMMUNITY_LEVEL: { 227185380Ssam u_int level = param; 228185380Ssam 229217684Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_NOISE_IMMUNITY_LEVEL: set level = %d\n", __func__, level); 230227376Sadrian if (level > params->maxNoiseImmunityLevel) { 231217925Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, 232203159Srpaulo "%s: immunity level out of range (%u > %u)\n", 233185380Ssam __func__, level, params->maxNoiseImmunityLevel); 234185380Ssam return AH_FALSE; 235185380Ssam } 236185380Ssam 237185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, 238185380Ssam AR_PHY_DESIRED_SZ_TOT_DES, params->totalSizeDesired[level]); 239185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, 240185380Ssam AR_PHY_AGC_CTL1_COARSE_LOW, params->coarseLow[level]); 241185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, 242185380Ssam AR_PHY_AGC_CTL1_COARSE_HIGH, params->coarseHigh[level]); 243185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, 244185380Ssam AR_PHY_FIND_SIG_FIRPWR, params->firpwr[level]); 245185380Ssam 246185380Ssam if (level > aniState->noiseImmunityLevel) 247185380Ssam ahp->ah_stats.ast_ani_niup++; 248185380Ssam else if (level < aniState->noiseImmunityLevel) 249185380Ssam ahp->ah_stats.ast_ani_nidown++; 250185380Ssam aniState->noiseImmunityLevel = level; 251185380Ssam break; 252185380Ssam } 253185380Ssam case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: { 254185380Ssam static const TABLE m1ThreshLow = { 127, 50 }; 255185380Ssam static const TABLE m2ThreshLow = { 127, 40 }; 256185380Ssam static const TABLE m1Thresh = { 127, 0x4d }; 257185380Ssam static const TABLE m2Thresh = { 127, 0x40 }; 258185380Ssam static const TABLE m2CountThr = { 31, 16 }; 259185380Ssam static const TABLE m2CountThrLow = { 63, 48 }; 260185380Ssam u_int on = param ? 1 : 0; 261185380Ssam 262217684Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: %s\n", __func__, on ? "enabled" : "disabled"); 263185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 264185380Ssam AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow[on]); 265185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 266185380Ssam AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow[on]); 267185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 268185380Ssam AR_PHY_SFCORR_M1_THRESH, m1Thresh[on]); 269185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 270185380Ssam AR_PHY_SFCORR_M2_THRESH, m2Thresh[on]); 271185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, 272185380Ssam AR_PHY_SFCORR_M2COUNT_THR, m2CountThr[on]); 273185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, 274185380Ssam AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow[on]); 275185380Ssam 276185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 277185380Ssam AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLow[on]); 278185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 279185380Ssam AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLow[on]); 280185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 281185380Ssam AR_PHY_SFCORR_EXT_M1_THRESH, m1Thresh[on]); 282185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, 283185380Ssam AR_PHY_SFCORR_EXT_M2_THRESH, m2Thresh[on]); 284185380Ssam 285185380Ssam if (on) { 286185380Ssam OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, 287185380Ssam AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); 288185380Ssam } else { 289185380Ssam OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, 290185380Ssam AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); 291185380Ssam } 292185380Ssam if (on) 293185380Ssam ahp->ah_stats.ast_ani_ofdmon++; 294185380Ssam else 295185380Ssam ahp->ah_stats.ast_ani_ofdmoff++; 296185380Ssam aniState->ofdmWeakSigDetectOff = !on; 297185380Ssam break; 298185380Ssam } 299185380Ssam case HAL_ANI_CCK_WEAK_SIGNAL_THR: { 300185380Ssam static const TABLE weakSigThrCck = { 8, 6 }; 301185380Ssam u_int high = param ? 1 : 0; 302185380Ssam 303217684Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_CCK_WEAK_SIGNAL_THR: %s\n", __func__, high ? "high" : "low"); 304185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT, 305185380Ssam AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, weakSigThrCck[high]); 306185380Ssam if (high) 307185380Ssam ahp->ah_stats.ast_ani_cckhigh++; 308185380Ssam else 309185380Ssam ahp->ah_stats.ast_ani_ccklow++; 310185380Ssam aniState->cckWeakSigThreshold = high; 311185380Ssam break; 312185380Ssam } 313185380Ssam case HAL_ANI_FIRSTEP_LEVEL: { 314185380Ssam u_int level = param; 315185380Ssam 316217684Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_FIRSTEP_LEVEL: level = %d\n", __func__, level); 317227376Sadrian if (level > params->maxFirstepLevel) { 318217925Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, 319203159Srpaulo "%s: firstep level out of range (%u > %u)\n", 320185380Ssam __func__, level, params->maxFirstepLevel); 321185380Ssam return AH_FALSE; 322185380Ssam } 323185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, 324185380Ssam AR_PHY_FIND_SIG_FIRSTEP, params->firstep[level]); 325185380Ssam if (level > aniState->firstepLevel) 326185380Ssam ahp->ah_stats.ast_ani_stepup++; 327185380Ssam else if (level < aniState->firstepLevel) 328185380Ssam ahp->ah_stats.ast_ani_stepdown++; 329185380Ssam aniState->firstepLevel = level; 330185380Ssam break; 331185380Ssam } 332185380Ssam case HAL_ANI_SPUR_IMMUNITY_LEVEL: { 333185380Ssam u_int level = param; 334185380Ssam 335217684Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: HAL_ANI_SPUR_IMMUNITY_LEVEL: level = %d\n", __func__, level); 336227376Sadrian if (level > params->maxSpurImmunityLevel) { 337217925Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, 338203159Srpaulo "%s: spur immunity level out of range (%u > %u)\n", 339185380Ssam __func__, level, params->maxSpurImmunityLevel); 340185380Ssam return AH_FALSE; 341185380Ssam } 342185380Ssam OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5, 343185380Ssam AR_PHY_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]); 344221600Sadrian 345185380Ssam if (level > aniState->spurImmunityLevel) 346185380Ssam ahp->ah_stats.ast_ani_spurup++; 347185380Ssam else if (level < aniState->spurImmunityLevel) 348185380Ssam ahp->ah_stats.ast_ani_spurdown++; 349185380Ssam aniState->spurImmunityLevel = level; 350185380Ssam break; 351185380Ssam } 352185380Ssam#ifdef AH_PRIVATE_DIAG 353185380Ssam case HAL_ANI_PHYERR_RESET: 354185380Ssam ahp->ah_stats.ast_ani_ofdmerrs = 0; 355185380Ssam ahp->ah_stats.ast_ani_cckerrs = 0; 356185380Ssam break; 357185380Ssam#endif /* AH_PRIVATE_DIAG */ 358185380Ssam default: 359217925Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: invalid cmd %u\n", 360185380Ssam __func__, cmd); 361185380Ssam return AH_FALSE; 362185380Ssam } 363185380Ssam return AH_TRUE; 364185380Ssam} 365185380Ssam 366185380Ssamstatic void 367185380Ssamar5416AniOfdmErrTrigger(struct ath_hal *ah) 368185380Ssam{ 369185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 370187831Ssam const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; 371185380Ssam struct ar5212AniState *aniState; 372185380Ssam const struct ar5212AniParams *params; 373185380Ssam 374185380Ssam HALASSERT(chan != AH_NULL); 375185380Ssam 376185380Ssam if (!ANI_ENA(ah)) 377185380Ssam return; 378185380Ssam 379185380Ssam aniState = ahp->ah_curani; 380185380Ssam params = aniState->params; 381185380Ssam /* First, raise noise immunity level, up to max */ 382227377Sadrian if (aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel) { 383227377Sadrian if (ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 384227377Sadrian aniState->noiseImmunityLevel + 1)) 385227377Sadrian return; 386185380Ssam } 387185380Ssam /* then, raise spur immunity level, up to max */ 388227377Sadrian if (aniState->spurImmunityLevel+1 < params->maxSpurImmunityLevel) { 389227377Sadrian if (ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 390227377Sadrian aniState->spurImmunityLevel + 1)) 391227377Sadrian return; 392185380Ssam } 393185380Ssam 394227378Sadrian /* 395227378Sadrian * In the case of AP mode operation, we cannot bucketize beacons 396227378Sadrian * according to RSSI. Instead, raise Firstep level, up to max, and 397227378Sadrian * simply return. 398227378Sadrian */ 399227378Sadrian if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) { 400227378Sadrian if (aniState->firstepLevel < params->maxFirstepLevel) { 401227378Sadrian if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 402227378Sadrian aniState->firstepLevel + 1)) 403227378Sadrian return; 404227378Sadrian } 405227378Sadrian } 406185380Ssam if (ANI_ENA_RSSI(ah)) { 407185380Ssam int32_t rssi = BEACON_RSSI(ahp); 408185380Ssam if (rssi > params->rssiThrHigh) { 409185380Ssam /* 410185380Ssam * Beacon rssi is high, can turn off ofdm 411185380Ssam * weak sig detect. 412185380Ssam */ 413185380Ssam if (!aniState->ofdmWeakSigDetectOff) { 414185380Ssam ar5416AniControl(ah, 415185380Ssam HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 416185380Ssam AH_FALSE); 417185380Ssam ar5416AniControl(ah, 418185380Ssam HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); 419185380Ssam return; 420185380Ssam } 421185380Ssam /* 422185380Ssam * If weak sig detect is already off, as last resort, 423185380Ssam * raise firstep level 424185380Ssam */ 425239753Sadrian if (aniState->firstepLevel < params->maxFirstepLevel) { 426227376Sadrian if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 427227376Sadrian aniState->firstepLevel + 1)) 428227376Sadrian return; 429185380Ssam } 430185380Ssam } else if (rssi > params->rssiThrLow) { 431185380Ssam /* 432185380Ssam * Beacon rssi in mid range, need ofdm weak signal 433185380Ssam * detect, but we can raise firststepLevel. 434185380Ssam */ 435185380Ssam if (aniState->ofdmWeakSigDetectOff) 436185380Ssam ar5416AniControl(ah, 437185380Ssam HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 438185380Ssam AH_TRUE); 439239753Sadrian if (aniState->firstepLevel < params->maxFirstepLevel) 440227376Sadrian if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 441227376Sadrian aniState->firstepLevel + 1)) 442227376Sadrian return; 443185380Ssam } else { 444185380Ssam /* 445185380Ssam * Beacon rssi is low, if in 11b/g mode, turn off ofdm 446185380Ssam * weak signal detection and zero firstepLevel to 447185380Ssam * maximize CCK sensitivity 448185380Ssam */ 449187831Ssam if (IEEE80211_IS_CHAN_CCK(chan)) { 450185380Ssam if (!aniState->ofdmWeakSigDetectOff) 451185380Ssam ar5416AniControl(ah, 452185380Ssam HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 453185380Ssam AH_FALSE); 454185380Ssam if (aniState->firstepLevel > 0) 455227376Sadrian if (ar5416AniControl(ah, 456227376Sadrian HAL_ANI_FIRSTEP_LEVEL, 0)) 457227376Sadrian return; 458185380Ssam } 459185380Ssam } 460185380Ssam } 461185380Ssam} 462185380Ssam 463185380Ssamstatic void 464185380Ssamar5416AniCckErrTrigger(struct ath_hal *ah) 465185380Ssam{ 466185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 467187831Ssam const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; 468185380Ssam struct ar5212AniState *aniState; 469185380Ssam const struct ar5212AniParams *params; 470185380Ssam 471185380Ssam HALASSERT(chan != AH_NULL); 472185380Ssam 473185380Ssam if (!ANI_ENA(ah)) 474185380Ssam return; 475185380Ssam 476185380Ssam /* first, raise noise immunity level, up to max */ 477185380Ssam aniState = ahp->ah_curani; 478185380Ssam params = aniState->params; 479222276Sadrian if ((AH5416(ah)->ah_ani_function & (1 << HAL_ANI_NOISE_IMMUNITY_LEVEL) && 480222276Sadrian aniState->noiseImmunityLevel+1 < params->maxNoiseImmunityLevel)) { 481185380Ssam ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 482185380Ssam aniState->noiseImmunityLevel + 1); 483185380Ssam return; 484185380Ssam } 485185380Ssam 486185380Ssam if (ANI_ENA_RSSI(ah)) { 487185380Ssam int32_t rssi = BEACON_RSSI(ahp); 488185380Ssam if (rssi > params->rssiThrLow) { 489185380Ssam /* 490185380Ssam * Beacon signal in mid and high range, 491185380Ssam * raise firstep level. 492185380Ssam */ 493239753Sadrian if (aniState->firstepLevel < params->maxFirstepLevel) 494185380Ssam ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 495185380Ssam aniState->firstepLevel + 1); 496185380Ssam } else { 497185380Ssam /* 498185380Ssam * Beacon rssi is low, zero firstep level to maximize 499185380Ssam * CCK sensitivity in 11b/g mode. 500185380Ssam */ 501187831Ssam if (IEEE80211_IS_CHAN_CCK(chan)) { 502185380Ssam if (aniState->firstepLevel > 0) 503185380Ssam ar5416AniControl(ah, 504185380Ssam HAL_ANI_FIRSTEP_LEVEL, 0); 505185380Ssam } 506185380Ssam } 507185380Ssam } 508185380Ssam} 509185380Ssam 510185380Ssamstatic void 511185380Ssamar5416AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState) 512185380Ssam{ 513185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 514185380Ssam const struct ar5212AniParams *params = aniState->params; 515185380Ssam 516185380Ssam aniState->listenTime = 0; 517185380Ssam /* 518185380Ssam * NB: these are written on reset based on the 519185380Ssam * ini so we must re-write them! 520185380Ssam */ 521185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, 522185380Ssam "%s: Writing ofdmbase=%u cckbase=%u\n", __func__, 523185380Ssam params->ofdmPhyErrBase, params->cckPhyErrBase); 524185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_1, params->ofdmPhyErrBase); 525185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_2, params->cckPhyErrBase); 526185380Ssam OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); 527219862Sadrian OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); 528185380Ssam 529185380Ssam /* Clear the mib counters and save them in the stats */ 530185380Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 531185380Ssam aniState->ofdmPhyErrCount = 0; 532185380Ssam aniState->cckPhyErrCount = 0; 533185380Ssam} 534185380Ssam 535185380Ssam/* 536185380Ssam * Restore/reset the ANI parameters and reset the statistics. 537185380Ssam * This routine must be called for every channel change. 538185380Ssam * 539185380Ssam * NOTE: This is where ah_curani is set; other ani code assumes 540185380Ssam * it is setup to reflect the current channel. 541185380Ssam */ 542185380Ssamvoid 543187831Ssamar5416AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan, 544185380Ssam HAL_OPMODE opmode, int restore) 545185380Ssam{ 546185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 547187831Ssam HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); 548187831Ssam /* XXX bounds check ic_devdata */ 549187831Ssam struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata]; 550185380Ssam uint32_t rxfilter; 551185380Ssam 552187831Ssam if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) { 553187831Ssam OS_MEMZERO(aniState, sizeof(*aniState)); 554187831Ssam if (IEEE80211_IS_CHAN_2GHZ(chan)) 555187831Ssam aniState->params = &ahp->ah_aniParams24; 556187831Ssam else 557187831Ssam aniState->params = &ahp->ah_aniParams5; 558187831Ssam ichan->privFlags |= CHANNEL_ANI_INIT; 559187831Ssam HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0); 560187831Ssam } 561185380Ssam ahp->ah_curani = aniState; 562185380Ssam#if 0 563187831Ssam ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n", 564187831Ssam __func__, chan->ic_freq, chan->ic_flags, restore, opmode, 565187831Ssam ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); 566185380Ssam#else 567187831Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n", 568187831Ssam __func__, chan->ic_freq, chan->ic_flags, restore, opmode, 569187831Ssam ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : ""); 570185380Ssam#endif 571185380Ssam OS_MARK(ah, AH_MARK_ANI_RESET, opmode); 572185380Ssam 573185380Ssam /* 574185380Ssam * Turn off PHY error frame delivery while we futz with settings. 575185380Ssam */ 576224514Sadrian rxfilter = ah->ah_getRxFilter(ah); 577224514Sadrian ah->ah_setRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR); 578224514Sadrian 579185380Ssam /* 580224514Sadrian * If ANI is disabled at this point, don't set the default 581224514Sadrian * ANI parameter settings - leave the HAL settings there. 582224514Sadrian * This is (currently) needed for reliable radar detection. 583224514Sadrian */ 584224514Sadrian if (! ANI_ENA(ah)) { 585224514Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: ANI disabled\n", 586224514Sadrian __func__); 587224514Sadrian goto finish; 588224514Sadrian } 589224514Sadrian 590227379Sadrian /* 591227379Sadrian * Use a restrictive set of ANI parameters for hostap mode. 592227379Sadrian */ 593227379Sadrian if (opmode == HAL_M_HOSTAP) { 594227379Sadrian if (IEEE80211_IS_CHAN_2GHZ(chan)) 595227379Sadrian AH5416(ah)->ah_ani_function = 596227379Sadrian HAL_ANI_SPUR_IMMUNITY_LEVEL | HAL_ANI_FIRSTEP_LEVEL; 597227379Sadrian else 598227379Sadrian AH5416(ah)->ah_ani_function = 0; 599227379Sadrian } 600224514Sadrian 601224514Sadrian /* 602185380Ssam * Automatic processing is done only in station mode right now. 603185380Ssam */ 604185380Ssam if (opmode == HAL_M_STA) 605185380Ssam ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA; 606185380Ssam else 607185380Ssam ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA; 608185380Ssam /* 609185380Ssam * Set all ani parameters. We either set them to initial 610185380Ssam * values or restore the previous ones for the channel. 611185380Ssam * XXX if ANI follows hardware, we don't care what mode we're 612185380Ssam * XXX in, we should keep the ani parameters 613185380Ssam */ 614187831Ssam if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) { 615185380Ssam ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 616185380Ssam aniState->noiseImmunityLevel); 617185380Ssam ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 618185380Ssam aniState->spurImmunityLevel); 619185380Ssam ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 620185380Ssam !aniState->ofdmWeakSigDetectOff); 621185380Ssam ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, 622185380Ssam aniState->cckWeakSigThreshold); 623185380Ssam ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 624185380Ssam aniState->firstepLevel); 625185380Ssam } else { 626185380Ssam ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0); 627185380Ssam ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0); 628185380Ssam ar5416AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 629227380Sadrian AH_FALSE); 630185380Ssam ar5416AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE); 631185380Ssam ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0); 632187831Ssam ichan->privFlags |= CHANNEL_ANI_SETUP; 633185380Ssam } 634224514Sadrian 635224514Sadrian /* 636224514Sadrian * In case the counters haven't yet been setup; set them up. 637224514Sadrian */ 638224514Sadrian enableAniMIBCounters(ah, aniState->params); 639185380Ssam ar5416AniRestart(ah, aniState); 640185380Ssam 641224514Sadrianfinish: 642185380Ssam /* restore RX filter mask */ 643224514Sadrian ah->ah_setRxFilter(ah, rxfilter); 644185380Ssam} 645185380Ssam 646185380Ssam/* 647185380Ssam * Process a MIB interrupt. We may potentially be invoked because 648185380Ssam * any of the MIB counters overflow/trigger so don't assume we're 649185380Ssam * here because a PHY error counter triggered. 650185380Ssam */ 651185380Ssamvoid 652185380Ssamar5416ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats) 653185380Ssam{ 654185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 655185380Ssam uint32_t phyCnt1, phyCnt2; 656185380Ssam 657185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x " 658185380Ssam "filtofdm 0x%x filtcck 0x%x\n", 659185380Ssam __func__, OS_REG_READ(ah, AR_MIBC), 660185380Ssam OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2), 661185380Ssam OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK)); 662185380Ssam 663185380Ssam /* 664185380Ssam * First order of business is to clear whatever caused 665185380Ssam * the interrupt so we don't keep getting interrupted. 666185380Ssam * We have the usual mib counters that are reset-on-read 667185380Ssam * and the additional counters that appeared starting in 668185380Ssam * Hainan. We collect the mib counters and explicitly 669185380Ssam * zero additional counters we are not using. Anything 670185380Ssam * else is reset only if it caused the interrupt. 671185380Ssam */ 672185380Ssam /* NB: these are not reset-on-read */ 673185380Ssam phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); 674185380Ssam phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); 675185380Ssam /* not used, always reset them in case they are the cause */ 676185380Ssam OS_REG_WRITE(ah, AR_FILTOFDM, 0); 677185380Ssam OS_REG_WRITE(ah, AR_FILTCCK, 0); 678185380Ssam if ((OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING) == 0) 679185380Ssam OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR); 680185380Ssam 681185380Ssam /* Clear the mib counters and save them in the stats */ 682185380Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 683185380Ssam ahp->ah_stats.ast_nodestats = *stats; 684185380Ssam 685185380Ssam /* 686185380Ssam * Check for an ani stat hitting the trigger threshold. 687185380Ssam * When this happens we get a MIB interrupt and the top 688185380Ssam * 2 bits of the counter register will be 0b11, hence 689185380Ssam * the mask check of phyCnt?. 690185380Ssam */ 691185380Ssam if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || 692185380Ssam ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { 693185380Ssam struct ar5212AniState *aniState = ahp->ah_curani; 694185380Ssam const struct ar5212AniParams *params = aniState->params; 695185380Ssam uint32_t ofdmPhyErrCnt, cckPhyErrCnt; 696185380Ssam 697185380Ssam ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; 698185380Ssam ahp->ah_stats.ast_ani_ofdmerrs += 699185380Ssam ofdmPhyErrCnt - aniState->ofdmPhyErrCount; 700185380Ssam aniState->ofdmPhyErrCount = ofdmPhyErrCnt; 701185380Ssam 702185380Ssam cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; 703185380Ssam ahp->ah_stats.ast_ani_cckerrs += 704185380Ssam cckPhyErrCnt - aniState->cckPhyErrCount; 705185380Ssam aniState->cckPhyErrCount = cckPhyErrCnt; 706185380Ssam 707185380Ssam /* 708185380Ssam * NB: figure out which counter triggered. If both 709185380Ssam * trigger we'll only deal with one as the processing 710185380Ssam * clobbers the error counter so the trigger threshold 711185380Ssam * check will never be true. 712185380Ssam */ 713185380Ssam if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) 714185380Ssam ar5416AniOfdmErrTrigger(ah); 715185380Ssam if (aniState->cckPhyErrCount > params->cckTrigHigh) 716185380Ssam ar5416AniCckErrTrigger(ah); 717185380Ssam /* NB: always restart to insure the h/w counters are reset */ 718185380Ssam ar5416AniRestart(ah, aniState); 719185380Ssam } 720185380Ssam} 721185380Ssam 722185380Ssamstatic void 723185380Ssamar5416AniLowerImmunity(struct ath_hal *ah) 724185380Ssam{ 725185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 726185380Ssam struct ar5212AniState *aniState; 727185380Ssam const struct ar5212AniParams *params; 728185380Ssam 729185380Ssam HALASSERT(ANI_ENA(ah)); 730185380Ssam 731185380Ssam aniState = ahp->ah_curani; 732185380Ssam params = aniState->params; 733227378Sadrian 734227378Sadrian /* 735227378Sadrian * In the case of AP mode operation, we cannot bucketize beacons 736227378Sadrian * according to RSSI. Instead, lower Firstep level, down to min, and 737227378Sadrian * simply return. 738227378Sadrian */ 739227378Sadrian if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) { 740227378Sadrian if (aniState->firstepLevel > 0) { 741227378Sadrian if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 742227378Sadrian aniState->firstepLevel - 1)) 743227378Sadrian return; 744227378Sadrian } 745227378Sadrian } 746185380Ssam if (ANI_ENA_RSSI(ah)) { 747185380Ssam int32_t rssi = BEACON_RSSI(ahp); 748185380Ssam if (rssi > params->rssiThrHigh) { 749185380Ssam /* 750185380Ssam * Beacon signal is high, leave ofdm weak signal 751185380Ssam * detection off or it may oscillate. Let it fall 752185380Ssam * through. 753185380Ssam */ 754185380Ssam } else if (rssi > params->rssiThrLow) { 755185380Ssam /* 756185380Ssam * Beacon rssi in mid range, turn on ofdm weak signal 757185380Ssam * detection or lower firstep level. 758185380Ssam */ 759185380Ssam if (aniState->ofdmWeakSigDetectOff) { 760227376Sadrian if (ar5416AniControl(ah, 761185380Ssam HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, 762227376Sadrian AH_TRUE)) 763227376Sadrian return; 764185380Ssam } 765185380Ssam if (aniState->firstepLevel > 0) { 766227376Sadrian if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 767227376Sadrian aniState->firstepLevel - 1)) 768227376Sadrian return; 769185380Ssam } 770185380Ssam } else { 771185380Ssam /* 772185380Ssam * Beacon rssi is low, reduce firstep level. 773185380Ssam */ 774185380Ssam if (aniState->firstepLevel > 0) { 775227376Sadrian if (ar5416AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 776227376Sadrian aniState->firstepLevel - 1)) 777227376Sadrian return; 778185380Ssam } 779185380Ssam } 780185380Ssam } 781185380Ssam /* then lower spur immunity level, down to zero */ 782185380Ssam if (aniState->spurImmunityLevel > 0) { 783227376Sadrian if (ar5416AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 784227376Sadrian aniState->spurImmunityLevel - 1)) 785227376Sadrian return; 786185380Ssam } 787185380Ssam /* 788185380Ssam * if all else fails, lower noise immunity level down to a min value 789185380Ssam * zero for now 790185380Ssam */ 791185380Ssam if (aniState->noiseImmunityLevel > 0) { 792227376Sadrian if (ar5416AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 793227376Sadrian aniState->noiseImmunityLevel - 1)) 794227376Sadrian return; 795185380Ssam } 796185380Ssam} 797185380Ssam 798185380Ssam#define CLOCK_RATE 44000 /* XXX use mac_usec or similar */ 799185380Ssam/* convert HW counter values to ms using 11g clock rate, goo9d enough 800185380Ssam for 11a and Turbo */ 801185380Ssam 802185380Ssam/* 803185380Ssam * Return an approximation of the time spent ``listening'' by 804185380Ssam * deducting the cycles spent tx'ing and rx'ing from the total 805185380Ssam * cycle count since our last call. A return value <0 indicates 806185380Ssam * an invalid/inconsistent time. 807234752Sadrian * 808234752Sadrian * This may be called with ANI disabled; in which case simply keep 809234752Sadrian * the statistics and don't write to the aniState pointer. 810234752Sadrian * 811234752Sadrian * XXX TODO: Make this cleaner! 812185380Ssam */ 813185380Ssamstatic int32_t 814185380Ssamar5416AniGetListenTime(struct ath_hal *ah) 815185380Ssam{ 816185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 817234752Sadrian struct ar5212AniState *aniState = NULL; 818234768Sadrian int32_t listenTime = 0; 819227381Sadrian int good; 820234752Sadrian HAL_SURVEY_SAMPLE hs; 821185380Ssam 822234752Sadrian /* 823234752Sadrian * We shouldn't see ah_curchan be NULL, but just in case.. 824234752Sadrian */ 825234752Sadrian if (AH_PRIVATE(ah)->ah_curchan == AH_NULL) { 826234752Sadrian ath_hal_printf(ah, "%s: ah_curchan = NULL?\n", __func__); 827234752Sadrian return (0); 828234752Sadrian } 829185380Ssam 830234752Sadrian /* 831234752Sadrian * Fetch the current statistics, squirrel away the current 832280828Sadrian * sample. 833234752Sadrian */ 834234752Sadrian OS_MEMZERO(&hs, sizeof(hs)); 835234752Sadrian good = ar5416GetMibCycleCounts(ah, &hs); 836280828Sadrian ath_hal_survey_add_sample(ah, &hs); 837234752Sadrian 838234752Sadrian if (ANI_ENA(ah)) 839234752Sadrian aniState = ahp->ah_curani; 840234752Sadrian 841234873Sadrian if (good == AH_FALSE) { 842185380Ssam /* 843185380Ssam * Cycle counter wrap (or initial call); it's not possible 844185380Ssam * to accurately calculate a value because the registers 845185380Ssam * right shift rather than wrap--so punt and return 0. 846185380Ssam */ 847185380Ssam listenTime = 0; 848185380Ssam ahp->ah_stats.ast_ani_lzero++; 849234752Sadrian } else if (ANI_ENA(ah)) { 850234752Sadrian /* 851234752Sadrian * Only calculate and update the cycle count if we have 852234752Sadrian * an ANI state. 853234752Sadrian */ 854234752Sadrian int32_t ccdelta = 855234752Sadrian AH5416(ah)->ah_cycleCount - aniState->cycleCount; 856234752Sadrian int32_t rfdelta = 857234752Sadrian AH5416(ah)->ah_rxBusy - aniState->rxFrameCount; 858234752Sadrian int32_t tfdelta = 859234752Sadrian AH5416(ah)->ah_txBusy - aniState->txFrameCount; 860185380Ssam listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE; 861185380Ssam } 862227381Sadrian 863234752Sadrian /* 864234752Sadrian * Again, only update ANI state if we have it. 865234752Sadrian */ 866234752Sadrian if (ANI_ENA(ah)) { 867234752Sadrian aniState->cycleCount = AH5416(ah)->ah_cycleCount; 868240984Sadrian aniState->rxFrameCount = AH5416(ah)->ah_rxBusy; 869240984Sadrian aniState->txFrameCount = AH5416(ah)->ah_txBusy; 870234752Sadrian } 871227381Sadrian 872185380Ssam return listenTime; 873185380Ssam} 874185380Ssam 875185380Ssam/* 876185380Ssam * Update ani stats in preparation for listen time processing. 877185380Ssam */ 878185380Ssamstatic void 879185380SsamupdateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState) 880185380Ssam{ 881185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 882185380Ssam const struct ar5212AniParams *params = aniState->params; 883185380Ssam uint32_t phyCnt1, phyCnt2; 884185380Ssam int32_t ofdmPhyErrCnt, cckPhyErrCnt; 885185380Ssam 886185380Ssam /* Clear the mib counters and save them in the stats */ 887185380Ssam ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); 888185380Ssam 889185380Ssam /* NB: these are not reset-on-read */ 890185380Ssam phyCnt1 = OS_REG_READ(ah, AR_PHY_ERR_1); 891185380Ssam phyCnt2 = OS_REG_READ(ah, AR_PHY_ERR_2); 892185380Ssam 893185380Ssam /* NB: these are spec'd to never roll-over */ 894185380Ssam ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase; 895185380Ssam if (ofdmPhyErrCnt < 0) { 896185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n", 897185380Ssam ofdmPhyErrCnt, phyCnt1); 898185380Ssam ofdmPhyErrCnt = AR_PHY_COUNTMAX; 899185380Ssam } 900185380Ssam ahp->ah_stats.ast_ani_ofdmerrs += 901185380Ssam ofdmPhyErrCnt - aniState->ofdmPhyErrCount; 902185380Ssam aniState->ofdmPhyErrCount = ofdmPhyErrCnt; 903185380Ssam 904185380Ssam cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase; 905185380Ssam if (cckPhyErrCnt < 0) { 906185380Ssam HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n", 907185380Ssam cckPhyErrCnt, phyCnt2); 908185380Ssam cckPhyErrCnt = AR_PHY_COUNTMAX; 909185380Ssam } 910185380Ssam ahp->ah_stats.ast_ani_cckerrs += 911185380Ssam cckPhyErrCnt - aniState->cckPhyErrCount; 912185380Ssam aniState->cckPhyErrCount = cckPhyErrCnt; 913185380Ssam} 914185380Ssam 915217684Sadrianvoid 916217684Sadrianar5416RxMonitor(struct ath_hal *ah, const HAL_NODE_STATS *stats, 917217684Sadrian const struct ieee80211_channel *chan) 918217684Sadrian{ 919217684Sadrian struct ath_hal_5212 *ahp = AH5212(ah); 920217684Sadrian ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi; 921217684Sadrian} 922217684Sadrian 923185380Ssam/* 924185380Ssam * Do periodic processing. This routine is called from the 925185380Ssam * driver's rx interrupt handler after processing frames. 926185380Ssam */ 927185380Ssamvoid 928217684Sadrianar5416AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan) 929185380Ssam{ 930185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 931185380Ssam struct ar5212AniState *aniState = ahp->ah_curani; 932185380Ssam const struct ar5212AniParams *params; 933185380Ssam int32_t listenTime; 934185380Ssam 935234752Sadrian /* Always update from the MIB, for statistics gathering */ 936234752Sadrian listenTime = ar5416AniGetListenTime(ah); 937234752Sadrian 938185380Ssam /* XXX can aniState be null? */ 939185380Ssam if (aniState == AH_NULL) 940185380Ssam return; 941227381Sadrian 942185380Ssam if (!ANI_ENA(ah)) 943185380Ssam return; 944185380Ssam 945185380Ssam if (listenTime < 0) { 946185380Ssam ahp->ah_stats.ast_ani_lneg++; 947185380Ssam /* restart ANI period if listenTime is invalid */ 948239753Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: invalid listenTime\n", 949239753Sadrian __func__); 950185380Ssam ar5416AniRestart(ah, aniState); 951185380Ssam } 952185380Ssam /* XXX beware of overflow? */ 953185380Ssam aniState->listenTime += listenTime; 954185380Ssam 955185380Ssam OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime); 956185380Ssam 957185380Ssam params = aniState->params; 958185380Ssam if (aniState->listenTime > 5*params->period) { 959185380Ssam /* 960185380Ssam * Check to see if need to lower immunity if 961185380Ssam * 5 aniPeriods have passed 962185380Ssam */ 963185380Ssam updateMIBStats(ah, aniState); 964185380Ssam if (aniState->ofdmPhyErrCount <= aniState->listenTime * 965185380Ssam params->ofdmTrigLow/1000 && 966185380Ssam aniState->cckPhyErrCount <= aniState->listenTime * 967185380Ssam params->cckTrigLow/1000) 968185380Ssam ar5416AniLowerImmunity(ah); 969239753Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower immunity\n", 970239753Sadrian __func__); 971185380Ssam ar5416AniRestart(ah, aniState); 972185380Ssam } else if (aniState->listenTime > params->period) { 973185380Ssam updateMIBStats(ah, aniState); 974185380Ssam /* check to see if need to raise immunity */ 975185380Ssam if (aniState->ofdmPhyErrCount > aniState->listenTime * 976185380Ssam params->ofdmTrigHigh / 1000) { 977219767Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, 978219767Sadrian "%s: OFDM err %u listenTime %u\n", __func__, 979219767Sadrian aniState->ofdmPhyErrCount, aniState->listenTime); 980185380Ssam ar5416AniOfdmErrTrigger(ah); 981185380Ssam ar5416AniRestart(ah, aniState); 982185380Ssam } else if (aniState->cckPhyErrCount > aniState->listenTime * 983185380Ssam params->cckTrigHigh / 1000) { 984219767Sadrian HALDEBUG(ah, HAL_DEBUG_ANI, 985219767Sadrian "%s: CCK err %u listenTime %u\n", __func__, 986239752Sadrian aniState->cckPhyErrCount, aniState->listenTime); 987185380Ssam ar5416AniCckErrTrigger(ah); 988185380Ssam ar5416AniRestart(ah, aniState); 989185380Ssam } 990185380Ssam } 991185380Ssam} 992